automation-api 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.dependabot/config.yml +8 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +3 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +7 -0
  8. data/CHANGELONG.md +13 -0
  9. data/Dockerfile +16 -0
  10. data/Gemfile +6 -0
  11. data/Gemfile.lock +149 -0
  12. data/Jenkinsfile +31 -0
  13. data/README.md +176 -0
  14. data/Rakefile +44 -0
  15. data/automation-api.gemspec +42 -0
  16. data/bin/console +17 -0
  17. data/bin/setup +8 -0
  18. data/config/basic.yml +16 -0
  19. data/config/essential.yml +21 -0
  20. data/config/loyalty.yml +7 -0
  21. data/config/online.yml +17 -0
  22. data/config/profile.yml.example +231 -0
  23. data/config/restrictions.yml +55 -0
  24. data/config/starter.yml +11 -0
  25. data/docker-compose.yml +12 -0
  26. data/exe/store-gen +6 -0
  27. data/lib/automation/api.rb +17 -0
  28. data/lib/automation/api/cli.rb +320 -0
  29. data/lib/automation/api/client.rb +133 -0
  30. data/lib/automation/api/requests.rb +26 -0
  31. data/lib/automation/api/requests/account_owner.rb +22 -0
  32. data/lib/automation/api/requests/app_sync.rb +93 -0
  33. data/lib/automation/api/requests/button_pages.rb +34 -0
  34. data/lib/automation/api/requests/buttons.rb +34 -0
  35. data/lib/automation/api/requests/categories.rb +34 -0
  36. data/lib/automation/api/requests/collection.rb +32 -0
  37. data/lib/automation/api/requests/customers.rb +34 -0
  38. data/lib/automation/api/requests/departments.rb +34 -0
  39. data/lib/automation/api/requests/employees.rb +34 -0
  40. data/lib/automation/api/requests/entitlements.rb +33 -0
  41. data/lib/automation/api/requests/features.rb +33 -0
  42. data/lib/automation/api/requests/helper.rb +59 -0
  43. data/lib/automation/api/requests/location_preferences.rb +22 -0
  44. data/lib/automation/api/requests/loyalty_provisioning.rb +22 -0
  45. data/lib/automation/api/requests/matrix_products.rb +34 -0
  46. data/lib/automation/api/requests/modifiers.rb +34 -0
  47. data/lib/automation/api/requests/named_discounts.rb +34 -0
  48. data/lib/automation/api/requests/online_orders.rb +18 -0
  49. data/lib/automation/api/requests/registers.rb +30 -0
  50. data/lib/automation/api/requests/sales.rb +22 -0
  51. data/lib/automation/api/requests/sales_restrictions.rb +34 -0
  52. data/lib/automation/api/requests/settings.rb +22 -0
  53. data/lib/automation/api/requests/stock_items.rb +34 -0
  54. data/lib/automation/api/requests/tax_rate_service_taxes.rb +34 -0
  55. data/lib/automation/api/requests/taxes.rb +34 -0
  56. data/lib/automation/api/requests/tenders.rb +38 -0
  57. data/lib/automation/api/version.rb +7 -0
  58. metadata +321 -0
@@ -0,0 +1,11 @@
1
+ :account_owner:
2
+ :first_name: Test
3
+ :last_name: Starter
4
+ :phone: '9736928048'
5
+ # Default: Here for reference
6
+ # :entitlements:
7
+ # :activate:
8
+ # - pocket
9
+ # - freemium_care
10
+ # - freemium
11
+ # - billing_management
@@ -0,0 +1,12 @@
1
+ version: '3'
2
+
3
+ services:
4
+ test:
5
+ build: .
6
+ environment:
7
+ - CODECLIMATE_REPO_TOKEN
8
+ - COVERAGE
9
+ - GIT_COMMIT
10
+ - GIT_BRANCH
11
+ command: >
12
+ bash -c "rspec --format progress --format RspecJunitFormatter --out /usr/artifacts/rspec.xml && bundle exec codeclimate-test-reporter && bundle exec rake pact:publish"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'automation/api'
5
+
6
+ Automation::API::CLI.start
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'automation/api/cli'
4
+ require 'automation/api/client'
5
+ require 'automation/api/version'
6
+
7
+ module Automation
8
+ # Automation::API
9
+ module API
10
+ # Your code here
11
+ ROOT = File.expand_path('../../', __dir__)
12
+
13
+ def self.root
14
+ ROOT
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,320 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'thor'
5
+ require 'yaml'
6
+ require 'securerandom'
7
+
8
+ module Automation
9
+ module API
10
+ # Automation::API::CLI
11
+ class CLI < Thor
12
+ DEFAULT_PROFILE = 'starter'
13
+ PROFILE_PATH = "#{File.expand_path('../../../', __dir__)}/config"
14
+ PROFILE_EXAMPLE = "#{PROFILE_PATH}/profile.yml.example"
15
+
16
+ ARCHETYPES = %w[
17
+ complex
18
+ coffee_shop
19
+ empty
20
+ ].freeze
21
+
22
+ PROCESSORS = %w[
23
+ tgate_dev
24
+ tgate_prod
25
+ tgate_mock
26
+ clover
27
+ freedompay_uat
28
+ tgate_emulator_uat
29
+ broken
30
+ none
31
+ ].freeze
32
+
33
+ desc 'create [PROFILE]', 'Create store of using specified profile'
34
+
35
+ method_option :archetype,
36
+ enum: ARCHETYPES,
37
+ aliases: '-a',
38
+ default: 'empty',
39
+ desc: 'Store archetype - the base upon which the profile is built'
40
+
41
+ method_option :license_count,
42
+ type: :numeric,
43
+ aliases: '-l',
44
+ default: 100,
45
+ desc: 'Number of register license available'
46
+
47
+ method_option :subdomain,
48
+ aliases: '-s',
49
+ default: "test-auto-thor-#{SecureRandom.hex(4)}",
50
+ desc: 'Store subdomain - randomly generated if not given'
51
+
52
+ method_option :email,
53
+ aliases: '-e',
54
+ desc: 'Email address associated with account - randomly generated if not given'
55
+
56
+ method_option :password,
57
+ aliases: '-p',
58
+ default: 'password',
59
+ desc: 'Password for account owner'
60
+
61
+ method_option :time_zone,
62
+ aliases: '-t',
63
+ default: 'Eastern Time (US & Canada)',
64
+ desc: 'Store time zone'
65
+
66
+ method_option :sk_expert,
67
+ aliases: '-x',
68
+ desc: "I'm not entirly sure what this is"
69
+
70
+ method_option :sk_employee,
71
+ aliases: '-e',
72
+ desc: "I'm not entirly sure what this is"
73
+
74
+ method_option :processor,
75
+ enum: PROCESSORS,
76
+ aliases: '-c',
77
+ default: 'tgate_dev',
78
+ desc: 'Credit card processor'
79
+
80
+ method_option :profile_path,
81
+ aliases: '--path',
82
+ default: PROFILE_PATH,
83
+ desc: 'Specify a path to the directory containing the profile configuration'
84
+
85
+ def create(profile = DEFAULT_PROFILE)
86
+ @saved = {}
87
+ @client = Automation::API::Client.create(options)
88
+
89
+ file_path = File.expand_path("#{profile}.yml", options[:profile_path])
90
+
91
+ raise 'Store profile not found' unless File.exist?(file_path)
92
+
93
+ configuration = YAML.load_file(file_path)
94
+
95
+ configuration.each do |key, value|
96
+ next unless private_methods.include?(key)
97
+
98
+ send(key, value)
99
+ end
100
+ end
101
+
102
+ desc 'list', 'List of predifined store profiles'
103
+
104
+ method_option :profile_path,
105
+ aliases: '--path',
106
+ default: PROFILE_PATH,
107
+ desc: 'Specify a path to the directory containing the profile configuration'
108
+
109
+ def list
110
+ regex = %r{([^\/]*).yml$}x
111
+ file_paths = Dir["#{options[:profile_path]}/*"]
112
+
113
+ file_paths.map { |file| file.match(regex) }
114
+ .compact
115
+ .map { |match| match[1] }
116
+ .sort
117
+ .each { |profile| puts profile }
118
+ end
119
+
120
+ desc 'view [PROFILE]', 'View config file for requested profile'
121
+
122
+ method_option :config_path,
123
+ aliases: '--path',
124
+ default: PROFILE_PATH,
125
+ desc: 'Specify a path to the directory containing the profile configuration'
126
+
127
+ def view(store_type = DEFAULT_STORE_TYPE)
128
+ path = "#{options[:config_path]}/#{store_type}.yml"
129
+
130
+ if File.exist?(path)
131
+ puts File.read(path)
132
+ else
133
+ puts 'No configuration file found'
134
+ end
135
+ end
136
+
137
+ desc 'new_profile [NAME]', 'Start creating a new custom profile'
138
+
139
+ method_option :profile_path,
140
+ aliases: '--path',
141
+ default: Dir.pwd,
142
+ desc: 'Specify a path to the directory where profile configuration will be saved'
143
+
144
+ def new_profile(name = 'custom_profile')
145
+ dest = File.expand_path("#{name}.yml", options[:profile_path])
146
+
147
+ FileUtils.cp(PROFILE_EXAMPLE, dest)
148
+ end
149
+
150
+ private
151
+
152
+ def account_owner(config)
153
+ print 'Updating account owner information'
154
+
155
+ @client.update_account_owner(config)
156
+
157
+ puts '...done'
158
+ end
159
+
160
+ def loyalty_provisioned(provision)
161
+ print "#{provision ? 'P' : 'Dep'}rovisioning loyalty"
162
+
163
+ provision ? @client.provision : @client.deprovison
164
+
165
+ puts '...done'
166
+ end
167
+
168
+ def location_preferences(config)
169
+ print 'Updating location preferences'
170
+
171
+ @client.update_location_preferences(config)
172
+
173
+ puts '...done'
174
+ end
175
+
176
+ def entitlements(params)
177
+ print 'Updating entitlements'
178
+
179
+ @client.update_entitlements(params)
180
+
181
+ puts '...done'
182
+ end
183
+
184
+ def features(params)
185
+ print 'Updating features'
186
+
187
+ @client.update_features(params)
188
+
189
+ puts '...done'
190
+ end
191
+
192
+ def settings(config)
193
+ print 'Updating settings'
194
+
195
+ @client.update_settings(config)
196
+
197
+ puts '...done'
198
+ end
199
+
200
+ def burn_first_register(burn)
201
+ return unless burn
202
+
203
+ print 'Burning first register'
204
+
205
+ @client.create_register
206
+
207
+ puts '...done'
208
+ end
209
+
210
+ def departments(config)
211
+ @saved[:departments] = []
212
+
213
+ config.each do |dept|
214
+ print "Creating department: #{dept[:name]}"
215
+
216
+ @saved[:departments] << @client.create_department(department: dept)
217
+
218
+ puts '...done'
219
+ end
220
+ end
221
+
222
+ def categories(config)
223
+ @saved[:categories] = []
224
+
225
+ config.each do |category|
226
+ print "Creating category: #{category[:name]}"
227
+
228
+ dept = @saved[:departments].find { |d| d.name == category[:department] }
229
+
230
+ @saved[:categories] << @client.create_category(department_id: dept.id, category: category)
231
+
232
+ puts '...done'
233
+ end
234
+ end
235
+
236
+ def sales_restrictions(config)
237
+ @saved[:sales_restrictions] = []
238
+
239
+ config.each do |restriction|
240
+ print "Creating sales restriction: #{restriction[:name]}"
241
+
242
+ dept = @saved[:departments].find { |d| d.name == restriction[:department] }
243
+ ctgy = @saved[:categories].find { |c| c.name == restriction[:category] }
244
+
245
+ @saved[:sales_restrictions] << @client.create_sales_restriction(department_id: dept.id, category_id: ctgy.id, sales_restriction: restriction)
246
+
247
+ puts '...done'
248
+ end
249
+ end
250
+
251
+ def stock_items(config)
252
+ @saved[:stock_items] = []
253
+
254
+ config.each do |item|
255
+ print "Creating stock item: #{item[:description]}"
256
+
257
+ dept = @saved[:departments].find { |d| d.name == item[:department] }
258
+ ctgy = @saved[:categories].find { |c| c.name == item[:category] }
259
+
260
+ item_params = item.merge(department_id: dept.id, category_id: ctgy.id)
261
+
262
+ @saved[:stock_items] << @client.create_stock_item(stock_item: item_params)
263
+
264
+ puts '...done'
265
+ end
266
+ end
267
+
268
+ def button_pages(pages)
269
+ @saved[:button_pages] = []
270
+
271
+ pages.each do |page|
272
+ print "Creating button page: #{page[:name]}"
273
+
274
+ @saved[:button_pages] << @client.create_button_page(button_page: page)
275
+
276
+ puts '...done'
277
+ end
278
+ end
279
+
280
+ def buttons(config)
281
+ @saved[:buttons] = []
282
+
283
+ config.each do |button|
284
+ print "Creating button for: #{button[:item]}"
285
+
286
+ page = @saved[:button_pages].find { |pg| pg.name == button[:page] }
287
+ item = @saved[:stock_items].find { |i| i.description == button[:item] }
288
+
289
+ butt = @client.create_button(button_page_id: page.id,
290
+ button: button.merge(item_id: item.id))
291
+
292
+ @saved[:buttons] << butt
293
+
294
+ puts '...done'
295
+ end
296
+ end
297
+
298
+ def online_ordering(_thing)
299
+ puts 'Online Ordering requires a active register. Please activate a register before continuing'
300
+
301
+ @client.enable_online_ordering if user_confirmation
302
+ end
303
+
304
+ def user_confirmation
305
+ puts '[Enter] to continue or [S] to skip'
306
+
307
+ answer = STDIN.gets.chomp.upcase
308
+
309
+ case answer
310
+ when ''
311
+ true
312
+ when 'S'
313
+ false
314
+ else
315
+ user_confirmation
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'automation/api/requests'
4
+ require 'json'
5
+ require 'pp'
6
+ require 'rest-client'
7
+ require 'stringio'
8
+ require 'zlib'
9
+ require 'base64'
10
+
11
+ module Automation
12
+ module API
13
+ # Automation::API::Client
14
+ class Client
15
+ include Requests::AccountOwner
16
+ include Requests::AppSync
17
+ include Requests::ButtonPages
18
+ include Requests::Buttons
19
+ include Requests::Categories
20
+ include Requests::Customers
21
+ include Requests::Departments
22
+ include Requests::Employees
23
+ include Requests::Entitlements
24
+ include Requests::Features
25
+ include Requests::LocationPreferences
26
+ include Requests::LoyaltyProvisioning
27
+ include Requests::MatrixProducts
28
+ include Requests::Modifiers
29
+ include Requests::NamedDiscounts
30
+ include Requests::OnlineOrders
31
+ include Requests::Registers
32
+ include Requests::Sales
33
+ include Requests::SalesRestrictions
34
+ include Requests::Settings
35
+ include Requests::StockItems
36
+ include Requests::Taxes
37
+ include Requests::TaxRateServiceTaxes
38
+ include Requests::Tenders
39
+
40
+ LOCAL_URL = 'http://admin.127.0.0.1.xip.io:3000'
41
+ MOCK_URL = 'http://localhost:1234'
42
+ URL = 'https://api.getshopkeep.com'
43
+ USERNAME = '8ec7225ad7aead3ef09265456f8de02c'
44
+ PASSWORD = 'X'
45
+ AUTH_TOKEN = 'OGVjNzIyNWFkN2FlYWQzZWYwOTI2NTQ1NmY4ZGUwMmM6WA=='
46
+
47
+ CONTENT_HEADERS = { content_type: :json, accept: :json }.freeze
48
+
49
+ DEFAULT_ARCHETYPE = 'complex'
50
+ DEFAULT_PROCESSOR = 'tgate_dev'
51
+
52
+ DEFAULT_OPTIONS = { archetype: DEFAULT_ARCHETYPE,
53
+ processor: DEFAULT_PROCESSOR }.freeze
54
+
55
+ def self.create(options = DEFAULT_OPTIONS)
56
+ store_data = generate_store(options)
57
+
58
+ pp store_data
59
+
60
+ store_settings(subdomain: store_data['subdomain'],
61
+ processor: options['processor'])
62
+
63
+ new(uuid: store_data['uuid'])
64
+ end
65
+
66
+ def self.generate_store(options)
67
+ response = RestClient.post("#{URL}/dev/test/generate_store",
68
+ options.to_json,
69
+ content_type: :json,
70
+ accept: :json,
71
+ authorization: "Basic #{AUTH_TOKEN}")
72
+
73
+ JSON.parse(response.body)
74
+ end
75
+
76
+ def self.store_settings(settings)
77
+ response = RestClient.post("#{URL}/dev/test/store_settings",
78
+ settings.to_json,
79
+ content_type: :json,
80
+ accept: :json,
81
+ authorization: "Basic #{AUTH_TOKEN}")
82
+
83
+ JSON.parse(response.body)
84
+ end
85
+
86
+ attr_reader :uuid, :store, :login, :password
87
+
88
+ def initialize(uuid:, store: '', login: '', password: '', url: URL)
89
+ @uuid = uuid
90
+ @store = store
91
+ @login = login
92
+ @password = password
93
+ @base_uri = "#{url}/dev/test/store/#{uuid}"
94
+ @trs_uri = "#{url}/tax-rates-service/stores/#{uuid}"
95
+ end
96
+
97
+ def dev_headers
98
+ @dev_headers ||= CONTENT_HEADERS.merge(authorization: "Basic #{AUTH_TOKEN}")
99
+ end
100
+
101
+ def jwt_headers
102
+ @jwt_headers ||= CONTENT_HEADERS.merge(authorization: "Token token=#{jwt_token}")
103
+ end
104
+
105
+ private
106
+
107
+ def jwt_token
108
+ header, payload, sig = *decompress_token
109
+
110
+ encoded_header = Base64.strict_encode64(header).match(/^[^=]*/)
111
+ encoded_payload = Base64.strict_encode64(payload).match(/^[^=]*/)
112
+
113
+ "#{encoded_header}.#{encoded_payload}.#{sig}"
114
+ end
115
+
116
+ def decompress_token
117
+ raw = Base64.decode64(auth_token)
118
+ gz = Zlib::GzipReader.new(StringIO.new(raw))
119
+
120
+ JSON.parse(gz.read)
121
+ end
122
+
123
+ def auth_token
124
+ url = 'https://api.getshopkeep.com/auth'
125
+ data = { store_name: store, password: password, login: login }.to_json
126
+
127
+ RestClient.post(url, data, content_type: :json) do |response, _, _|
128
+ response.cookies.fetch('_sk_oidc_auth_token')
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end