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.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +813 -0
  3. data/Rakefile +9 -0
  4. data/lib/my_john_deere_api.rb +17 -0
  5. data/lib/my_john_deere_api/authorize.rb +69 -0
  6. data/lib/my_john_deere_api/client.rb +151 -0
  7. data/lib/my_john_deere_api/consumer.rb +90 -0
  8. data/lib/my_john_deere_api/errors.rb +7 -0
  9. data/lib/my_john_deere_api/errors/invalid_record_error.rb +29 -0
  10. data/lib/my_john_deere_api/errors/missing_contribution_definition_id_error.rb +12 -0
  11. data/lib/my_john_deere_api/errors/not_yet_implemented_error.rb +12 -0
  12. data/lib/my_john_deere_api/errors/type_mismatch_error.rb +12 -0
  13. data/lib/my_john_deere_api/errors/unsupported_environment_error.rb +18 -0
  14. data/lib/my_john_deere_api/helpers.rb +6 -0
  15. data/lib/my_john_deere_api/helpers/case_conversion.rb +37 -0
  16. data/lib/my_john_deere_api/helpers/environment_helper.rb +25 -0
  17. data/lib/my_john_deere_api/helpers/uri_helpers.rb +19 -0
  18. data/lib/my_john_deere_api/helpers/validate_contribution_definition.rb +18 -0
  19. data/lib/my_john_deere_api/model.rb +10 -0
  20. data/lib/my_john_deere_api/model/asset.rb +69 -0
  21. data/lib/my_john_deere_api/model/asset_location.rb +19 -0
  22. data/lib/my_john_deere_api/model/base.rb +97 -0
  23. data/lib/my_john_deere_api/model/contribution_definition.rb +15 -0
  24. data/lib/my_john_deere_api/model/contribution_product.rb +33 -0
  25. data/lib/my_john_deere_api/model/field.rb +40 -0
  26. data/lib/my_john_deere_api/model/flag.rb +36 -0
  27. data/lib/my_john_deere_api/model/organization.rb +43 -0
  28. data/lib/my_john_deere_api/net_http_retry.rb +2 -0
  29. data/lib/my_john_deere_api/net_http_retry/decorator.rb +53 -0
  30. data/lib/my_john_deere_api/net_http_retry/max_retries_exceeded_error.rb +20 -0
  31. data/lib/my_john_deere_api/request.rb +6 -0
  32. data/lib/my_john_deere_api/request/collection.rb +10 -0
  33. data/lib/my_john_deere_api/request/collection/asset_locations.rb +27 -0
  34. data/lib/my_john_deere_api/request/collection/assets.rb +34 -0
  35. data/lib/my_john_deere_api/request/collection/base.rb +91 -0
  36. data/lib/my_john_deere_api/request/collection/contribution_definitions.rb +26 -0
  37. data/lib/my_john_deere_api/request/collection/contribution_products.rb +26 -0
  38. data/lib/my_john_deere_api/request/collection/fields.rb +26 -0
  39. data/lib/my_john_deere_api/request/collection/flags.rb +33 -0
  40. data/lib/my_john_deere_api/request/collection/organizations.rb +26 -0
  41. data/lib/my_john_deere_api/request/create.rb +5 -0
  42. data/lib/my_john_deere_api/request/create/asset.rb +57 -0
  43. data/lib/my_john_deere_api/request/create/asset_location.rb +110 -0
  44. data/lib/my_john_deere_api/request/create/base.rb +67 -0
  45. data/lib/my_john_deere_api/request/individual.rb +8 -0
  46. data/lib/my_john_deere_api/request/individual/asset.rb +19 -0
  47. data/lib/my_john_deere_api/request/individual/base.rb +52 -0
  48. data/lib/my_john_deere_api/request/individual/contribution_definition.rb +19 -0
  49. data/lib/my_john_deere_api/request/individual/contribution_product.rb +19 -0
  50. data/lib/my_john_deere_api/request/individual/field.rb +19 -0
  51. data/lib/my_john_deere_api/request/individual/organization.rb +19 -0
  52. data/lib/my_john_deere_api/request/update.rb +4 -0
  53. data/lib/my_john_deere_api/request/update/asset.rb +40 -0
  54. data/lib/my_john_deere_api/request/update/base.rb +67 -0
  55. data/lib/my_john_deere_api/validators.rb +5 -0
  56. data/lib/my_john_deere_api/validators/asset.rb +35 -0
  57. data/lib/my_john_deere_api/validators/asset_location.rb +34 -0
  58. data/lib/my_john_deere_api/validators/base.rb +67 -0
  59. data/lib/my_john_deere_api/version.rb +3 -0
  60. data/test/lib/my_john_deere_api/authorize_test.rb +81 -0
  61. data/test/lib/my_john_deere_api/client_test.rb +225 -0
  62. data/test/lib/my_john_deere_api/consumer_test.rb +58 -0
  63. data/test/lib/my_john_deere_api/errors/invalid_record_error_test.rb +26 -0
  64. data/test/lib/my_john_deere_api/errors/max_retries_exceeded_error_test.rb +13 -0
  65. data/test/lib/my_john_deere_api/errors/missing_contribution_definition_id_error_test.rb +14 -0
  66. data/test/lib/my_john_deere_api/errors/not_yet_implemented_error_test.rb +13 -0
  67. data/test/lib/my_john_deere_api/errors/type_mismatch_error_test.rb +13 -0
  68. data/test/lib/my_john_deere_api/errors/unsupported_environment_error_test.rb +18 -0
  69. data/test/lib/my_john_deere_api/errors_test.rb +25 -0
  70. data/test/lib/my_john_deere_api/helpers/case_conversion_test.rb +100 -0
  71. data/test/lib/my_john_deere_api/helpers/environment_helper_test.rb +67 -0
  72. data/test/lib/my_john_deere_api/helpers/uri_helpers_test.rb +58 -0
  73. data/test/lib/my_john_deere_api/helpers/validate_contribution_definition_test.rb +74 -0
  74. data/test/lib/my_john_deere_api/helpers_test.rb +21 -0
  75. data/test/lib/my_john_deere_api/model/asset_location_test.rb +38 -0
  76. data/test/lib/my_john_deere_api/model/asset_test.rb +133 -0
  77. data/test/lib/my_john_deere_api/model/base_test.rb +76 -0
  78. data/test/lib/my_john_deere_api/model/contribution_definition_test.rb +52 -0
  79. data/test/lib/my_john_deere_api/model/contribution_product_test.rb +82 -0
  80. data/test/lib/my_john_deere_api/model/field_test.rb +65 -0
  81. data/test/lib/my_john_deere_api/model/flag_test.rb +48 -0
  82. data/test/lib/my_john_deere_api/model/organization_test.rb +84 -0
  83. data/test/lib/my_john_deere_api/model_test.rb +37 -0
  84. data/test/lib/my_john_deere_api/net_http_retry/decorator_test.rb +163 -0
  85. data/test/lib/my_john_deere_api/request/collection/asset_locations_test.rb +102 -0
  86. data/test/lib/my_john_deere_api/request/collection/assets_test.rb +99 -0
  87. data/test/lib/my_john_deere_api/request/collection/base_test.rb +27 -0
  88. data/test/lib/my_john_deere_api/request/collection/contribution_definitions_test.rb +88 -0
  89. data/test/lib/my_john_deere_api/request/collection/contribution_products_test.rb +88 -0
  90. data/test/lib/my_john_deere_api/request/collection/fields_test.rb +86 -0
  91. data/test/lib/my_john_deere_api/request/collection/flags_test.rb +92 -0
  92. data/test/lib/my_john_deere_api/request/collection/organizations_test.rb +87 -0
  93. data/test/lib/my_john_deere_api/request/collection_test.rb +37 -0
  94. data/test/lib/my_john_deere_api/request/create/asset_location_test.rb +163 -0
  95. data/test/lib/my_john_deere_api/request/create/asset_test.rb +182 -0
  96. data/test/lib/my_john_deere_api/request/create/base_test.rb +29 -0
  97. data/test/lib/my_john_deere_api/request/create_test.rb +17 -0
  98. data/test/lib/my_john_deere_api/request/individual/asset_test.rb +33 -0
  99. data/test/lib/my_john_deere_api/request/individual/base_test.rb +18 -0
  100. data/test/lib/my_john_deere_api/request/individual/contribution_definition_test.rb +33 -0
  101. data/test/lib/my_john_deere_api/request/individual/contribution_product_test.rb +33 -0
  102. data/test/lib/my_john_deere_api/request/individual/field_test.rb +37 -0
  103. data/test/lib/my_john_deere_api/request/individual/organization_test.rb +33 -0
  104. data/test/lib/my_john_deere_api/request/individual_test.rb +29 -0
  105. data/test/lib/my_john_deere_api/request/update/asset_test.rb +99 -0
  106. data/test/lib/my_john_deere_api/request/update/base_test.rb +60 -0
  107. data/test/lib/my_john_deere_api/request/update_test.rb +13 -0
  108. data/test/lib/my_john_deere_api/request_test.rb +21 -0
  109. data/test/lib/my_john_deere_api/validators/asset_location_test.rb +61 -0
  110. data/test/lib/my_john_deere_api/validators/asset_test.rb +93 -0
  111. data/test/lib/my_john_deere_api/validators/base_test.rb +92 -0
  112. data/test/lib/my_john_deere_api/validators_test.rb +17 -0
  113. data/test/lib/my_john_deere_api/version_test.rb +9 -0
  114. data/test/my_john_deere_api_test.rb +37 -0
  115. data/test/support/helper.rb +97 -0
  116. data/test/support/vcr/accessor/delete_failed.yml +327 -0
  117. data/test/support/vcr/accessor/delete_max_failed.yml +615 -0
  118. data/test/support/vcr/accessor/delete_retry.yml +191 -0
  119. data/test/support/vcr/accessor/delete_retry_too_soon.yml +191 -0
  120. data/test/support/vcr/accessor/get_failed.yml +390 -0
  121. data/test/support/vcr/accessor/get_max_failed.yml +734 -0
  122. data/test/support/vcr/accessor/get_retry.yml +226 -0
  123. data/test/support/vcr/accessor/get_retry_too_soon.yml +226 -0
  124. data/test/support/vcr/accessor/post_failed.yml +417 -0
  125. data/test/support/vcr/accessor/post_max_failed.yml +785 -0
  126. data/test/support/vcr/accessor/post_retry.yml +241 -0
  127. data/test/support/vcr/accessor/post_retry_too_soon.yml +241 -0
  128. data/test/support/vcr/accessor/put_failed.yml +372 -0
  129. data/test/support/vcr/accessor/put_max_failed.yml +700 -0
  130. data/test/support/vcr/accessor/put_retry.yml +216 -0
  131. data/test/support/vcr/accessor/put_retry_too_soon.yml +216 -0
  132. data/test/support/vcr/catalog.yml +89 -0
  133. data/test/support/vcr/delete_asset.yml +82 -0
  134. data/test/support/vcr/get_access_token.yml +41 -0
  135. data/test/support/vcr/get_asset.yml +144 -0
  136. data/test/support/vcr/get_asset_locations.yml +196 -0
  137. data/test/support/vcr/get_assets.yml +152 -0
  138. data/test/support/vcr/get_contribution_definition.yml +90 -0
  139. data/test/support/vcr/get_contribution_definitions.yml +91 -0
  140. data/test/support/vcr/get_contribution_product.yml +91 -0
  141. data/test/support/vcr/get_contribution_products.yml +91 -0
  142. data/test/support/vcr/get_field.yml +144 -0
  143. data/test/support/vcr/get_fields.yml +146 -0
  144. data/test/support/vcr/get_flags.yml +47 -0
  145. data/test/support/vcr/get_organization.yml +90 -0
  146. data/test/support/vcr/get_organizations.yml +149 -0
  147. data/test/support/vcr/get_request_token.yml +83 -0
  148. data/test/support/vcr/post_asset_locations.yml +244 -0
  149. data/test/support/vcr/post_assets.yml +192 -0
  150. data/test/support/vcr/put_asset.yml +87 -0
  151. data/test/support/vcr/warning.txt +28 -0
  152. data/test/support/vcr_setup.rb +488 -0
  153. 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