huginn_bigcommerce_product_agent 1.12.0 → 2.0.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.
- checksums.yaml +4 -4
- data/lib/client/custom_field.rb +21 -2
- data/lib/client/meta_field.rb +11 -8
- data/lib/client/product.rb +22 -13
- data/lib/huginn_bigcommerce_product_agent/bigcommerce_product_agent.rb +428 -411
- data/lib/mapper/custom_field_mapper.rb +32 -24
- data/lib/mapper/meta_field_mapper.rb +10 -10
- data/lib/mapper/product_mapper.rb +38 -184
- metadata +2 -9
- data/lib/client/modifier.rb +0 -35
- data/lib/client/modifier_value.rb +0 -11
- data/lib/client/product_option.rb +0 -45
- data/lib/client/product_option_value.rb +0 -72
- data/lib/client/product_variant.rb +0 -40
- data/lib/client/variant.rb +0 -40
- data/lib/mapper/modifier_mapper.rb +0 -83
@@ -2,41 +2,49 @@ module BigcommerceProductAgent
|
|
2
2
|
module Mapper
|
3
3
|
class CustomFieldMapper
|
4
4
|
|
5
|
-
def self.map(field_map,
|
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
|
-
|
11
|
+
to_delete = {}
|
12
12
|
|
13
|
-
if bc_product
|
14
|
-
|
13
|
+
if bc_product && current_custom_fields
|
14
|
+
current_custom_fields.each do |cf|
|
15
15
|
cf['product_id'] = bc_product['id']
|
16
|
-
|
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 |
|
22
|
-
field = self.from_additional_property(
|
23
|
-
|
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 |
|
29
|
-
if
|
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(
|
34
|
-
|
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] =
|
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(
|
61
|
-
if !
|
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:
|
72
|
+
value: raw_product[from_key].to_s
|
65
73
|
}
|
66
74
|
|
67
|
-
if
|
68
|
-
field[:id] =
|
69
|
-
|
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(
|
84
|
+
def self.from_additional_property(raw_product, fields_to_delete, from_key, to_key)
|
77
85
|
# date published
|
78
|
-
item =
|
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
|
86
|
-
field[:id] =
|
87
|
-
|
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,
|
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
|
-
|
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(
|
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(
|
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(
|
48
|
+
def self.from_property(raw_product, existing_fields, from_key, to_key, namespace, bc_product)
|
49
49
|
|
50
|
-
if !
|
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:
|
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(
|
72
|
+
def self.from_additional_property(raw_product, existing_fields, from_key, to_key, namespace, bc_product)
|
73
73
|
|
74
|
-
item =
|
74
|
+
item = raw_product['additionalProperty'].select {|p| p['propertyID'] == from_key}.first
|
75
75
|
if !item.nil?
|
76
76
|
|
77
77
|
field = {
|
@@ -2,134 +2,42 @@ module BigcommerceProductAgent
|
|
2
2
|
module Mapper
|
3
3
|
class ProductMapper
|
4
4
|
|
5
|
-
def self.
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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['
|
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] = sku
|
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
|
-
if model['isAvailableForPurchase']
|
80
|
-
map[model['sku']] = self.get_option(model)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
return map
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.get_is_default(product)
|
88
|
-
map = {}
|
89
|
-
|
90
|
-
product['model'].each do |model|
|
91
|
-
map[model['sku']] = model["isDefault"]
|
92
|
-
end
|
93
|
-
|
94
|
-
return map
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.has_digital_variants?(product)
|
98
|
-
product['model'].any? {|m| m['isDigital'] == true}
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.has_physical_variants?(product)
|
102
|
-
product['model'].any? {|m| m['isDigital'] != true}
|
103
|
-
end
|
104
|
-
|
105
|
-
def self.split_digital_and_physical(product, field_map)
|
106
|
-
result = {}
|
107
|
-
|
108
|
-
digitals = product['model'].select {|m| m['isDigital'] == true}
|
109
|
-
|
110
|
-
if digitals.length > 0
|
111
|
-
clone = Marshal.load(Marshal.dump(product))
|
112
|
-
clone['model'] = digitals
|
113
|
-
clone['sku'] = clone['model'].select {|m| m['isDefault'] = true}.first['sku']
|
114
|
-
clone['categories'] = self.get_categories_after_split(clone, clone['categories'])
|
115
|
-
self.merge_additional_properties(clone, field_map)
|
116
|
-
result[:digital] = clone
|
117
|
-
end
|
118
|
-
|
119
|
-
physicals = product['model'].select {|m| m['isDigital'] != true}
|
120
|
-
|
121
|
-
if physicals.length > 0
|
122
|
-
clone = Marshal.load(Marshal.dump(product))
|
123
|
-
clone['model'] = physicals
|
124
|
-
clone['sku'] = clone['model'].select {|m| m['isDefault'] = true}.first['sku']
|
125
|
-
clone['categories'] = self.get_categories_after_split(clone, clone['categories'])
|
126
|
-
self.merge_additional_properties(clone, field_map)
|
127
|
-
result[:physical] = clone
|
128
|
-
end
|
129
|
-
|
130
|
-
return result
|
131
|
-
end
|
132
|
-
|
133
41
|
private
|
134
42
|
|
135
43
|
def self.get_categories(product)
|
@@ -144,31 +52,6 @@ module BigcommerceProductAgent
|
|
144
52
|
return categories
|
145
53
|
end
|
146
54
|
|
147
|
-
def self.get_categories_after_split(product, categories)
|
148
|
-
# categories equal shared categories between digital and physical products
|
149
|
-
|
150
|
-
if product['model'].select { |c| c['categories']}
|
151
|
-
product['model'].each do |variant|
|
152
|
-
variant['categories'].map do |category|
|
153
|
-
categories.push(category)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
return categories
|
159
|
-
end
|
160
|
-
|
161
|
-
def self.get_option(variant)
|
162
|
-
if variant['encodingFormat']
|
163
|
-
return variant['encodingFormat']
|
164
|
-
elsif variant['bookFormat']
|
165
|
-
parts = variant['bookFormat'].split('/')
|
166
|
-
return parts[parts.length - 1]
|
167
|
-
else
|
168
|
-
return self.get_additional_property_value(variant, 'option')
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
55
|
def self.meta_description(product)
|
173
56
|
return self.get_additional_property_value(product, 'meta_description', '')
|
174
57
|
end
|
@@ -176,7 +59,7 @@ module BigcommerceProductAgent
|
|
176
59
|
def self.meta_keywords(product)
|
177
60
|
meta_keywords = []
|
178
61
|
|
179
|
-
product['keywords'].split(',') unless product['keywords'].nil?
|
62
|
+
meta_keywords = product['keywords'].split(',') unless product['keywords'].nil?
|
180
63
|
|
181
64
|
return meta_keywords
|
182
65
|
end
|
@@ -192,52 +75,23 @@ module BigcommerceProductAgent
|
|
192
75
|
return value
|
193
76
|
end
|
194
77
|
|
195
|
-
def self.get_variant_by_sku(sku, product)
|
196
|
-
product['model'].select {|m| m['sku'] == sku}.first
|
197
|
-
end
|
198
|
-
|
199
|
-
def self.merge_additional_properties(clone, field_map)
|
200
|
-
defaultVariant = clone['model'].select { |v| v['isDefault'] }.first
|
201
|
-
if defaultVariant['isDefault'] && defaultVariant['additionalProperty']
|
202
|
-
unless field_map.nil? || field_map['additionalProperty'].nil?
|
203
|
-
field_map['additionalProperty'].each do |field, key|
|
204
|
-
prop = defaultVariant['additionalProperty'].select { |prop| prop['propertyID'] == field[key] }.first
|
205
|
-
clone['additionalProperty'].push(prop) unless prop.nil?
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
78
|
# get a list of search keywords for the products
|
212
79
|
def self.get_search_keywords(additional_search_terms, product)
|
213
|
-
|
214
|
-
|
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
|
215
85
|
|
216
|
-
|
217
|
-
if product['datePublished'] && product['datePublished'].to_datetime && product['datePublished'].to_datetime < DateTime.now
|
218
|
-
return 'available'
|
219
|
-
else
|
220
|
-
if product['releaseDate'] && product['releaseDate'].to_datetime
|
221
|
-
if product['releaseDate'].to_datetime > DateTime.now
|
222
|
-
return 'preorder'
|
223
|
-
else
|
224
|
-
return 'available'
|
225
|
-
end
|
226
|
-
else
|
227
|
-
return 'disabled'
|
228
|
-
end
|
229
|
-
end
|
86
|
+
return search_keywords.uniq.join(",")
|
230
87
|
end
|
231
88
|
|
232
|
-
def self.
|
233
|
-
if product['
|
234
|
-
|
89
|
+
def self.get_availability(product)
|
90
|
+
if product['productAvailability'] == 'not available'
|
91
|
+
return 'disabled'
|
235
92
|
else
|
236
|
-
|
237
|
-
return variant['offers'][0]['availability']
|
238
|
-
end
|
93
|
+
return product['productAvailability']
|
239
94
|
end
|
240
|
-
return ''
|
241
95
|
end
|
242
96
|
end
|
243
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:
|
4
|
+
version: 2.0.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:
|
11
|
+
date: 2021-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -63,18 +63,11 @@ 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
|
74
68
|
- lib/huginn_bigcommerce_product_agent/bigcommerce_product_agent.rb
|
75
69
|
- lib/mapper/custom_field_mapper.rb
|
76
70
|
- lib/mapper/meta_field_mapper.rb
|
77
|
-
- lib/mapper/modifier_mapper.rb
|
78
71
|
- lib/mapper/option_mapper.rb
|
79
72
|
- lib/mapper/option_value_mapper.rb
|
80
73
|
- lib/mapper/product_mapper.rb
|