automation-api 0.3.0

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