my_john_deere_api 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +813 -0
- data/Rakefile +9 -0
- data/lib/my_john_deere_api.rb +17 -0
- data/lib/my_john_deere_api/authorize.rb +69 -0
- data/lib/my_john_deere_api/client.rb +151 -0
- data/lib/my_john_deere_api/consumer.rb +90 -0
- data/lib/my_john_deere_api/errors.rb +7 -0
- data/lib/my_john_deere_api/errors/invalid_record_error.rb +29 -0
- data/lib/my_john_deere_api/errors/missing_contribution_definition_id_error.rb +12 -0
- data/lib/my_john_deere_api/errors/not_yet_implemented_error.rb +12 -0
- data/lib/my_john_deere_api/errors/type_mismatch_error.rb +12 -0
- data/lib/my_john_deere_api/errors/unsupported_environment_error.rb +18 -0
- data/lib/my_john_deere_api/helpers.rb +6 -0
- data/lib/my_john_deere_api/helpers/case_conversion.rb +37 -0
- data/lib/my_john_deere_api/helpers/environment_helper.rb +25 -0
- data/lib/my_john_deere_api/helpers/uri_helpers.rb +19 -0
- data/lib/my_john_deere_api/helpers/validate_contribution_definition.rb +18 -0
- data/lib/my_john_deere_api/model.rb +10 -0
- data/lib/my_john_deere_api/model/asset.rb +69 -0
- data/lib/my_john_deere_api/model/asset_location.rb +19 -0
- data/lib/my_john_deere_api/model/base.rb +97 -0
- data/lib/my_john_deere_api/model/contribution_definition.rb +15 -0
- data/lib/my_john_deere_api/model/contribution_product.rb +33 -0
- data/lib/my_john_deere_api/model/field.rb +40 -0
- data/lib/my_john_deere_api/model/flag.rb +36 -0
- data/lib/my_john_deere_api/model/organization.rb +43 -0
- data/lib/my_john_deere_api/net_http_retry.rb +2 -0
- data/lib/my_john_deere_api/net_http_retry/decorator.rb +53 -0
- data/lib/my_john_deere_api/net_http_retry/max_retries_exceeded_error.rb +20 -0
- data/lib/my_john_deere_api/request.rb +6 -0
- data/lib/my_john_deere_api/request/collection.rb +10 -0
- data/lib/my_john_deere_api/request/collection/asset_locations.rb +27 -0
- data/lib/my_john_deere_api/request/collection/assets.rb +34 -0
- data/lib/my_john_deere_api/request/collection/base.rb +91 -0
- data/lib/my_john_deere_api/request/collection/contribution_definitions.rb +26 -0
- data/lib/my_john_deere_api/request/collection/contribution_products.rb +26 -0
- data/lib/my_john_deere_api/request/collection/fields.rb +26 -0
- data/lib/my_john_deere_api/request/collection/flags.rb +33 -0
- data/lib/my_john_deere_api/request/collection/organizations.rb +26 -0
- data/lib/my_john_deere_api/request/create.rb +5 -0
- data/lib/my_john_deere_api/request/create/asset.rb +57 -0
- data/lib/my_john_deere_api/request/create/asset_location.rb +110 -0
- data/lib/my_john_deere_api/request/create/base.rb +67 -0
- data/lib/my_john_deere_api/request/individual.rb +8 -0
- data/lib/my_john_deere_api/request/individual/asset.rb +19 -0
- data/lib/my_john_deere_api/request/individual/base.rb +52 -0
- data/lib/my_john_deere_api/request/individual/contribution_definition.rb +19 -0
- data/lib/my_john_deere_api/request/individual/contribution_product.rb +19 -0
- data/lib/my_john_deere_api/request/individual/field.rb +19 -0
- data/lib/my_john_deere_api/request/individual/organization.rb +19 -0
- data/lib/my_john_deere_api/request/update.rb +4 -0
- data/lib/my_john_deere_api/request/update/asset.rb +40 -0
- data/lib/my_john_deere_api/request/update/base.rb +67 -0
- data/lib/my_john_deere_api/validators.rb +5 -0
- data/lib/my_john_deere_api/validators/asset.rb +35 -0
- data/lib/my_john_deere_api/validators/asset_location.rb +34 -0
- data/lib/my_john_deere_api/validators/base.rb +67 -0
- data/lib/my_john_deere_api/version.rb +3 -0
- data/test/lib/my_john_deere_api/authorize_test.rb +81 -0
- data/test/lib/my_john_deere_api/client_test.rb +225 -0
- data/test/lib/my_john_deere_api/consumer_test.rb +58 -0
- data/test/lib/my_john_deere_api/errors/invalid_record_error_test.rb +26 -0
- data/test/lib/my_john_deere_api/errors/max_retries_exceeded_error_test.rb +13 -0
- data/test/lib/my_john_deere_api/errors/missing_contribution_definition_id_error_test.rb +14 -0
- data/test/lib/my_john_deere_api/errors/not_yet_implemented_error_test.rb +13 -0
- data/test/lib/my_john_deere_api/errors/type_mismatch_error_test.rb +13 -0
- data/test/lib/my_john_deere_api/errors/unsupported_environment_error_test.rb +18 -0
- data/test/lib/my_john_deere_api/errors_test.rb +25 -0
- data/test/lib/my_john_deere_api/helpers/case_conversion_test.rb +100 -0
- data/test/lib/my_john_deere_api/helpers/environment_helper_test.rb +67 -0
- data/test/lib/my_john_deere_api/helpers/uri_helpers_test.rb +58 -0
- data/test/lib/my_john_deere_api/helpers/validate_contribution_definition_test.rb +74 -0
- data/test/lib/my_john_deere_api/helpers_test.rb +21 -0
- data/test/lib/my_john_deere_api/model/asset_location_test.rb +38 -0
- data/test/lib/my_john_deere_api/model/asset_test.rb +133 -0
- data/test/lib/my_john_deere_api/model/base_test.rb +76 -0
- data/test/lib/my_john_deere_api/model/contribution_definition_test.rb +52 -0
- data/test/lib/my_john_deere_api/model/contribution_product_test.rb +82 -0
- data/test/lib/my_john_deere_api/model/field_test.rb +65 -0
- data/test/lib/my_john_deere_api/model/flag_test.rb +48 -0
- data/test/lib/my_john_deere_api/model/organization_test.rb +84 -0
- data/test/lib/my_john_deere_api/model_test.rb +37 -0
- data/test/lib/my_john_deere_api/net_http_retry/decorator_test.rb +163 -0
- data/test/lib/my_john_deere_api/request/collection/asset_locations_test.rb +102 -0
- data/test/lib/my_john_deere_api/request/collection/assets_test.rb +99 -0
- data/test/lib/my_john_deere_api/request/collection/base_test.rb +27 -0
- data/test/lib/my_john_deere_api/request/collection/contribution_definitions_test.rb +88 -0
- data/test/lib/my_john_deere_api/request/collection/contribution_products_test.rb +88 -0
- data/test/lib/my_john_deere_api/request/collection/fields_test.rb +86 -0
- data/test/lib/my_john_deere_api/request/collection/flags_test.rb +92 -0
- data/test/lib/my_john_deere_api/request/collection/organizations_test.rb +87 -0
- data/test/lib/my_john_deere_api/request/collection_test.rb +37 -0
- data/test/lib/my_john_deere_api/request/create/asset_location_test.rb +163 -0
- data/test/lib/my_john_deere_api/request/create/asset_test.rb +182 -0
- data/test/lib/my_john_deere_api/request/create/base_test.rb +29 -0
- data/test/lib/my_john_deere_api/request/create_test.rb +17 -0
- data/test/lib/my_john_deere_api/request/individual/asset_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual/base_test.rb +18 -0
- data/test/lib/my_john_deere_api/request/individual/contribution_definition_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual/contribution_product_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual/field_test.rb +37 -0
- data/test/lib/my_john_deere_api/request/individual/organization_test.rb +33 -0
- data/test/lib/my_john_deere_api/request/individual_test.rb +29 -0
- data/test/lib/my_john_deere_api/request/update/asset_test.rb +99 -0
- data/test/lib/my_john_deere_api/request/update/base_test.rb +60 -0
- data/test/lib/my_john_deere_api/request/update_test.rb +13 -0
- data/test/lib/my_john_deere_api/request_test.rb +21 -0
- data/test/lib/my_john_deere_api/validators/asset_location_test.rb +61 -0
- data/test/lib/my_john_deere_api/validators/asset_test.rb +93 -0
- data/test/lib/my_john_deere_api/validators/base_test.rb +92 -0
- data/test/lib/my_john_deere_api/validators_test.rb +17 -0
- data/test/lib/my_john_deere_api/version_test.rb +9 -0
- data/test/my_john_deere_api_test.rb +37 -0
- data/test/support/helper.rb +97 -0
- data/test/support/vcr/accessor/delete_failed.yml +327 -0
- data/test/support/vcr/accessor/delete_max_failed.yml +615 -0
- data/test/support/vcr/accessor/delete_retry.yml +191 -0
- data/test/support/vcr/accessor/delete_retry_too_soon.yml +191 -0
- data/test/support/vcr/accessor/get_failed.yml +390 -0
- data/test/support/vcr/accessor/get_max_failed.yml +734 -0
- data/test/support/vcr/accessor/get_retry.yml +226 -0
- data/test/support/vcr/accessor/get_retry_too_soon.yml +226 -0
- data/test/support/vcr/accessor/post_failed.yml +417 -0
- data/test/support/vcr/accessor/post_max_failed.yml +785 -0
- data/test/support/vcr/accessor/post_retry.yml +241 -0
- data/test/support/vcr/accessor/post_retry_too_soon.yml +241 -0
- data/test/support/vcr/accessor/put_failed.yml +372 -0
- data/test/support/vcr/accessor/put_max_failed.yml +700 -0
- data/test/support/vcr/accessor/put_retry.yml +216 -0
- data/test/support/vcr/accessor/put_retry_too_soon.yml +216 -0
- data/test/support/vcr/catalog.yml +89 -0
- data/test/support/vcr/delete_asset.yml +82 -0
- data/test/support/vcr/get_access_token.yml +41 -0
- data/test/support/vcr/get_asset.yml +144 -0
- data/test/support/vcr/get_asset_locations.yml +196 -0
- data/test/support/vcr/get_assets.yml +152 -0
- data/test/support/vcr/get_contribution_definition.yml +90 -0
- data/test/support/vcr/get_contribution_definitions.yml +91 -0
- data/test/support/vcr/get_contribution_product.yml +91 -0
- data/test/support/vcr/get_contribution_products.yml +91 -0
- data/test/support/vcr/get_field.yml +144 -0
- data/test/support/vcr/get_fields.yml +146 -0
- data/test/support/vcr/get_flags.yml +47 -0
- data/test/support/vcr/get_organization.yml +90 -0
- data/test/support/vcr/get_organizations.yml +149 -0
- data/test/support/vcr/get_request_token.yml +83 -0
- data/test/support/vcr/post_asset_locations.yml +244 -0
- data/test/support/vcr/post_assets.yml +192 -0
- data/test/support/vcr/put_asset.yml +87 -0
- data/test/support/vcr/warning.txt +28 -0
- data/test/support/vcr_setup.rb +488 -0
- metadata +277 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://sandboxapi.deere.com/platform/
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/vnd.deere.axiom.v3+json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
User-Agent:
|
15
|
+
- OAuth gem v0.5.4
|
16
|
+
Authorization:
|
17
|
+
- OAuth oauth_consumer_key="johndeere-0000000000000000000000000000000000000000",
|
18
|
+
oauth_nonce="000000000000000000000000000000000000000000", oauth_signature="0000000000000000000000000000",
|
19
|
+
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1581334172", oauth_version="1.0"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Mon, 10 Feb 2020 17:29:29 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/vnd.deere.axiom.v3+json;charset=UTF-8
|
29
|
+
X-Deere-Handling-Server:
|
30
|
+
- ip-10-214-45-99
|
31
|
+
X-Frame-Options:
|
32
|
+
- SAMEORIGIN
|
33
|
+
X-Deere-Elapsed-Ms:
|
34
|
+
- '10'
|
35
|
+
Cache-Control:
|
36
|
+
- no-store
|
37
|
+
Content-Language:
|
38
|
+
- en-US
|
39
|
+
Transfer-Encoding:
|
40
|
+
- chunked
|
41
|
+
body:
|
42
|
+
encoding: ASCII-8BIT
|
43
|
+
string: '{"@type":"ApiCatalog","links":[{"@type":"Link","rel":"oauthRequestToken","uri":"https://sandboxapi.deere.com/platform/oauth/request_token"},{"@type":"Link","rel":"oauthAuthorizeRequestToken","uri":"https://my.deere.com/consentToUseOfData?oauth_token={token}"},{"@type":"Link","rel":"oauthAccessToken","uri":"https://sandboxapi.deere.com/platform/oauth/access_token"},{"@type":"Link","rel":"agencies","uri":"https://sandboxapi.deere.com/platform/agencies"}]}'
|
44
|
+
http_version:
|
45
|
+
recorded_at: Mon, 10 Feb 2020 17:29:29 GMT
|
46
|
+
- request:
|
47
|
+
method: put
|
48
|
+
uri: https://sandboxapi.deere.com/platform/assets/00000000-0000-0000-0000-000000000000
|
49
|
+
body:
|
50
|
+
encoding: UTF-8
|
51
|
+
string: '{"assetCategory":"DEVICE","assetType":"SENSOR","assetSubType":"ENVIRONMENTAL","title":"i
|
52
|
+
REALLY like turtles!","links":[{"@type":"Link","rel":"contributionDefinition","uri":"https://sandboxapi.deere.com/contributionDefinitions/00000000-0000-0000-0000-000000000000"}]}'
|
53
|
+
headers:
|
54
|
+
Accept:
|
55
|
+
- application/vnd.deere.axiom.v3+json
|
56
|
+
Content-Type:
|
57
|
+
- application/vnd.deere.axiom.v3+json
|
58
|
+
Accept-Encoding:
|
59
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
60
|
+
User-Agent:
|
61
|
+
- OAuth gem v0.5.4
|
62
|
+
Content-Length:
|
63
|
+
- '267'
|
64
|
+
Authorization:
|
65
|
+
- OAuth oauth_body_hash="JXqhMr0ayK5gfHyej3M1xuUfSbw%3D", oauth_consumer_key="johndeere-0000000000000000000000000000000000000000",
|
66
|
+
oauth_nonce="000000000000000000000000000000000000000000", oauth_signature="0000000000000000000000000000",
|
67
|
+
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1581334172", oauth_token="00000000-0000-0000-0000-000000000000",
|
68
|
+
oauth_version="1.0"
|
69
|
+
response:
|
70
|
+
status:
|
71
|
+
code: 204
|
72
|
+
message: No Content
|
73
|
+
headers:
|
74
|
+
Date:
|
75
|
+
- Mon, 10 Feb 2020 17:29:30 GMT
|
76
|
+
X-Deere-Handling-Server:
|
77
|
+
- ip-10-214-44-33
|
78
|
+
X-Frame-Options:
|
79
|
+
- SAMEORIGIN
|
80
|
+
X-Deere-Elapsed-Ms:
|
81
|
+
- '185'
|
82
|
+
body:
|
83
|
+
encoding: UTF-8
|
84
|
+
string: ''
|
85
|
+
http_version:
|
86
|
+
recorded_at: Mon, 10 Feb 2020 17:29:30 GMT
|
87
|
+
recorded_with: VCR 5.0.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
-------------------------------------------------------------------------------
|
2
|
+
WARNING: Generating VCR cassettes requires the following in your John Deere
|
3
|
+
API sandbox account:
|
4
|
+
|
5
|
+
- Environment variables:
|
6
|
+
- API_KEY
|
7
|
+
- API_SECRET
|
8
|
+
- A valid John Deere user account with which you can authenticate.
|
9
|
+
- Asset creation/deletion enabled in your JD account.
|
10
|
+
- At least one organization created in your JD account.
|
11
|
+
- The organization must have at least one field.
|
12
|
+
- The field must have at least one flag.
|
13
|
+
|
14
|
+
By default, this setup will use the first organization returned from the API.
|
15
|
+
If this is not the organization you wish to use, you can create an environment
|
16
|
+
variable ORGANIZATION_ID which contains the id of the organization you wish to
|
17
|
+
use. This organization must contain a field, which contains a flag.
|
18
|
+
|
19
|
+
This is required because organizations cannot be created via the API, and
|
20
|
+
fields and flags can be created but not destroyed via the API. We don't create
|
21
|
+
anything during VCR cassette recording that we can't undo. This process will
|
22
|
+
leave your sandbox environment in the exact state it was before the cassettes
|
23
|
+
were generated.
|
24
|
+
|
25
|
+
If you don't meet the above requirements, you will be unable to successfully
|
26
|
+
generate the VCR cassettes.
|
27
|
+
-------------------------------------------------------------------------------
|
28
|
+
|
@@ -0,0 +1,488 @@
|
|
1
|
+
require 'vcr'
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
##########################################################################
|
7
|
+
# We're going to make a bunch of requests upfront so we can create/delete
|
8
|
+
# things in the correct order, and end up with no permanent changes to the
|
9
|
+
# sandbox environment. We only need to do this pre-emptively for resources
|
10
|
+
# where we create/delete. We build the client from scratch to get the full
|
11
|
+
# interaction history recorded.
|
12
|
+
##########################################################################
|
13
|
+
|
14
|
+
class VcrSetup
|
15
|
+
attr_reader :api_key, :api_secret, :access_token, :access_secret, :verify_code,
|
16
|
+
:contribution_product_id, :contribution_definition_id,
|
17
|
+
:placeholders, :organization_id, :asset_id, :field_id, :flag_id
|
18
|
+
|
19
|
+
AUTH_CASSETTES = [:catalog, :get_request_token, :get_access_token]
|
20
|
+
|
21
|
+
GENERATED_CASSETTES = [
|
22
|
+
:catalog, :get_request_token, :get_access_token,
|
23
|
+
:get_contribution_products, :get_contribution_product,
|
24
|
+
:get_contribution_definitions, :get_contribution_definition,
|
25
|
+
:get_organizations, :get_organization,
|
26
|
+
:get_fields, :get_field, :get_flags,
|
27
|
+
:post_assets, :get_assets, :get_asset, :put_asset,
|
28
|
+
:post_asset_locations, :get_asset_locations,
|
29
|
+
:delete_asset
|
30
|
+
]
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@vcr_dir = File.dirname(__FILE__) + '/vcr'
|
34
|
+
|
35
|
+
# Before we configure VCR, we want to make a few requests to set some constants
|
36
|
+
@client = new_client
|
37
|
+
|
38
|
+
# placeholders
|
39
|
+
@uuid = '00000000-0000-0000-0000-000000000000'
|
40
|
+
@api_key = 'johndeere-0000000000000000000000000000000000000000'
|
41
|
+
@api_secret = '0' * 64
|
42
|
+
@access_token = @uuid
|
43
|
+
@access_secret = '0' * 107 + '='
|
44
|
+
@contribution_product_id = @uuid
|
45
|
+
@contribution_definition_id = @uuid
|
46
|
+
@organization_id = '000000'
|
47
|
+
@asset_id = @uuid
|
48
|
+
@field_id = @uuid
|
49
|
+
@flag_id = @uuid
|
50
|
+
@verify_code = 'VERIFY'
|
51
|
+
@placeholders = {}
|
52
|
+
|
53
|
+
unless all_cassettes_generated? || meets_vcr_requirements?
|
54
|
+
raise "Cannot continue until VCR cassettes can be generated."
|
55
|
+
end
|
56
|
+
|
57
|
+
configure_vcr
|
58
|
+
|
59
|
+
# create and retrieve things
|
60
|
+
unless all_cassettes_generated?
|
61
|
+
puts "\ngenerating:"
|
62
|
+
|
63
|
+
unless ENV['ACCESS_TOKEN'] && ENV['ACCESS_SECRET']
|
64
|
+
generate_cassettes(AUTH_CASSETTES)
|
65
|
+
end
|
66
|
+
|
67
|
+
@placeholders.merge!(
|
68
|
+
ENV['API_KEY'] => @api_key,
|
69
|
+
ENV['API_SECRET'] => @api_secret,
|
70
|
+
ENV['ACCESS_TOKEN'] => @access_token,
|
71
|
+
ENV['ACCESS_SECRET'] => @access_secret
|
72
|
+
)
|
73
|
+
|
74
|
+
VCR.use_cassette('temp') do
|
75
|
+
set_contribution_product_id
|
76
|
+
set_contribution_definition_id
|
77
|
+
set_organization_id
|
78
|
+
end
|
79
|
+
|
80
|
+
File.unlink("#{@vcr_dir}/temp.yml")
|
81
|
+
|
82
|
+
generate_cassettes(GENERATED_CASSETTES)
|
83
|
+
end
|
84
|
+
|
85
|
+
sanitize_files
|
86
|
+
end
|
87
|
+
|
88
|
+
# provide a client with sanitized credentials
|
89
|
+
def client
|
90
|
+
JD::Client.new(
|
91
|
+
api_key,
|
92
|
+
api_secret,
|
93
|
+
contribution_definition_id: contribution_definition_id,
|
94
|
+
environment: :sandbox,
|
95
|
+
access: [access_token, access_secret]
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def timestamp
|
100
|
+
return @timestamp if defined?(@timestamp)
|
101
|
+
|
102
|
+
filename = "#{@vcr_dir}/get_asset_locations.yml"
|
103
|
+
|
104
|
+
@timestamp = if File.exist?(filename)
|
105
|
+
contents = File.read('test/support/vcr/get_asset_locations.yml')
|
106
|
+
body = YAML.load(contents)['http_interactions'].last['response']['body']['string']
|
107
|
+
JSON.parse(body)['values'].last['timestamp']
|
108
|
+
else
|
109
|
+
Time.now.strftime('%Y-%m-%dT%H:%M:%S.000Z')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def epoch_timestamp
|
114
|
+
return @epoch_timestamp if defined?(@epoch_timestamp)
|
115
|
+
@epoch_timestamp = DateTime.parse(timestamp).to_time.to_i
|
116
|
+
end
|
117
|
+
|
118
|
+
def coordinates
|
119
|
+
@coordinates ||= [-96.668978, 40.865984]
|
120
|
+
end
|
121
|
+
|
122
|
+
def geometry
|
123
|
+
@geometry ||= {
|
124
|
+
type: 'Feature',
|
125
|
+
geometry: {
|
126
|
+
geometries: [
|
127
|
+
coordinates: coordinates,
|
128
|
+
type: 'Point'
|
129
|
+
],
|
130
|
+
type: 'GeometryCollection'
|
131
|
+
}
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def measurement_data
|
136
|
+
@measurement_data ||= [
|
137
|
+
{
|
138
|
+
name: 'Temperature',
|
139
|
+
value: '68.0',
|
140
|
+
unit: 'F'
|
141
|
+
}
|
142
|
+
]
|
143
|
+
end
|
144
|
+
|
145
|
+
def asset_attributes
|
146
|
+
@asset_attributes ||= {
|
147
|
+
title: 'Asset Title',
|
148
|
+
asset_category: 'DEVICE',
|
149
|
+
asset_type: 'SENSOR',
|
150
|
+
asset_sub_type: 'ENVIRONMENTAL'
|
151
|
+
}.freeze
|
152
|
+
end
|
153
|
+
|
154
|
+
def asset_location_attributes
|
155
|
+
@asset_location_attributes ||= {
|
156
|
+
timestamp: timestamp,
|
157
|
+
geometry: geometry,
|
158
|
+
measurement_data: measurement_data
|
159
|
+
}.freeze
|
160
|
+
end
|
161
|
+
|
162
|
+
def url
|
163
|
+
return @url if defined?(@url)
|
164
|
+
@url = JD::Consumer::URLS[:sandbox]
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def generate_cassettes(list)
|
170
|
+
list.each do |method_name|
|
171
|
+
filename = "#{method_name}.yml"
|
172
|
+
|
173
|
+
if File.exist?("#{@vcr_dir}/#{filename}")
|
174
|
+
puts " - using #{filename}"
|
175
|
+
else
|
176
|
+
puts " - generating #{filename}"
|
177
|
+
VCR.use_cassette(method_name) { send(method_name) }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def all_cassettes_generated?
|
183
|
+
return @all_cassettes_generated if defined?(@all_cassettes_generated)
|
184
|
+
@all_cassettes_generated = GENERATED_CASSETTES.all?{|cassette| File.exist?("#{@vcr_dir}/#{cassette}.yml") }
|
185
|
+
end
|
186
|
+
|
187
|
+
# provide a fresh client with no memoized requests
|
188
|
+
def new_client
|
189
|
+
JD::Client.new(
|
190
|
+
ENV['API_KEY'],
|
191
|
+
ENV['API_SECRET'],
|
192
|
+
environment: :sandbox,
|
193
|
+
contribution_definition_id: ENV['CONTRIBUTION_DEFINITION_ID'],
|
194
|
+
access: [ENV['ACCESS_TOKEN'], ENV['ACCESS_SECRET']]
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
def catalog
|
199
|
+
new_client.get('/')
|
200
|
+
end
|
201
|
+
|
202
|
+
def get_request_token
|
203
|
+
@temporary_authorize = JD::Authorize.new(
|
204
|
+
ENV['API_KEY'],
|
205
|
+
ENV['API_SECRET'],
|
206
|
+
environment: :sandbox
|
207
|
+
)
|
208
|
+
|
209
|
+
@temporary_authorize_url = @temporary_authorize.authorize_url
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_access_token
|
213
|
+
puts "\n\n----\nFOLLOW THIS LINK, AND ENTER THE VERIFICATION CODE:\n#{@temporary_authorize_url}\n----\n\n"
|
214
|
+
$stdout.print 'Verification Code: '; $stdout.flush
|
215
|
+
code = $stdin.gets.chomp
|
216
|
+
puts
|
217
|
+
|
218
|
+
placeholders[code] = verify_code
|
219
|
+
|
220
|
+
@temporary_authorize.verify(code)
|
221
|
+
|
222
|
+
unless ENV['ACCESS_TOKEN'] && ENV['ACCESS_SECRET']
|
223
|
+
set_env 'ACCESS_TOKEN', @temporary_authorize.access_token
|
224
|
+
set_env 'ACCESS_SECRET', @temporary_authorize.access_secret
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def get_contribution_products
|
229
|
+
new_client.get('/contributionProducts?clientControlled=true')
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_contribution_product
|
233
|
+
new_client.get("/contributionProducts/#{ENV['CONTRIBUTION_PRODUCT_ID']}")
|
234
|
+
end
|
235
|
+
|
236
|
+
def get_contribution_definitions
|
237
|
+
new_client.get("/contributionProducts/#{ENV['CONTRIBUTION_PRODUCT_ID']}/contributionDefinitions")
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_contribution_definition
|
241
|
+
new_client.get("/contributionDefinitions/#{ENV['CONTRIBUTION_DEFINITION_ID']}")
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_organizations
|
245
|
+
new_client.organizations.all
|
246
|
+
end
|
247
|
+
|
248
|
+
def get_organization
|
249
|
+
new_client.organizations.find(ENV['ORGANIZATION_ID'])
|
250
|
+
end
|
251
|
+
|
252
|
+
def get_fields
|
253
|
+
@temporary_field = find_organization(ENV['ORGANIZATION_ID']).fields.all.first
|
254
|
+
end
|
255
|
+
|
256
|
+
def get_field
|
257
|
+
find_organization(ENV['ORGANIZATION_ID']).fields.find(@temporary_field.id)
|
258
|
+
end
|
259
|
+
|
260
|
+
def get_flags
|
261
|
+
@temporary_field.flags.all
|
262
|
+
end
|
263
|
+
|
264
|
+
def post_assets
|
265
|
+
@temporary_asset_id = find_organization(ENV['ORGANIZATION_ID']).assets.create(asset_attributes).id
|
266
|
+
placeholders[@temporary_asset_id] = asset_id
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_assets
|
270
|
+
find_organization(ENV['ORGANIZATION_ID']).assets.all
|
271
|
+
end
|
272
|
+
|
273
|
+
def get_asset
|
274
|
+
find_organization(ENV['ORGANIZATION_ID']).assets.find(@temporary_asset_id)
|
275
|
+
end
|
276
|
+
|
277
|
+
def put_asset
|
278
|
+
attrs = asset_attributes.slice(
|
279
|
+
:asset_category, :asset_type, :asset_sub_type, :links
|
280
|
+
).merge(
|
281
|
+
title: 'i REALLY like turtles!',
|
282
|
+
links: [
|
283
|
+
{
|
284
|
+
'@type' => 'Link',
|
285
|
+
'rel' => 'contributionDefinition',
|
286
|
+
'uri' => "#{url}/contributionDefinitions/#{asset_attributes[:contribution_definition_id]}"
|
287
|
+
}
|
288
|
+
]
|
289
|
+
)
|
290
|
+
|
291
|
+
new_client.put("/assets/#{@temporary_asset_id}", attrs)
|
292
|
+
end
|
293
|
+
|
294
|
+
def delete_asset
|
295
|
+
new_client.delete("/assets/#{@temporary_asset_id}")
|
296
|
+
end
|
297
|
+
|
298
|
+
def post_asset_locations
|
299
|
+
find_asset(ENV['ORGANIZATION_ID'], @temporary_asset_id).locations.create(asset_location_attributes)
|
300
|
+
end
|
301
|
+
|
302
|
+
def get_asset_locations
|
303
|
+
find_asset(ENV['ORGANIZATION_ID'], @temporary_asset_id).locations.all
|
304
|
+
end
|
305
|
+
|
306
|
+
def configure_vcr
|
307
|
+
VCR.configure do |config|
|
308
|
+
config.cassette_library_dir = 'test/support/vcr'
|
309
|
+
config.hook_into :webmock
|
310
|
+
config.default_cassette_options = {record: :once}
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def sanitize_files
|
315
|
+
Dir[File.dirname(__FILE__) + "/vcr/**/*.yml"].each do |filename|
|
316
|
+
text = sanitize_text(File.read(filename))
|
317
|
+
|
318
|
+
data = YAML.load(text)
|
319
|
+
sanitize_yaml!(data)
|
320
|
+
|
321
|
+
File.write(filename, data.to_yaml)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def sanitize_text(text)
|
326
|
+
data = text
|
327
|
+
|
328
|
+
placeholders.each do |value, placeholder|
|
329
|
+
data.gsub!(value, placeholder)
|
330
|
+
end
|
331
|
+
|
332
|
+
# organization links
|
333
|
+
data.gsub!(/\/organizations\/[0-9]+/, "/organizations/#{organization_id}")
|
334
|
+
|
335
|
+
# random ids
|
336
|
+
data.gsub!(/"id":"[0-9]+"/, '"id":"000000"')
|
337
|
+
|
338
|
+
# uuids
|
339
|
+
data.gsub!(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/, @uuid)
|
340
|
+
|
341
|
+
# timestamps
|
342
|
+
data.gsub!(/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z/, timestamp)
|
343
|
+
data.gsub!(/1[4567][0-9]{8}/, epoch_timestamp.to_s)
|
344
|
+
|
345
|
+
# geo coordinates
|
346
|
+
data.gsub!(/\[-{0,1}[0-9]{1,3}\.[0-9]{6},-{0,1}[0-9]{1,3}\.[0-9]{6}\]/, coordinates.to_s.gsub(' ', ''))
|
347
|
+
|
348
|
+
# oauth headers
|
349
|
+
data.gsub!(/oauth_nonce="[0-9a-zA-Z]+"/, 'oauth_nonce="000000000000000000000000000000000000000000"')
|
350
|
+
data.gsub!(/oauth_signature="[0-9a-zA-Z%]+"/, 'oauth_signature="0000000000000000000000000000"')
|
351
|
+
data.gsub!(/oauth_token_secret=[0-9a-zA-Z%]+/, 'oauth_token_secret=000000000000000000000000000000000000000000')
|
352
|
+
data.gsub!(/oauth_verifier="[0-9A-Za-z]{6}"/, 'oauth_verifier="VERIFY"')
|
353
|
+
|
354
|
+
data
|
355
|
+
end
|
356
|
+
|
357
|
+
def sanitize_yaml!(data)
|
358
|
+
if data.is_a?(Array)
|
359
|
+
data.each { |item| sanitize_yaml!(item) }
|
360
|
+
elsif data.is_a?(Hash)
|
361
|
+
data.each do |key, value|
|
362
|
+
if key == 'string'
|
363
|
+
data[key] = sanitize_response_body(value)
|
364
|
+
else
|
365
|
+
sanitize_yaml!(value)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def sanitize_response_body(string)
|
372
|
+
data = nil
|
373
|
+
|
374
|
+
# if this isn't JSON, just return the original string
|
375
|
+
begin
|
376
|
+
data = JSON.parse(string)
|
377
|
+
rescue JSON::ParserError
|
378
|
+
return string
|
379
|
+
end
|
380
|
+
|
381
|
+
# various response body attributes
|
382
|
+
if data.is_a?(Hash)
|
383
|
+
if data['values']
|
384
|
+
data['values'].each do |value|
|
385
|
+
value = sanitize_value(value)
|
386
|
+
end
|
387
|
+
elsif data['@type']
|
388
|
+
data = sanitize_value(data)
|
389
|
+
end
|
390
|
+
elsif data.is_a?(Array)
|
391
|
+
data.each do |value|
|
392
|
+
# nothing yet
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
data.to_json
|
397
|
+
end
|
398
|
+
|
399
|
+
def sanitize_value(value)
|
400
|
+
merge_hash = case value['@type']
|
401
|
+
when 'Organization'
|
402
|
+
{'name' => 'Organization Name'}
|
403
|
+
when 'ContributionProduct'
|
404
|
+
{
|
405
|
+
'marketPlaceName' => 'Market Place Name',
|
406
|
+
'marketPlaceDescription' => 'Market Place Description'
|
407
|
+
}
|
408
|
+
when 'ContributionDefinition'
|
409
|
+
{'name' => 'Definition Name'}
|
410
|
+
when 'ContributedAsset'
|
411
|
+
{
|
412
|
+
'title' => 'Asset Title',
|
413
|
+
'assetCategory' => 'DEVICE',
|
414
|
+
'assetType' => 'SENSOR',
|
415
|
+
'assetSubType' => 'ENVIRONMENTAL'
|
416
|
+
}
|
417
|
+
when 'Field'
|
418
|
+
{'name' => 'Field Name'}
|
419
|
+
else
|
420
|
+
{}
|
421
|
+
end
|
422
|
+
|
423
|
+
value.merge!(merge_hash)
|
424
|
+
end
|
425
|
+
|
426
|
+
def set_contribution_product_id
|
427
|
+
unless ENV['CONTRIBUTION_PRODUCT_ID']
|
428
|
+
set_env(
|
429
|
+
'CONTRIBUTION_PRODUCT_ID',
|
430
|
+
new_client.get('/contributionProducts?clientControlled=true')['values'].first['id']
|
431
|
+
)
|
432
|
+
end
|
433
|
+
|
434
|
+
placeholders[ENV['CONTRIBUTION_PRODUCT_ID']] = contribution_product_id
|
435
|
+
end
|
436
|
+
|
437
|
+
def set_contribution_definition_id
|
438
|
+
unless ENV['CONTRIBUTION_DEFINITION_ID']
|
439
|
+
set_env(
|
440
|
+
'CONTRIBUTION_DEFINITION_ID',
|
441
|
+
new_client.get("/contributionProducts/#{ENV['CONTRIBUTION_PRODUCT_ID']}/contributionDefinitions")['values'].first['id']
|
442
|
+
)
|
443
|
+
end
|
444
|
+
|
445
|
+
placeholders[ENV['CONTRIBUTION_DEFINITION_ID']] = contribution_definition_id
|
446
|
+
end
|
447
|
+
|
448
|
+
def set_organization_id
|
449
|
+
unless ENV['ORGANIZATION_ID']
|
450
|
+
set_env(
|
451
|
+
'ORGANIZATION_ID',
|
452
|
+
new_client.get("/organizations")['values'].first['id']
|
453
|
+
)
|
454
|
+
end
|
455
|
+
|
456
|
+
placeholders[ENV['ORGANIZATION_ID']] = organization_id
|
457
|
+
end
|
458
|
+
|
459
|
+
def set_env(name, value)
|
460
|
+
puts "SAVING ENVIRONMENT VARIABLE: #{name}: #{value.inspect}"
|
461
|
+
|
462
|
+
ENV[name] = value
|
463
|
+
File.open('.env', 'a') { |f| f.puts "#{name}=#{value}"}
|
464
|
+
end
|
465
|
+
|
466
|
+
def find_organization(id)
|
467
|
+
new_client.organizations.detect{|org| org.id == id}
|
468
|
+
end
|
469
|
+
|
470
|
+
def find_asset(org_id, id)
|
471
|
+
find_organization(org_id).assets.detect{|asset| asset.id == id}
|
472
|
+
end
|
473
|
+
|
474
|
+
def meets_vcr_requirements?
|
475
|
+
missing_env_vars = ['API_KEY', 'API_SECRET'].reject{|var| ENV[var]}
|
476
|
+
|
477
|
+
unless missing_env_vars.empty?
|
478
|
+
puts "The following required environment variables are missing: #{missing_env_vars.join(', ')}"
|
479
|
+
return false
|
480
|
+
end
|
481
|
+
|
482
|
+
puts File.read("#{@vcr_dir}/warning.txt")
|
483
|
+
$stdout.print("Do you meet the requirements? [y/N]: "); $stdout.flush
|
484
|
+
|
485
|
+
answer = $stdin.gets.chomp
|
486
|
+
answer.downcase == 'y'
|
487
|
+
end
|
488
|
+
end
|