smartdust-client 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d5ad0e2e8968ddc2d92112ef56c8ad15403b99507f9699b45e18fd75c42adf76
4
+ data.tar.gz: a44754161a3a0a3eb9ce069717c6c00e5ce2ffce12d00380c43764863172101d
5
+ SHA512:
6
+ metadata.gz: 9ff4070a8ba7c49778a713b777e149487747a9aa28462f5cbc8bbbc7f0e8d918a344312f6652c49a4a6a2469fd4ab56d0ef09f4f6cb1b3feb1cb49598df3a7df
7
+ data.tar.gz: 84bd40b60b759ea84d4b498c96b0a91c139be9bc398bb0de72c3cc71d89347b5713b0c502d034bc3e75edd25447bd38c36832263059b632754bf5dd8145ad03f
data/.gitignore ADDED
@@ -0,0 +1,103 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ ### JetBrains template
10
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
11
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
12
+
13
+ # User-specific stuff:
14
+ .idea/workspace.xml
15
+ .idea/tasks.xml
16
+ .idea/dictionaries
17
+ .idea/vcs.xml
18
+ .idea/jsLibraryMappings.xml
19
+
20
+ # Sensitive or high-churn files:
21
+ .idea/dataSources.ids
22
+ .idea/dataSources.xml
23
+ .idea/dataSources.local.xml
24
+ .idea/sqlDataSources.xml
25
+ .idea/dynamic.xml
26
+ .idea/uiDesigner.xml
27
+
28
+ # Gradle:
29
+ .idea/gradle.xml
30
+ .idea/libraries
31
+
32
+ # Mongo Explorer plugin:
33
+ .idea/mongoSettings.xml
34
+
35
+ # RubyMine
36
+ .idea/.rakeTasks
37
+ .idea/misc.xml
38
+ .idea/modules.xml
39
+ .idea/stf-client.iml
40
+
41
+ ## File-based project format:
42
+ *.iws
43
+
44
+ ## Plugin-specific files:
45
+
46
+ # IntelliJ
47
+ /out/
48
+
49
+ # mpeltonen/sbt-idea plugin
50
+ .idea_modules/
51
+
52
+ # JIRA plugin
53
+ atlassian-ide-plugin.xml
54
+
55
+ # Crashlytics plugin (for Android Studio and IntelliJ)
56
+ com_crashlytics_export_strings.xml
57
+ crashlytics.properties
58
+ crashlytics-build.properties
59
+ fabric.properties
60
+ ### Ruby template
61
+ *.gem
62
+ *.rbc
63
+ /.config
64
+ /InstalledFiles
65
+ /spec/examples.txt
66
+ /test/tmp/
67
+ /test/version_tmp/
68
+
69
+ # Used by dotenv library to load environment variables.
70
+ # .env
71
+
72
+ ## Specific to RubyMotion:
73
+ .dat*
74
+ .repl_history
75
+ build/
76
+ *.bridgesupport
77
+ build-iPhoneOS/
78
+ build-iPhoneSimulator/
79
+
80
+ ## Specific to RubyMotion (use of CocoaPods):
81
+ #
82
+ # We recommend against adding the Pods directory to your .gitignore. However
83
+ # you should judge for yourself, the pros and cons are mentioned at:
84
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
85
+ #
86
+ # vendor/Pods/
87
+
88
+ ## Documentation cache and generated files:
89
+ /.yardoc/
90
+ /rdoc/
91
+
92
+ ## Environment normalization:
93
+ /vendor/bundle
94
+ /lib/bundler/man/
95
+
96
+ # for a library or gem, you might want to ignore these files since the code is
97
+ # intended to run in multiple environments; otherwise, check them in:
98
+ # Gemfile.lock
99
+ # .ruby-version
100
+ # .ruby-gemset
101
+
102
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
103
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
4
+ --format RspecJunitFormatter --out spec/reports/rspec.xml
5
+ --format html --out spec/reports/rspec.html
data/.simplecov ADDED
@@ -0,0 +1,10 @@
1
+ require 'simplecov-json'
2
+
3
+ SimpleCov.formatters = [
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ SimpleCov::Formatter::JSONFormatter
6
+ ]
7
+ SimpleCov.minimum_coverage 80
8
+ SimpleCov.start do
9
+ add_filter '/spec/'
10
+ end
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ smartdust-client (1.0.0)
5
+ ADB (~> 0.5)
6
+ dante (~> 0.2.0)
7
+ dry-configurable (~> 0.1.4)
8
+ dry-container (~> 0.6.0)
9
+ facets (~> 3.1.0)
10
+ gli (~> 2.17)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ ADB (0.5.6)
16
+ childprocess (>= 0.3.5)
17
+ addressable (2.8.1)
18
+ public_suffix (>= 2.0.2, < 6.0)
19
+ childprocess (4.1.0)
20
+ concurrent-ruby (1.2.2)
21
+ crack (0.4.5)
22
+ rexml
23
+ dante (0.2.0)
24
+ diff-lcs (1.5.0)
25
+ docile (1.4.0)
26
+ dry-configurable (0.1.7)
27
+ concurrent-ruby (~> 1.0)
28
+ dry-container (0.6.0)
29
+ concurrent-ruby (~> 1.0)
30
+ dry-configurable (~> 0.1, >= 0.1.3)
31
+ facets (3.1.0)
32
+ gli (2.21.0)
33
+ hashdiff (1.0.1)
34
+ json (2.6.3)
35
+ public_suffix (5.0.1)
36
+ rack (1.6.13)
37
+ rack-protection (1.5.5)
38
+ rack
39
+ rake (10.5.0)
40
+ rexml (3.2.5)
41
+ rspec (3.12.0)
42
+ rspec-core (~> 3.12.0)
43
+ rspec-expectations (~> 3.12.0)
44
+ rspec-mocks (~> 3.12.0)
45
+ rspec-core (3.12.1)
46
+ rspec-support (~> 3.12.0)
47
+ rspec-expectations (3.12.2)
48
+ diff-lcs (>= 1.2.0, < 2.0)
49
+ rspec-support (~> 3.12.0)
50
+ rspec-mocks (3.12.4)
51
+ diff-lcs (>= 1.2.0, < 2.0)
52
+ rspec-support (~> 3.12.0)
53
+ rspec-support (3.12.0)
54
+ rspec_junit_formatter (0.6.0)
55
+ rspec-core (>= 2, < 4, != 2.12.0)
56
+ simplecov (0.22.0)
57
+ docile (~> 1.1)
58
+ simplecov-html (~> 0.11)
59
+ simplecov_json_formatter (~> 0.1)
60
+ simplecov-html (0.12.3)
61
+ simplecov-json (0.2.3)
62
+ json
63
+ simplecov
64
+ simplecov_json_formatter (0.1.4)
65
+ sinatra (1.4.8)
66
+ rack (~> 1.5)
67
+ rack-protection (~> 1.4)
68
+ tilt (>= 1.3, < 3)
69
+ tilt (2.1.0)
70
+ webmock (2.3.2)
71
+ addressable (>= 2.3.6)
72
+ crack (>= 0.3.2)
73
+ hashdiff
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ bundler (~> 1.16.a)
80
+ rake (~> 10.0)
81
+ rspec (~> 3.5)
82
+ rspec_junit_formatter (~> 0.2)
83
+ simplecov (~> 0.14)
84
+ simplecov-json (~> 0.2)
85
+ sinatra (~> 1.4)
86
+ smartdust-client!
87
+ webmock (~> 2.1)
88
+
89
+ BUNDLED WITH
90
+ 1.17.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Anton Malinskiy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ [![Build Status](https://travis-ci.org/Malinskiy/stf-client.svg?branch=master)](https://travis-ci.org/Malinskiy/stf-client)
2
+ [![Gem](https://img.shields.io/gem/v/stf-client.svg)](https://rubygems.org/gems/stf-client)
3
+ [![Gem](https://img.shields.io/gem/dt/stf-client.svg)](https://rubygems.org/gems/stf-client)
4
+
5
+ # Smartdust CLI client
6
+ ![smartdust-logo-text-2021.png](smartdust-logo-text-2021.png)
7
+ Automation client for connecting to Smartdust Lab devices via adb from a terminal.
8
+ Designed with the following scenario in mind:
9
+
10
+ 1. Connect to remote devices
11
+ 2. Do something with the device via adb (Instrumentation Test, adb install, etc)
12
+ 3. Disconnect from device
13
+
14
+ Due to it being a console tool, it's very easy to use it for test automation in a CI\CD pipeline, for example in Jenkins.
15
+
16
+ Allows for filtering by any device description parameter
17
+ as well as listing all available values of a given parameter
18
+ e.g. all unique names of devices in the lab instance.
19
+
20
+ ## Installation from source
21
+ ## Prequisities
22
+
23
+ - Ruby along with RubyGems installed - versions higher than 3.0.2 are not guaranteed to work
24
+ - This repository cloned
25
+
26
+ ## Installation
27
+ - Enter the directory where the repository is cloned
28
+ - adding "sudo" before the following commands might be needed:
29
+ - ```gem build smartdust-client.gemspec```
30
+ - ```gem install smartdust-client-1.0.0.gem``` (or different version, see output of the previous command)
31
+
32
+ - Run it by simply entering ```smartdust-client```
33
+
34
+ ## Usage
35
+
36
+ ```
37
+ NAME
38
+ stf-client - Smartphone Test Lab client
39
+
40
+ SYNOPSIS
41
+ stf-client [global options] command [command options] [arguments...]
42
+
43
+ GLOBAL OPTIONS
44
+ --help - Show this message
45
+ -t, --token=arg - Authorization token, can also be set by environment variable STF_TOKEN (default: none)
46
+ -u, --url=arg - URL to STF, can also be set by environment variable STF_URL (default: none)
47
+ -v, --[no-]verbose - Be verbose
48
+
49
+ COMMANDS
50
+ clean - Frees all devices that are assigned to current user in STF. Doesn't modify local adb
51
+ connect - Search for a device available in STF and attach it to local adb server
52
+ disconnect - Disconnect device(s) from local adb server and remove device(s) from user devices in STF
53
+ help - Shows a list of commands or help for one command
54
+ keys - Show available keys for filtering
55
+ values - Show known values for the filtering key
56
+
57
+ ENVIRONMENT VARIABLES
58
+ STF_TOKEN - Authorization token
59
+ STF_URL - URL to STF
60
+ ```
61
+ - Authorization token can be obtained from the Smartdust Lab web interface in Settings -> Keys
62
+ - When connecting with this tool for the first time, have the Smartdust Lab web interface open
63
+ to accept adding a new ADB key. This is necessary for every new machine that hasn't connected
64
+ yet to Remote Debug on a given Lab instance.
65
+
66
+ ## License
67
+
68
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ begin
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ rescue LoadError
6
+ end
7
+
8
+ task :default => :spec
9
+ task :test => :spec
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ require 'stf'
data/lib/di.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'dry-container'
2
+ require 'dante'
3
+ require 'stf/system/demonizer'
4
+
5
+ require 'stf/interactor/start_debug_session_interactor'
6
+ require 'stf/interactor/start_one_debug_session_interactor'
7
+ require 'stf/interactor/stop_debug_session_interactor'
8
+ require 'stf/interactor/stop_all_debug_sessions_interactor'
9
+ require 'stf/interactor/remove_all_user_devices_interactor'
10
+ require 'stf/interactor/get_keys_interactor'
11
+ require 'stf/interactor/get_values_interactor'
12
+ require 'stf/interactor/add_adb_public_key'
13
+ require 'stf/validate/uri_validator'
14
+ require 'stf/model/device_enhancer'
15
+
16
+ class DI
17
+ class << self
18
+ def init (opts = {})
19
+
20
+ c = Dry::Container.new
21
+ @@container = c
22
+
23
+ # one time object
24
+ c.register(:dante_runner,
25
+ -> {Dante::Runner.new('stf-client')})
26
+
27
+ # one time object because dante is one time
28
+ c.register(:demonizer,
29
+ -> do
30
+ Stf::Demonizer.new(c[:dante_runner],
31
+ log_path: opts[:log], pid_path: opts[:pid])
32
+ end)
33
+
34
+ c.register(:stf,
35
+ -> {Stf::Client.new(opts[:url], opts[:token])},
36
+ memoize: true)
37
+
38
+ c.register(:start_debug_session_interactor,
39
+ -> {Stf::StartDebugSessionInteractor.new},
40
+ memoize: true)
41
+
42
+ c.register(:start_one_debug_session_interactor,
43
+ -> {Stf::StartOneDebugSessionInteractor.new},
44
+ memoize: true)
45
+
46
+ c.register(:get_keys_interactor,
47
+ -> {Stf::GetKeysInteractor.new},
48
+ memoize: true)
49
+
50
+ c.register(:get_values_interactor,
51
+ -> {Stf::GetValuesInteractor.new},
52
+ memoize: true)
53
+
54
+ c.register(:stop_all_debug_sessions_interactor,
55
+ -> {Stf::StopAllDebugSessionsInteractor.new},
56
+ memoize: true)
57
+
58
+ c.register(:stop_debug_session_interactor,
59
+ -> {Stf::StopDebugSessionInteractor.new},
60
+ memoize: true)
61
+
62
+ c.register(:remove_all_user_devices_interactor,
63
+ -> {Stf::RemoveAllUserDevicesInteractor.new},
64
+ memoize: true)
65
+
66
+ c.register(:uri_validator,
67
+ -> {Stf::URIValidator.new},
68
+ memoize: true)
69
+
70
+ c.register(:add_adb_public_key_interactor,
71
+ -> {Stf::AddAdbPublicKeyInteractor.new},
72
+ memoize: true)
73
+ c.register(:device_enhancer,
74
+ -> {Stf::DeviceEnhancer.new},
75
+ memoize: true)
76
+ end
77
+
78
+ def [](what)
79
+ @@container.resolve(what)
80
+ end
81
+
82
+ def container
83
+ @@container
84
+ end
85
+ end
86
+ end
data/lib/stf/client.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'ostruct'
4
+
5
+ require 'stf/version'
6
+ require 'stf/log/log'
7
+
8
+ module Stf
9
+ class Client
10
+ include Log
11
+
12
+ def initialize(base_url, token)
13
+ @base_url = base_url
14
+ @token = token
15
+ end
16
+
17
+ def get_devices
18
+ response = execute '/api/v1/devices', Net::HTTP::Get
19
+ return response.devices
20
+ end
21
+
22
+ def get_device(serial)
23
+ response = execute "/api/v1/devices/#{serial}", Net::HTTP::Get
24
+ return response.device
25
+ end
26
+
27
+ def get_user
28
+ response = execute '/api/v1/user', Net::HTTP::Get
29
+ return response.user
30
+ end
31
+
32
+ def get_user_devices
33
+ response = execute '/api/v1/user/devices', Net::HTTP::Get
34
+ return response.devices
35
+ end
36
+
37
+ def add_device(serial)
38
+ response = execute '/api/v1/user/devices', Net::HTTP::Post, {serial: serial}.to_json
39
+ return response.success
40
+ end
41
+
42
+ def remove_device(serial)
43
+ response = execute "/api/v1/user/devices/#{serial}", Net::HTTP::Delete
44
+ return response.success
45
+ end
46
+
47
+ def start_debug(serial)
48
+ response = execute "/api/v1/user/devices/#{serial}/remoteConnect", Net::HTTP::Post
49
+ return response
50
+ end
51
+
52
+ def open_tunnel(provider_ip, port)
53
+ response = execute "/api/v1/tunnel", Net::HTTP::Post, {ipAddress: provider_ip, port: port}.to_json
54
+ return response
55
+ end
56
+
57
+ def destroy_tunnel(ip, port)
58
+ response = execute "/api/v1/tunnel", Net::HTTP::Delete, {ipAddress: ip, port: port}.to_json
59
+ return response.success
60
+ end
61
+
62
+ def stop_debug(serial)
63
+ response = execute "/api/v1/user/devices/#{serial}/remoteConnect", Net::HTTP::Delete
64
+ return response.success
65
+ end
66
+
67
+ def check_tunnel(provider_ip, port)
68
+ response = execute "/api/v1/tunnel/#{provider_ip}/#{port}", Net::HTTP::Get
69
+ return response
70
+ end
71
+
72
+ def add_adb_public_key(adb_public_key)
73
+ response = execute '/api/v1/user/adbPublicKeys', Net::HTTP::Post, {publickey: adb_public_key }.to_json
74
+ return response.success
75
+ end
76
+
77
+ private
78
+
79
+ def execute(relative_url, type, body='')
80
+ return execute_absolute @base_url + relative_url, type, body
81
+ end
82
+
83
+ def execute_absolute(url, type, body='', limit = 10)
84
+ raise ArgumentError, 'too many HTTP redirects' if limit == 0
85
+
86
+ uri = URI.parse(url)
87
+ http = Net::HTTP.new(uri.host, uri.port)
88
+ http.use_ssl = true if uri.scheme == 'https'
89
+ request = type.new(uri, 'Authorization' => "Bearer #{@token}", 'Content-Type' => 'application/json')
90
+ request.body = body
91
+ response = http.request(request)
92
+
93
+ case response
94
+ when Net::HTTPSuccess then
95
+ json = JSON.parse(response.body, object_class: OpenStruct)
96
+
97
+ logger.debug "API returned #{json}"
98
+ when Net::HTTPRedirection then
99
+ location = response['location']
100
+ logger.debug "redirected to #{location}"
101
+ return execute_absolute(location, type, body, limit - 1)
102
+ else
103
+ logger.error "API returned #{response.value}"
104
+ end
105
+
106
+ return json
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,20 @@
1
+ require 'di'
2
+
3
+ require 'stf/client'
4
+ require 'stf/log/log'
5
+
6
+ module Stf
7
+ class AddAdbPublicKeyInteractor
8
+ include Log
9
+
10
+ def execute(adb_public_key_location)
11
+ public_key = File.open(adb_public_key_location, 'rb', &:read)
12
+ success = DI[:stf].add_adb_public_key public_key
13
+ if success
14
+ logger.info "adb public key from '#{adb_public_key_location}' has been added"
15
+ elsif logger.error "Can't add public key from '#{adb_public_key_location}'"
16
+ return false
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require 'ADB'
2
+ require 'di'
3
+
4
+ require 'stf/client'
5
+ require 'stf/log/log'
6
+ require 'stf/model/device'
7
+
8
+ module Stf
9
+ class GetKeysInteractor
10
+ include Log
11
+ include ADB
12
+
13
+ def execute
14
+ devices = DI[:stf].get_devices
15
+
16
+ if devices.nil? || (devices.is_a?(Array) && devices.empty?)
17
+ logger.info 'No devices connected to STF'
18
+ return []
19
+ end
20
+
21
+ devices
22
+ .map {|d| Device.new(d)}
23
+ .flat_map {|d| d.getKeys}
24
+ .uniq
25
+ .sort
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ require 'di'
2
+ require 'ADB'
3
+
4
+ require 'stf/client'
5
+ require 'stf/log/log'
6
+ require 'stf/model/device'
7
+
8
+ module Stf
9
+ class GetValuesInteractor
10
+
11
+ include Log
12
+ include ADB
13
+
14
+ def execute(key)
15
+ devices = DI[:stf].get_devices
16
+
17
+ if devices.nil? || (devices.is_a?(Array) && devices.empty?)
18
+ logger.info r 'No devices connected to STF'
19
+ return []
20
+ end
21
+
22
+ devices
23
+ .map {|d| Device.new(d)}
24
+ .map {|d| d.getValue(key)}
25
+ .uniq
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,12 @@
1
+ require 'di'
2
+
3
+ module Stf
4
+ class RemoveAllUserDevicesInteractor
5
+ def execute(opts = {})
6
+ DI[:demonizer].kill unless opts[:nokill]
7
+
8
+ devices = DI[:stf].get_user_devices
9
+ devices.each {|d| DI[:stf].remove_device d.serial}
10
+ end
11
+ end
12
+ end