huginn_bigcommerce_product_agent 1.11.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,41 +2,49 @@ module BigcommerceProductAgent
2
2
  module Mapper
3
3
  class CustomFieldMapper
4
4
 
5
- def self.map(field_map, product, bc_product)
5
+ def self.map(field_map, raw_product, bc_product, current_custom_fields, namespace)
6
6
  fields = {
7
7
  upsert: [],
8
8
  delete: [],
9
9
  }
10
10
 
11
- existing_fields = {}
11
+ to_delete = {}
12
12
 
13
- if bc_product
14
- bc_product['custom_fields'].each do |cf|
13
+ if bc_product && current_custom_fields
14
+ current_custom_fields.each do |cf|
15
15
  cf['product_id'] = bc_product['id']
16
- existing_fields[cf['name'].to_s] = cf
16
+
17
+ # Initially assume that a field should be deleted.
18
+ to_delete[cf['name'].to_s] = cf
17
19
  end
18
20
  end
19
21
 
20
22
  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?
23
+ field_map['additionalProperty'].each do |from_key, to_key|
24
+ field = self.from_additional_property(raw_product, to_delete, from_key, to_key)
25
+ if field
26
+ field['product_id'] = bc_product['id'] unless bc_product.nil?
27
+ fields[:upsert].push(field)
28
+ end
24
29
  end
25
30
  end
26
31
 
27
32
  if field_map
28
- field_map.each do |key, val|
29
- if key == 'additionalProperty'
33
+ field_map.each do |from_key, to_key|
34
+ if from_key == 'additionalProperty'
30
35
  next
31
36
  end
32
37
 
33
- field = self.from_property(product, existing_fields, key, val)
34
- fields[:upsert].push(field) unless field.nil?
38
+ field = self.from_property(raw_product, to_delete, from_key, to_key)
39
+ if field
40
+ field['product_id'] = bc_product['id'] unless bc_product.nil?
41
+ fields[:upsert].push(field)
42
+ end
35
43
  end
36
44
  end
37
45
 
38
46
  # return values that need deleted
39
- fields[:delete] = existing_fields.values
47
+ fields[:delete] = to_delete.values
40
48
  return fields
41
49
  end
42
50
 
@@ -57,34 +65,34 @@ module BigcommerceProductAgent
57
65
 
58
66
  private
59
67
 
60
- def self.from_property(product, existing_fields, from_key, to_key)
61
- if !product[from_key].nil?
68
+ def self.from_property(raw_product, fields_to_delete, from_key, to_key)
69
+ if !raw_product[from_key].nil?
62
70
  field = {
63
71
  name: to_key,
64
- value: product[from_key].to_s
72
+ value: raw_product[from_key].to_s
65
73
  }
66
74
 
67
- if existing_fields[to_key]
68
- field[:id] = existing_fields[to_key]['id']
69
- existing_fields.delete(to_key)
75
+ if fields_to_delete[to_key]
76
+ field[:id] = fields_to_delete[to_key]['id']
77
+ fields_to_delete.delete(to_key)
70
78
  end
71
79
 
72
80
  return field
73
81
  end
74
82
  end
75
83
 
76
- def self.from_additional_property(product, existing_fields, from_key, to_key)
84
+ def self.from_additional_property(raw_product, fields_to_delete, from_key, to_key)
77
85
  # date published
78
- item = product['additionalProperty'].select {|p| p['propertyID'] == from_key}.first
86
+ item = raw_product['additionalProperty'].select {|p| p['propertyID'] == from_key}.first
79
87
  if !item.nil?
80
88
  field = {
81
89
  name: to_key,
82
90
  value: item['value'].to_s
83
91
  }
84
92
 
85
- if existing_fields[to_key]
86
- field[:id] = existing_fields[to_key]['id']
87
- existing_fields.delete(to_key)
93
+ if fields_to_delete[to_key]
94
+ field[:id] = fields_to_delete[to_key]['id']
95
+ fields_to_delete.delete(to_key)
88
96
  end
89
97
 
90
98
  return field
@@ -2,7 +2,7 @@ module BigcommerceProductAgent
2
2
  module Mapper
3
3
  class MetaFieldMapper
4
4
 
5
- def self.map(field_map, product, bc_product, meta_fields, namespace)
5
+ def self.map(field_map, raw_product, bc_product, current_meta_fields, namespace)
6
6
  fields = {
7
7
  upsert: [],
8
8
  delete: [],
@@ -11,8 +11,8 @@ module BigcommerceProductAgent
11
11
  existing_fields = {}
12
12
 
13
13
  if bc_product
14
- meta_fields.each do |mf|
15
-
14
+ current_meta_fields.each do |mf|
15
+ mf['resource_id'] = bc_product['id']
16
16
  unless mf['namespace'] != namespace
17
17
  # Only delete meta fields managed by this sync
18
18
  existing_fields[mf['key'].to_s] = mf
@@ -22,7 +22,7 @@ module BigcommerceProductAgent
22
22
 
23
23
  if field_map && field_map['additionalProperty']
24
24
  field_map['additionalProperty'].each do |key, val|
25
- field = self.from_additional_property(product, existing_fields, key, val, namespace, bc_product)
25
+ field = self.from_additional_property(raw_product, existing_fields, key, val, namespace, bc_product)
26
26
  fields[:upsert].push(field) unless field.nil?
27
27
  end
28
28
  end
@@ -33,7 +33,7 @@ module BigcommerceProductAgent
33
33
  next
34
34
  end
35
35
 
36
- field = self.from_property(product, existing_fields, key, val, namespace, bc_product)
36
+ field = self.from_property(raw_product, existing_fields, key, val, namespace, bc_product)
37
37
  fields[:upsert].push(field) unless field.nil?
38
38
  end
39
39
  end
@@ -45,15 +45,15 @@ module BigcommerceProductAgent
45
45
 
46
46
  private
47
47
 
48
- def self.from_property(product, existing_fields, from_key, to_key, namespace, bc_product)
48
+ def self.from_property(raw_product, existing_fields, from_key, to_key, namespace, bc_product)
49
49
 
50
- if !product[from_key].nil?
50
+ if !raw_product[from_key].nil?
51
51
  field = {
52
52
  namespace: namespace,
53
53
  permission_set: 'write',
54
54
  resource_type: 'product',
55
55
  key: to_key,
56
- value: product[from_key]
56
+ value: raw_product[from_key]
57
57
  }
58
58
 
59
59
  if bc_product
@@ -69,9 +69,9 @@ module BigcommerceProductAgent
69
69
  end
70
70
  end
71
71
 
72
- def self.from_additional_property(product, existing_fields, from_key, to_key, namespace, bc_product)
72
+ def self.from_additional_property(raw_product, existing_fields, from_key, to_key, namespace, bc_product)
73
73
 
74
- item = product['additionalProperty'].select {|p| p['propertyID'] == from_key}.first
74
+ item = raw_product['additionalProperty'].select {|p| p['propertyID'] == from_key}.first
75
75
  if !item.nil?
76
76
 
77
77
  field = {
@@ -2,132 +2,42 @@ module BigcommerceProductAgent
2
2
  module Mapper
3
3
  class ProductMapper
4
4
 
5
- def self.map(product, variant, additional_data = {}, is_digital = false, default_sku='')
5
+ def self.map_payload(product, additional_data = {}, track_inventory = true, default_sku = '')
6
6
  name = product['name']
7
+ isDigital = product['isDigital'].to_s == 'true'
7
8
 
8
- if variant
9
- # variants inherit from and override parent product info
10
- name = "#{name} (#{self.get_option(variant)})"
11
- product = product.merge(variant)
12
- else
13
- # wrapper product
14
- default_variant = self.get_variant_by_sku(product['sku'], product)
15
- if default_variant
16
- # pull up some properties from default variant (since bc doesn't display them otherwise)
17
- product = {
18
- "weight" => default_variant['weight'],
19
- "width" => default_variant['width'],
20
- "depth" => default_variant['depth'],
21
- "height" => default_variant['height'],
22
- "releaseDate" => default_variant['releaseDate'],
23
- "datePublished" => default_variant['datePublished'],
24
- "availability" => self.get_availability_offer(product, default_variant),
25
- }.merge(product)
26
- end
27
- end
9
+ track_inventory = self.get_availability(product) == 'preorder' ? false : track_inventory
28
10
 
29
11
  result = {
30
- name: name,
31
- sku: product ? product['sku'] : default_sku,
32
- is_default: product['isDefault'],
33
- type: product['isDigital'] == true || is_digital ? 'digital' : 'physical',
34
- description: product['description'] || '',
35
- price: product['offers'] && product['offers'][0] ? product['offers'][0]['price'] : '0',
36
- categories: self.get_categories(product),
37
- availability: self.get_availability(product),
38
- weight: product['weight'] ? product['weight']['value'] : '0',
39
- width: product['width'] ? product['width']['value'] : '0',
40
- depth: product['depth'] ? product['depth']['value'] : '0',
41
- height: product['height'] ? product['height']['value'] : '0',
42
- meta_keywords: self.meta_keywords(product),
43
- meta_description: self.meta_description(product) || '',
44
- search_keywords: self.get_search_keywords(additional_data.delete(:additional_search_terms), product),
45
- is_visible: variant ? false : true,
46
- preorder_release_date: product['releaseDate'] && product['releaseDate'].to_datetime ? product['releaseDate'].to_datetime.strftime("%FT%T%:z") : nil,
47
- preorder_message: self.get_availability(product) == 'preorder' ? product['availability'] : '',
48
- is_preorder_only: self.get_availability(product) == 'preorder' ? true : false,
49
- page_title: product['page_title'] || '',
12
+ availability: self.get_availability(product),
13
+ categories: self.get_categories(product),
14
+ depth: product['depth'] ? product['depth']['value'] : '0',
15
+ description: product['description'] || '',
16
+ height: product['height'] ? product['height']['value'] : '0',
17
+ is_default: product['isDefault'],
18
+ is_preorder_only: self.get_availability(product) == 'preorder' ? true : false,
19
+ is_visible: true,
20
+ meta_description: self.meta_description(product) || '',
21
+ meta_keywords: self.meta_keywords(product),
22
+ name: name,
23
+ page_title: product['page_title'] || '',
24
+ preorder_message: self.get_availability(product) == 'preorder' ? product['offers'][0]['availability'] : '',
25
+ preorder_release_date: product['releaseDate'] && product['releaseDate'].to_datetime ? product['releaseDate'].to_datetime.strftime("%FT%T%:z") : nil,
26
+ price: product['offers'] && product['offers'][0] ? product['offers'][0]['price'] : '0',
27
+ retail_price: product['offers'] && product['offers'][0] ? product['offers'][0]['price'] : '0',
28
+ search_keywords: self.get_search_keywords(additional_data.delete(:additional_search_terms), product),
29
+ sku: product ? product['sku'] : default_sku,
30
+ type: isDigital ? 'digital' : 'physical',
31
+ weight: product['weight'] ? product['weight']['value'] : '0',
32
+ width: product['width'] ? product['width']['value'] : '0',
33
+ inventory_tracking: isDigital || !track_inventory ? 'none' : 'product',
50
34
  }
51
- result[:upc] = product['gtin12'] if product['gtin12']
35
+ result[:upc] = product['isbn'] ? product['isbn'] : product['gtin12']
36
+ result[:gtin] = product['gtin12'] if product['gtin12']
52
37
 
53
38
  result.merge(additional_data)
54
39
  end
55
40
 
56
- def self.get_wrapper_sku(product)
57
- if product
58
- "#{product['sku']}-W"
59
- end
60
- end
61
-
62
- def self.payload(sku, product, product_id = nil, additional_data = {}, is_digital = false)
63
- variant = self.get_variant_by_sku(sku, product)
64
- payload = self.map(product, variant, additional_data, is_digital, sku)
65
- payload[:id] = product_id unless product_id.nil?
66
- payload[:sku] = self.get_wrapper_sku(product)
67
-
68
- return payload
69
- end
70
-
71
- def self.get_product_skus(product)
72
- product['model'].map { |model| model['sku'] }
73
- end
74
-
75
- def self.get_sku_option_label_map(product)
76
- map = {}
77
-
78
- product['model'].each do |model|
79
- map[model['sku']] = self.get_option(model)
80
- end
81
-
82
- return map
83
- end
84
-
85
- def self.get_is_default(product)
86
- map = {}
87
-
88
- product['model'].each do |model|
89
- map[model['sku']] = model["isDefault"]
90
- end
91
-
92
- return map
93
- end
94
-
95
- def self.has_digital_variants?(product)
96
- product['model'].any? {|m| m['isDigital'] == true}
97
- end
98
-
99
- def self.has_physical_variants?(product)
100
- product['model'].any? {|m| m['isDigital'] != true}
101
- end
102
-
103
- def self.split_digital_and_physical(product, field_map)
104
- result = {}
105
-
106
- digitals = product['model'].select {|m| m['isDigital'] == true}
107
-
108
- if digitals.length > 0
109
- clone = Marshal.load(Marshal.dump(product))
110
- clone['model'] = digitals
111
- clone['sku'] = clone['model'].select {|m| m['isDefault'] = true}.first['sku']
112
- clone['categories'] = self.get_categories_after_split(clone, clone['categories'])
113
- self.merge_additional_properties(clone, field_map)
114
- result[:digital] = clone
115
- end
116
-
117
- physicals = product['model'].select {|m| m['isDigital'] != true}
118
-
119
- if physicals.length > 0
120
- clone = Marshal.load(Marshal.dump(product))
121
- clone['model'] = physicals
122
- clone['sku'] = clone['model'].select {|m| m['isDefault'] = true}.first['sku']
123
- clone['categories'] = self.get_categories_after_split(clone, clone['categories'])
124
- self.merge_additional_properties(clone, field_map)
125
- result[:physical] = clone
126
- end
127
-
128
- return result
129
- end
130
-
131
41
  private
132
42
 
133
43
  def self.get_categories(product)
@@ -142,31 +52,6 @@ module BigcommerceProductAgent
142
52
  return categories
143
53
  end
144
54
 
145
- def self.get_categories_after_split(product, categories)
146
- # categories equal shared categories between digital and physical products
147
-
148
- if product['model'].select { |c| c['categories']}
149
- product['model'].each do |variant|
150
- variant['categories'].map do |category|
151
- categories.push(category)
152
- end
153
- end
154
- end
155
-
156
- return categories
157
- end
158
-
159
- def self.get_option(variant)
160
- if variant['encodingFormat']
161
- return variant['encodingFormat']
162
- elsif variant['bookFormat']
163
- parts = variant['bookFormat'].split('/')
164
- return parts[parts.length - 1]
165
- else
166
- return self.get_additional_property_value(variant, 'option')
167
- end
168
- end
169
-
170
55
  def self.meta_description(product)
171
56
  return self.get_additional_property_value(product, 'meta_description', '')
172
57
  end
@@ -174,7 +59,7 @@ module BigcommerceProductAgent
174
59
  def self.meta_keywords(product)
175
60
  meta_keywords = []
176
61
 
177
- product['keywords'].split(',') unless product['keywords'].nil?
62
+ meta_keywords = product['keywords'].split(',') unless product['keywords'].nil?
178
63
 
179
64
  return meta_keywords
180
65
  end
@@ -190,52 +75,23 @@ module BigcommerceProductAgent
190
75
  return value
191
76
  end
192
77
 
193
- def self.get_variant_by_sku(sku, product)
194
- product['model'].select {|m| m['sku'] == sku}.first
195
- end
196
-
197
- def self.merge_additional_properties(clone, field_map)
198
- defaultVariant = clone['model'].select { |v| v['isDefault'] }.first
199
- if defaultVariant['isDefault'] && defaultVariant['additionalProperty']
200
- unless field_map.nil? || field_map['additionalProperty'].nil?
201
- field_map['additionalProperty'].each do |field, key|
202
- prop = defaultVariant['additionalProperty'].select { |prop| prop['propertyID'] == field[key] }.first
203
- clone['additionalProperty'].push(prop) unless prop.nil?
204
- end
205
- end
206
- end
207
- end
208
-
209
78
  # get a list of search keywords for the products
210
79
  def self.get_search_keywords(additional_search_terms, product)
211
- return (self.meta_keywords(product) + additional_search_terms.split(",")).join(",")
212
- end
80
+ search_keywords = self.meta_keywords(product)
81
+ search_keywords << product['isbn'] if product['isbn'].present?
82
+ if (additional_search_terms && !additional_search_terms.empty?)
83
+ search_keywords.concat(additional_search_terms)
84
+ end
213
85
 
214
- def self.get_availability(product)
215
- if product['datePublished'] && product['datePublished'].to_datetime && product['datePublished'].to_datetime < DateTime.now
216
- return 'available'
217
- else
218
- if product['releaseDate'] && product['releaseDate'].to_datetime
219
- if product['releaseDate'].to_datetime > DateTime.now
220
- return 'preorder'
221
- else
222
- return 'available'
223
- end
224
- else
225
- return 'disabled'
226
- end
227
- end
86
+ return search_keywords.uniq.join(",")
228
87
  end
229
88
 
230
- def self.get_availability_offer(product, variant)
231
- if product['offers'] && product['offers'][0]
232
- return product['offers'][0]['availability']
89
+ def self.get_availability(product)
90
+ if product['productAvailability'] == 'not available'
91
+ return 'disabled'
233
92
  else
234
- if variant['offers'] && variant['offers'][0]
235
- return variant['offers'][0]['availability']
236
- end
93
+ return product['productAvailability']
237
94
  end
238
- return ''
239
95
  end
240
96
  end
241
97
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: huginn_bigcommerce_product_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Spizziri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-03 00:00:00.000000000 Z
11
+ date: 2021-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -63,18 +63,12 @@ files:
63
63
  - lib/abstract_client.rb
64
64
  - lib/client/custom_field.rb
65
65
  - lib/client/meta_field.rb
66
- - lib/client/modifier.rb
67
- - lib/client/modifier_value.rb
68
66
  - lib/client/product.rb
69
- - lib/client/product_option.rb
70
- - lib/client/product_option_value.rb
71
- - lib/client/product_variant.rb
72
- - lib/client/variant.rb
73
67
  - lib/huginn_bigcommerce_product_agent.rb
68
+ - lib/huginn_bigcommerce_product_agent/big_commerce_product_error.rb
74
69
  - lib/huginn_bigcommerce_product_agent/bigcommerce_product_agent.rb
75
70
  - lib/mapper/custom_field_mapper.rb
76
71
  - lib/mapper/meta_field_mapper.rb
77
- - lib/mapper/modifier_mapper.rb
78
72
  - lib/mapper/option_mapper.rb
79
73
  - lib/mapper/option_value_mapper.rb
80
74
  - lib/mapper/product_mapper.rb