my_john_deere_api 2.4.1
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 +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
|