huginn_bigcommerce_product_agent 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7c7ba2d9c033ac9bcb4e6b918e5061e4a24aefc5969df512976bcb40ba9dff99
4
+ data.tar.gz: 27ba376c8ae1c70e19e0f0412e742899885aa42946ec4ceec22a3efcac96b409
5
+ SHA512:
6
+ metadata.gz: dcd14b6b838f97586e9b7d039ef6620074d56cb76cf90623edfc4ddd2b6826e9ad4c675f5fd68c36d393f8933dc86d440372727c13171330f276a5116adb7df1
7
+ data.tar.gz: 0f73c946976ad3b633635e32c79e823a1a36e57cee63708aac667bbcb86726c6a3761549021636d45cbfaee146baa23c05f584ba38003adf5990e0fe4db78dcb
@@ -0,0 +1,80 @@
1
+
2
+ module BigcommerceProductAgent
3
+ module Client
4
+ class AbstractClient
5
+ @client = nil
6
+ @uri_base = ''
7
+
8
+ def initialize(
9
+ store_hash,
10
+ client_id,
11
+ access_token,
12
+ params = {
13
+ endpoint: 'https://api.bigcommerce.com',
14
+ api_version: 'v3',
15
+ }
16
+ )
17
+ @headers = {
18
+ 'X-Auth-Client' => client_id,
19
+ 'X-Auth-Token' => access_token,
20
+ 'Content-Type' => 'application/json',
21
+ 'Accept' => 'application/json'
22
+ }
23
+ @store_hash = store_hash
24
+ @endpoint = params[:endpoint]
25
+ @api_version = params[:api_version]
26
+ end
27
+
28
+ def self.uri_base
29
+ @uri_base
30
+ end
31
+
32
+ def uri_base
33
+ self.class.uri_base
34
+ end
35
+
36
+ def client
37
+ if !@client
38
+ @client = Faraday.new({ url: @endpoint, headers: @headers }) do |conn|
39
+ conn.use Faraday::Response::RaiseError
40
+ conn.response :logger, nil, { headers: true, bodies: true }
41
+ conn.response :json, :content_type => 'application/json'
42
+ conn.adapter Faraday.default_adapter
43
+ end
44
+ end
45
+
46
+ return @client
47
+ end
48
+
49
+ def uri(params = {})
50
+ u = "/stores/#{@store_hash}/#{@api_version}/#{uri_base}"
51
+
52
+ params.each do |key,val|
53
+ u = u.gsub(":#{key.to_s}", val.to_s)
54
+ end
55
+
56
+ # remove params that weren't provided
57
+ u = u.gsub(/(\/\:[^\/]+)/, '')
58
+
59
+ return u
60
+ end
61
+
62
+ def index(params = {})
63
+ response = client.get(uri, params)
64
+ return response.body['data']
65
+ end
66
+
67
+ def create(payload)
68
+ raise "not implemented yet."
69
+ end
70
+
71
+ def update(payload)
72
+ raise "not implemented yet."
73
+ end
74
+
75
+ def upsert(payload)
76
+ raise "not implemented yet."
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,11 @@
1
+ module BigcommerceProductAgent
2
+ module Client
3
+ class CustomField < AbstractClient
4
+ @uri_base = 'catalog/products/:product_id/custom-fields/:custom_field_id'
5
+
6
+ def delete(product_id, custom_field_id)
7
+ client.delete(uri(product_id: product_id, custom_field_id: custom_field_id))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ module BigcommerceProductAgent
2
+ module Client
3
+ class Modifier < AbstractClient
4
+ @uri_base = 'catalog/products/:product_id/modifiers/:modifier_id'
5
+
6
+ def delete(product_id, modifier_id)
7
+ client.delete(uri(product_id: product_id, modifier_id: modifier_id))
8
+ end
9
+
10
+ def upsert(product_id, modifier)
11
+ begin
12
+ if modifier[:id] || modifier['id']
13
+ modifier['id'] = modifier[:id] unless modifier[:id].nil?
14
+ return update(product_id, modifier)
15
+ else
16
+ return create(product_id, modifier)
17
+ end
18
+ rescue Faraday::Error::ClientError => e
19
+ puts e.inspect
20
+ raise e
21
+ end
22
+ end
23
+
24
+ def update(product_id, modifier)
25
+ response = client.put(uri(product_id: product_id, modifier_id: modifier['id']), modifier.to_json)
26
+ return response.body['data']
27
+ end
28
+
29
+ def create(product_id, modifier)
30
+ response = client.post(uri(product_id: product_id), modifier.to_json)
31
+ return response.body['data']
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,11 @@
1
+ module BigcommerceProductAgent
2
+ module Client
3
+ class ModifierValue < AbstractClient
4
+ @uri_base = 'catalog/products/:product_id/modifiers/:modifier_id/values/:value_id'
5
+
6
+ def delete(product_id, modifier_id, value_id)
7
+ client.delete(uri(product_id: product_id, modifier_id: modifier_id, value_id: value_id))
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ module BigcommerceProductAgent
2
+ module Client
3
+ class Product < AbstractClient
4
+ @uri_base = 'catalog/products/:product_id'
5
+
6
+ def update(id, payload)
7
+ response = client.put(uri(product_id: id), { data: payload }.to_json)
8
+ return response.body['data']
9
+ end
10
+
11
+ def create(payload)
12
+ response = client.post(uri, payload.to_json)
13
+ return response.body['data']
14
+ end
15
+
16
+ def upsert(payload)
17
+ begin
18
+ if payload['id']
19
+ return update(payload['id'], payload)
20
+ else
21
+ return create(payload)
22
+ end
23
+ rescue Faraday::Error::ClientError => e
24
+ puts e.inspect
25
+ raise e
26
+ end
27
+ end
28
+
29
+ def get_by_skus(skus, include = %w[custom_fields modifiers])
30
+ products = index({
31
+ 'sku:in': skus.join(','),
32
+ include: include.join(','),
33
+ })
34
+
35
+ map = {}
36
+
37
+ products.each do |product|
38
+ map[product['sku']] = product
39
+ end
40
+
41
+ map
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ require 'huginn_agent'
2
+ require 'json'
3
+
4
+ # load clients
5
+ HuginnAgent.load 'abstract_client'
6
+ Dir[File.join(__dir__, 'client', '*.rb')].each do |file|
7
+ HuginnAgent.load file
8
+ end
9
+
10
+ # load mappers
11
+ Dir[File.join(__dir__, 'mapper', '*.rb')].each do |file|
12
+ HuginnAgent.load file
13
+ end
14
+
15
+ HuginnAgent.register 'huginn_bigcommerce_product_agent/bigcommerce_product_agent'
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Agents
6
+ class BigcommerceProductAgent < Agent
7
+
8
+ include WebRequestConcern
9
+
10
+ can_dry_run!
11
+ default_schedule 'never'
12
+
13
+ description <<-MD
14
+ Takes a generic product interface && upserts that product in BigCommerce.
15
+ MD
16
+
17
+ def default_options
18
+ {
19
+ 'store_hash' => '',
20
+ 'client_id' => '',
21
+ 'access_token' => '',
22
+ 'custom_fields_map' => {},
23
+ }
24
+ end
25
+
26
+ def validate_options
27
+ unless options['store_hash'].present?
28
+ errors.add(:base, 'store_hash is a required field')
29
+ end
30
+
31
+ unless options['client_id'].present?
32
+ errors.add(:base, 'client_id is a required field')
33
+ end
34
+
35
+ unless options['access_token'].present?
36
+ errors.add(:base, 'access_token is a required field')
37
+ end
38
+
39
+ unless options['custom_fields_map'].is_a?(Hash)
40
+ errors.add(:base, "if provided, custom_fields_map must be a hash")
41
+ end
42
+ end
43
+
44
+ def working?
45
+ received_event_without_error?
46
+ end
47
+
48
+ def check
49
+ initialize_clients()
50
+ handle interpolated['payload'].presence || {}
51
+ end
52
+
53
+ def receive(incoming_events)
54
+ initialize_clients()
55
+ incoming_events.each do |event|
56
+ handle(event)
57
+ end
58
+ end
59
+
60
+ def handle(event)
61
+ product = event.payload
62
+
63
+ skus = ::BigcommerceProductAgent::Mapper::ProductMapper.get_product_skus(product)
64
+ wrapper_sku = ::BigcommerceProductAgent::Mapper::ProductMapper.get_wrapper_sku(product)
65
+ all_skus = [].push(*skus).push(wrapper_sku)
66
+ bc_products = @product.get_by_skus(all_skus)
67
+
68
+ # upsert child products
69
+ bc_children = []
70
+ custom_fields_delete = []
71
+ skus.each do |sku|
72
+ bc_product = bc_products[sku]
73
+ result = upsert(sku, product, bc_product)
74
+ custom_fields_delete += result[:custom_fields_delete]
75
+ bc_children.push(result[:product])
76
+ end
77
+
78
+ # upsert wrapper
79
+ bc_wrapper_product = bc_products[wrapper_sku]
80
+ result = upsert(wrapper_sku, product, bc_wrapper_product)
81
+ custom_fields_delete += result[:custom_fields_delete]
82
+
83
+ # update modifier
84
+ sku_option_map = ::BigcommerceProductAgent::Mapper::ProductMapper.get_sku_option_label_map(product)
85
+ modifier_updates = ::BigcommerceProductAgent::Mapper::ModifierMapper.map(
86
+ bc_wrapper_product,
87
+ bc_children,
88
+ sku_option_map
89
+ )
90
+ @modifier.upsert(result[:product]['id'], modifier_updates[:upsert])
91
+
92
+ clean_up_custom_fields(custom_fields_delete)
93
+ clean_up_modifier_values(modifier_updates[:delete])
94
+
95
+ product['modifiers'] = modifier_updates[:upsert]
96
+ create_event payload: {
97
+ product: product,
98
+ parent: result[:product],
99
+ children: bc_children,
100
+ }
101
+ end
102
+
103
+ private
104
+
105
+ def initialize_clients
106
+ @product = ::BigcommerceProductAgent::Client::Product.new(
107
+ interpolated['store_hash'],
108
+ interpolated['client_id'],
109
+ interpolated['access_token']
110
+ )
111
+
112
+ @custom_field = ::BigcommerceProductAgent::Client::CustomField.new(
113
+ interpolated['store_hash'],
114
+ interpolated['client_id'],
115
+ interpolated['access_token']
116
+ )
117
+
118
+ @modifier = ::BigcommerceProductAgent::Client::Modifier.new(
119
+ interpolated['store_hash'],
120
+ interpolated['client_id'],
121
+ interpolated['access_token']
122
+ )
123
+
124
+ @modifier_value = ::BigcommerceProductAgent::Client::ModifierValue.new(
125
+ interpolated['store_hash'],
126
+ interpolated['client_id'],
127
+ interpolated['access_token']
128
+ )
129
+ end
130
+
131
+ def upsert(sku, product, bc_product = nil)
132
+ custom_fields_updates = ::BigcommerceProductAgent::Mapper::CustomFieldMapper.map(
133
+ interpolated['custom_fields_map'],
134
+ product,
135
+ bc_product
136
+ )
137
+
138
+ product_id = bc_product['id'] unless bc_product.nil?
139
+ payload = ::BigcommerceProductAgent::Mapper::ProductMapper.payload(
140
+ sku,
141
+ product,
142
+ product_id,
143
+ { custom_fields: custom_fields_updates[:upsert] }
144
+ )
145
+
146
+ bc_product = @product.upsert(payload)
147
+
148
+ return {
149
+ product: bc_product,
150
+ custom_fields_delete: custom_fields_updates[:delete],
151
+ }
152
+ end
153
+
154
+ def clean_up_custom_fields(custom_fields)
155
+ custom_fields.each do |field|
156
+ @custom_field.delete(field['product_id'], field['id'])
157
+ end
158
+ end
159
+
160
+ def clean_up_modifier_values(modifier_values)
161
+ modifier_values.each do |field|
162
+ @modifier_value.delete(field[:product_id], field[:modifier_id], field[:value_id])
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,81 @@
1
+ module BigcommerceProductAgent
2
+ module Mapper
3
+ class CustomFieldMapper
4
+
5
+ def self.map(field_map, product, bc_product)
6
+ fields = {
7
+ upsert: [],
8
+ delete: [],
9
+ }
10
+
11
+ existing_fields = {}
12
+
13
+ if bc_product
14
+ bc_product['custom_fields'].each do |cf|
15
+ cf['product_id'] = bc_product['id']
16
+ existing_fields[cf['name'].to_s] = cf
17
+ end
18
+ end
19
+
20
+ if field_map && field_map['additionalProperty']
21
+ field_map['additionalProperty'].each do |key, val|
22
+ field = self.from_additional_property(product, existing_fields, key, val)
23
+ fields[:upsert].push(field) unless field.nil?
24
+ end
25
+ end
26
+
27
+ if field_map
28
+ field_map.each do |key, val|
29
+ if key == 'additionalProperty'
30
+ next
31
+ end
32
+
33
+ field = self.from_property(product, existing_fields, key, val)
34
+ fields[:upsert].push(field) unless field.nil?
35
+ end
36
+ end
37
+
38
+ # return values that need deleted
39
+ fields[:delete] = existing_fields.values
40
+ return fields
41
+ end
42
+
43
+ private
44
+
45
+ def self.from_property(product, existing_fields, from_key, to_key)
46
+ if !product[from_key].nil?
47
+ field = {
48
+ name: to_key,
49
+ value: product[from_key]
50
+ }
51
+
52
+ if existing_fields[to_key]
53
+ field[:id] = existing_fields[to_key]['id']
54
+ existing_fields.delete(to_key)
55
+ end
56
+
57
+ return field
58
+ end
59
+ end
60
+
61
+ def self.from_additional_property(product, existing_fields, from_key, to_key)
62
+ # date published
63
+ item = product['additionalProperty'].select {|p| p['propertyID'] == from_key}.first
64
+ if !item.nil?
65
+ field = {
66
+ name: to_key,
67
+ value: item['value']
68
+ }
69
+
70
+ if existing_fields[to_key]
71
+ field[:id] = existing_fields[to_key]['id']
72
+ existing_fields.delete(to_key)
73
+ end
74
+
75
+ return field
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,83 @@
1
+ module BigcommerceProductAgent
2
+ module Mapper
3
+ class ModifierMapper
4
+
5
+ def self.map(bc_product, bc_children, sku_option_map)
6
+ modifier = {
7
+ display_name: 'Option',
8
+ type: 'product_list',
9
+ required: true,
10
+ sort_order: 1,
11
+ config: {
12
+ product_list_adjusts_inventory: true,
13
+ product_list_adjusts_pricing: true,
14
+ product_list_shipping_calc: 'none'
15
+ },
16
+ option_values: []
17
+ }
18
+
19
+ existing_modifier = nil
20
+ existing_option_ids = []
21
+ if bc_product && !bc_product['modifiers'].nil?
22
+ existing_modifier = bc_product['modifiers'].select {|m| m['display_name'] == modifier[:display_name]}.first
23
+ modifier[:product_id] = bc_product['id']
24
+
25
+ if !existing_modifier.nil?
26
+ modifier[:id] = existing_modifier['id']
27
+ existing_option_ids = existing_modifier['option_values'].map {|value| value['id']}
28
+ end
29
+ end
30
+
31
+ bc_children.each do |child|
32
+ existing_option = nil
33
+ if existing_modifier
34
+ existing_option = existing_modifier['option_values'].select do |val|
35
+ val['value_data'] && val['value_data']['product_id'] == child['id']
36
+ end.first
37
+ end
38
+
39
+ option = {
40
+ label: sku_option_map[child['sku']],
41
+ sort_order: 0,
42
+ value_data: {
43
+ product_id: child['id']
44
+ },
45
+ is_default: false,
46
+ adjusters: {
47
+ price: nil,
48
+ weight: nil,
49
+ image_url: '',
50
+ purchasing_disabled: {
51
+ status: false,
52
+ message: ''
53
+ }
54
+ }
55
+ }
56
+
57
+ if existing_option
58
+ option[:id] = existing_option['id']
59
+ option[:option_id] = existing_option['option_id']
60
+ existing_option_ids.delete(existing_option['id'])
61
+ end
62
+
63
+ modifier[:option_values].push(option)
64
+ end
65
+
66
+ # any left over option value should be removed
67
+ modifier_values_delete = existing_option_ids.map do |id|
68
+ {
69
+ product_id: bc_product['id'],
70
+ modifier_id: existing_modifier['id'],
71
+ value_id: id
72
+ }
73
+ end
74
+
75
+ return {
76
+ upsert: modifier,
77
+ delete: modifier_values_delete
78
+ }
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,114 @@
1
+ module BigcommerceProductAgent
2
+ module Mapper
3
+ class ProductMapper
4
+
5
+ def self.map(product, variant, additional_data = {})
6
+ product = {
7
+ name: variant.nil? ? product['name'] : "#{product['name']} (#{self.get_option(variant)})",
8
+ sku: variant ? variant['sku'] : self.get_wrapper_sku(product),
9
+ type: variant && variant['isDigital'] == true ? 'digital' : 'physical',
10
+ description: product['description'],
11
+ price: variant && variant['offers'] && variant['offers'][0] ? variant['offers'][0]['price'] : '0',
12
+ categories: self.get_categories(product),
13
+ available: 'available',
14
+ weight: variant && variant['weight'] ? variant['weight']['value'] : '0',
15
+ width: variant && variant['width'] ? variant['width']['value'] : '0',
16
+ depth: variant && variant['depth'] ? variant['depth']['value'] : '0',
17
+ height: variant && variant['height'] ? variant['height']['value'] : '0',
18
+ meta_keywords: self.meta_keywords(product),
19
+ meta_description: self.meta_description(product),
20
+ search_keywords: self.meta_keywords(product).join(','),
21
+ is_visible: variant.nil? ? true : false,
22
+ }.merge(additional_data)
23
+
24
+
25
+ upc = variant ? variant['gtin12'] : product['gtin12']
26
+
27
+ if upc
28
+ product[:upc] = upc
29
+ end
30
+
31
+ return product
32
+ end
33
+
34
+ def self.get_wrapper_sku(product)
35
+ "#{product['sku']}-W"
36
+ end
37
+
38
+ def self.payload(sku, product, product_id = nil, additional_data = {})
39
+ variant = self.get_variant_by_sku(sku, product)
40
+ payload = self.map(product, variant, additional_data)
41
+ payload['id'] = product_id unless product_id.nil?
42
+
43
+ return payload
44
+ end
45
+
46
+ def self.get_product_skus(product)
47
+ product['model'].map { |model| model['sku'] }
48
+ end
49
+
50
+ def self.get_sku_option_label_map(product)
51
+ map = {}
52
+
53
+ product['model'].each do |model|
54
+ map[model['sku']] = self.get_option(model)
55
+ end
56
+
57
+ return map
58
+ end
59
+
60
+ private
61
+
62
+ def self.get_categories(product)
63
+ categories = []
64
+
65
+ if product['categories']
66
+ categories = product['categories'].map do |category|
67
+ category['identifier'].to_i
68
+ end
69
+ end
70
+
71
+ return categories
72
+ end
73
+
74
+ def self.get_option(variant)
75
+ if variant['encodingFormat']
76
+ return variant['encodingFormat']
77
+ elsif variant['bookFormat']
78
+ parts = variant['bookFormat'].split('/')
79
+ return parts[parts.length - 1]
80
+ else
81
+ return self.get_additional_property_value(variant, 'option')
82
+ end
83
+ end
84
+
85
+ def self.meta_description(product)
86
+ return self.get_additional_property_value(product, 'meta_description', '')
87
+ end
88
+
89
+ def self.meta_keywords(product)
90
+ meta_keywords = []
91
+
92
+ product['keywords'].split(',') unless product['keywords'].nil?
93
+
94
+ return meta_keywords
95
+ end
96
+
97
+ def self.get_additional_property_value(product, name, default = nil)
98
+ value = default
99
+
100
+ return value if product['additionalProperty'].nil?
101
+
102
+ idx = product['additionalProperty'].index {|item| item['propertyID'] == name}
103
+ value = product['additionalProperty'][idx]['value'] unless idx.nil?
104
+
105
+ return value
106
+ end
107
+
108
+ def self.get_variant_by_sku(sku, product)
109
+ product['model'].select {|m| m['sku'] == sku}.first
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,13 @@
1
+ # require 'rails_helper'
2
+ # require 'huginn_agent/spec_helper'
3
+ #
4
+ # describe Agents::BigcommerceProductAgent do
5
+ # before(:each) do
6
+ # @valid_options = Agents::BigcommerceProductAgent.new.default_options
7
+ # @checker = Agents::BigcommerceProductAgent.new(:name => "BigcommerceProductAgent", :options => @valid_options)
8
+ # @checker.user = users(:bob)
9
+ # @checker.save!
10
+ # end
11
+ #
12
+ # pending "add specs here"
13
+ # end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: huginn_bigcommerce_product_agent
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jacob Spizziri
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: huginn_agent
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Agent that takes a generic product interface and upserts that product
56
+ in BigCommerce.
57
+ email:
58
+ - jspizziri@weare5stones.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - lib/abstract_client.rb
64
+ - lib/client/custom_field.rb
65
+ - lib/client/modifier.rb
66
+ - lib/client/modifier_value.rb
67
+ - lib/client/product.rb
68
+ - lib/huginn_bigcommerce_product_agent.rb
69
+ - lib/huginn_bigcommerce_product_agent/bigcommerce_product_agent.rb
70
+ - lib/mapper/custom_field_mapper.rb
71
+ - lib/mapper/modifier_mapper.rb
72
+ - lib/mapper/product_mapper.rb
73
+ - spec/bigcommerce_product_agent_spec.rb
74
+ homepage: https://github.com/5-stones/huginn_bigcommerce_product_agent
75
+ licenses: []
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.0.3
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Agent that takes a generic product interface and upserts that product in
96
+ BigCommerce.
97
+ test_files:
98
+ - spec/bigcommerce_product_agent_spec.rb