my_john_deere_api 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
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