shopify_transporter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +23 -0
  3. data/LICENSE +20 -0
  4. data/README.md +290 -0
  5. data/RELEASING +16 -0
  6. data/Rakefile +11 -0
  7. data/exe/shopify_transporter +47 -0
  8. data/lib/shopify_transporter/generators/base_group.rb +25 -0
  9. data/lib/shopify_transporter/generators/generate.rb +64 -0
  10. data/lib/shopify_transporter/generators/new.rb +23 -0
  11. data/lib/shopify_transporter/generators.rb +3 -0
  12. data/lib/shopify_transporter/pipeline/all_platforms/metafields.rb +61 -0
  13. data/lib/shopify_transporter/pipeline/magento/customer/addresses_attribute.rb +60 -0
  14. data/lib/shopify_transporter/pipeline/magento/customer/top_level_attributes.rb +47 -0
  15. data/lib/shopify_transporter/pipeline/magento/order/addresses_attribute.rb +46 -0
  16. data/lib/shopify_transporter/pipeline/magento/order/line_items.rb +55 -0
  17. data/lib/shopify_transporter/pipeline/magento/order/top_level_attributes.rb +39 -0
  18. data/lib/shopify_transporter/pipeline/magento/product/top_level_attributes.rb +36 -0
  19. data/lib/shopify_transporter/pipeline/stage.rb +16 -0
  20. data/lib/shopify_transporter/pipeline.rb +4 -0
  21. data/lib/shopify_transporter/record_builder.rb +51 -0
  22. data/lib/shopify_transporter/shopify/attributes_accumulator.rb +43 -0
  23. data/lib/shopify_transporter/shopify/attributes_helpers.rb +53 -0
  24. data/lib/shopify_transporter/shopify/customer.rb +79 -0
  25. data/lib/shopify_transporter/shopify/order.rb +107 -0
  26. data/lib/shopify_transporter/shopify/product.rb +118 -0
  27. data/lib/shopify_transporter/shopify/record.rb +60 -0
  28. data/lib/shopify_transporter/shopify.rb +7 -0
  29. data/lib/shopify_transporter/version.rb +4 -0
  30. data/lib/shopify_transporter.rb +247 -0
  31. data/lib/tasks/factory_bot.rake +9 -0
  32. data/lib/templates/custom_stage.tt +31 -0
  33. data/lib/templates/gemfile.tt +5 -0
  34. data/lib/templates/magento/config.tt +23 -0
  35. data/shopify_transporter.gemspec +36 -0
  36. metadata +152 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e731ee32bf65a13db82226613fcdfe4de06e5dcd
4
+ data.tar.gz: 6f52f02f93b67cc3bb96a709ccc895126bd2dfe6
5
+ SHA512:
6
+ metadata.gz: 624777e136f3cb0d659935f6fbe5152ccba71e17fa38e0b884bad43074f5e730315916aa52250758255dd28aec3c47fabc2042567f22b5a217a40d6a5ed47c2c
7
+ data.tar.gz: 505991f408682f301e7af3be5e4805e74ed2130bbb3c8ed3d42ae85ec1f5159bf6ea49350979e374924163d85ddb0db9fc43432415b5ad6e096129405a3a55dd
data/Gemfile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ # Specify your gem's dependencies in shopify_transporter.gemspec
5
+ gemspec
6
+
7
+ group :development do
8
+ gem 'package_cloud'
9
+ gem 'rubocop'
10
+ gem 'rubocop-git', '~> 0.1'
11
+ end
12
+
13
+ group :test do
14
+ gem 'codecov', require: false
15
+ gem 'simplecov', require: false
16
+ end
17
+
18
+ group :development, :test do
19
+ gem 'factory_bot', '~> 4.8'
20
+ gem 'pry', '~> 0.11'
21
+ gem 'pry-byebug'
22
+ gem 'rspec', '~> 3.0'
23
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018 "Shopify Inc."
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,290 @@
1
+ # Transporter Tools
2
+
3
+ The Transporter tool helps converts data from a third-party platform format into a format that can
4
+ be imported into Shopify via the [Transporter app](https://help.shopify.com/manual/migrating-to-shopify/transporter-app).
5
+
6
+ The conversions are currently limited to JSON structured data that has been exported from Magento 1.x via its
7
+ SOAP API. More details about the exact structure of the JSON data, and additional scripts for procuding such
8
+ data, will be made available in the near future.
9
+
10
+ Note: the Transporter app is available to Shopify Plus plans only.
11
+
12
+ ## Installation
13
+
14
+ ### Building and installing locally
15
+ To build locally run the following command:
16
+
17
+ ```
18
+ $ bundle exec rake build
19
+
20
+ shopify_transporter 1.0.0 built to pkg/shopify_transporter-1.0.0.gem.
21
+ ```
22
+
23
+ Then, you can install the gem system-wide by running:
24
+
25
+ ```
26
+ $ gem install pkg/shopify_transporter-1.0.0.gem
27
+
28
+ Successfully installed shopify_transporter-1.0.0
29
+ Parsing documentation for shopify_transporter-1.0.0
30
+ Installing ri documentation for shopify_transporter-1.0.0
31
+ Done installing documentation for shopify_transporter after 0 seconds
32
+ 1 gem installed
33
+ ```
34
+ Your locally built gem is now installed system-wide.
35
+
36
+ ### Installing the Transporter tool gem from rubygems:
37
+
38
+ ```
39
+ $ gem install shopify_transporter
40
+ ```
41
+
42
+ ### Running
43
+ After you install the gem, you should find the executable `shopify_transporter` available in your path:
44
+
45
+ ```
46
+ $ which shopify_transporter
47
+ /usr/local/bin/shopify_transporter
48
+ ```
49
+ ## Help and usage
50
+
51
+ To view the usage and help for the `shopify_transporter` run the following command:
52
+
53
+ ```
54
+ $ shopify_transporter -h
55
+
56
+ Commands:
57
+ shopify_transporter convert --config=CONFIG --object=OBJECT file1.csv file2.csv ... # Converts your files into shopify formatted files.
58
+ shopify_transporter generate STAGE_NAME --object=OBJECT # Generates a new pipeline stage for the specified object type
59
+ shopify_transporter help [COMMAND] # Describe available commands or one specific command
60
+ shopify_transporter new PROJECTNAME --platform=PLATFORM # Generates a new project structure for a platform
61
+ ```
62
+
63
+ ## Create a conversion project
64
+
65
+ It's convenient to create a conversion project for each store that you want to migrate to Shopify.
66
+ To create it, use the `new` sub-command:
67
+
68
+ ```
69
+ $ shopify_transporter new example-magento-conversion --platform=magento
70
+ create example_magento_migration/Gemfile
71
+ create example_magento_migration/config.yml
72
+ create example_magento_migration/lib/magento/custom_pipeline_stages
73
+ ```
74
+
75
+ The `new` sub-command creates a project folder with the following:
76
+
77
+ * a `Gemfile` that references the `shopify_transporter` gem. This Gemfile allows custom pipeline stages
78
+ to refer to the base classes that are defined in the `shopify_transporter` gem.
79
+
80
+ * a configuration file (`config.yml`) that provides the configuration required for each pipeline stage
81
+ in the conversion process.
82
+
83
+ * a folder to hold additional custom pipeline stages you may define later
84
+
85
+ Switch to the project directory. For example:
86
+
87
+ ```
88
+ cd example_magento_migration
89
+ ```
90
+
91
+ As with any Ruby project, you need to run the following command before you create and run any custom
92
+ pipeline stages:
93
+
94
+ ```
95
+ $ bundle install
96
+ ```
97
+
98
+
99
+ ## Convert records from the third-party platform to Shopify
100
+
101
+ Run `shopify_transporter` and use the `convert` command to convert your objects from the
102
+ third-party platform to the Shopify format. For example, the following command converts a
103
+ JSON file that contains customers (*magento_customers.json*) from Magento to Shopify:
104
+
105
+ ```
106
+ shopify_transporter convert --config=config.yml --object=customer magento_customers.json > shopify_customers.csv
107
+ ```
108
+
109
+ In this example, the converted customer objects are saved to *shopify_customers.csv*. If errors occur during
110
+ the conversion, then they appear in your terminal.
111
+
112
+ ## Convert multiple files
113
+
114
+ To convert multiple files to Shopify, separate the file names with a space:
115
+
116
+ ```
117
+ shopify_transporter convert --config=config.yml --object=customer magento_customers_1.json magento_customers_2.json ...
118
+ ```
119
+
120
+ ## Configuration file (_config.yml_)
121
+
122
+ The configuraton file is generated when you create your conversion project. This file is specific to the
123
+ third-party platform that you are converting to Shopify.
124
+
125
+ Here's an example of a _config.yml_ file for converting customers from Magento:
126
+
127
+ ```
128
+ platform_type: Magento
129
+ object_types:
130
+ customer:
131
+ record_key: email
132
+ pipeline_stages:
133
+ - TopLevelAttributes
134
+ - AddressesAttribute
135
+ - Metafields:
136
+ type: all_platforms
137
+ params:
138
+ # Specify a custom namespace for your metafields with metafield_namespace.
139
+ # Uses migrated_data by default.
140
+ # metafield_namespace: migrated_data
141
+ metafields:
142
+ - website
143
+ - group
144
+ - free_trial_start_at
145
+ ```
146
+
147
+ ### record_key
148
+
149
+ The `config.yml` file allows you to define the object type to convert. An object type needs a `record_key`,
150
+ whose values must be unique among the other records in the file. For example, the default `record_key` for
151
+ customers is the customer's email address.
152
+
153
+ ```
154
+ platform_type: Magento
155
+ object_types:
156
+ customer:
157
+ record_key: email
158
+ ...
159
+ ```
160
+
161
+ When you run `shopify_transporter` with the `convert` command, the input (third-party platform) files are
162
+ read one-by-one and line-by-line. Each object in the input file must have a `record_key` value. Rows that
163
+ have the same `record_key` value are considered to be part of the same object.
164
+
165
+ ### Pipeline_stages
166
+
167
+ The Transporter tool processes the input (third-party platform objects) through a series of pipeline
168
+ stages. These stages, and the order in which they are to be processed, are defined in the `config.yml` file.
169
+
170
+ In the example below, there are two pipeline stages for converting objects from Magento to
171
+ Shopify: TopLevelAttributes and AddressAttributes.
172
+
173
+ ```
174
+ platform_type: Magento
175
+ object_types:
176
+ customer:
177
+ record_key: email
178
+ pipeline_stages:
179
+ - TopLevelAttributes
180
+ - AddressesAttribute
181
+ ```
182
+
183
+ Each pipeline stage's _convert_ method receives the row currently being processed, as well as the
184
+ current state of the corresponding Shopify object being converted. The method's responsibility is to
185
+ inject into the Shopify object the relevant attributes from the input row.
186
+
187
+ The role of a pipeline stage is to examine the input rows and populate attributes on the Shopify object.
188
+ For example, the `TopLevelAttributes` stage of a Magento customer migration looks for a column
189
+ named `firstname` on the input, and then populates the Shopify object accordingly:
190
+
191
+
192
+ ```
193
+ record['first_name'] = input['firstname']
194
+ ```
195
+
196
+ Any changes that are made to the this record in a pipeline stage are permanent to the Shopify record
197
+ associated with the `record_key`.
198
+
199
+ The next pipeline stage that receives this record, receives the same input and the existing record which
200
+ consist of:
201
+
202
+ ```
203
+ {
204
+ 'first_name' => 'John',
205
+ }
206
+ ```
207
+
208
+ Existing pipeline stages and the attributes are populated below.
209
+
210
+ ### Magento v1.x customer
211
+
212
+
213
+ ### AddressesAttribute
214
+
215
+ Addresses are built from the `shipping_` and `billing_` prefixed fields. The Shopify object's `addresses`
216
+ attribute is an array that consists of the `shipping_` prefixed attributes as the first address, and
217
+ the `billing_` prefixed attributes as the second.
218
+
219
+ ```
220
+ {
221
+ 'addresses': [
222
+ {
223
+ 'first_name': ...,
224
+ 'last_name': ...,
225
+ 'address1': ...,
226
+ 'address2': ...,
227
+ 'city': ...,
228
+ 'province': ...,
229
+ 'country_code': ...,
230
+ 'zip': ...,
231
+ 'company': ...,
232
+ 'phone': ...,
233
+ },
234
+ ]
235
+ }
236
+ ```
237
+
238
+ ### Metafields
239
+
240
+ See the metafields section in the All Platforms section below.
241
+
242
+ ### All Platforms
243
+
244
+ Some stages are a little more versatile and can support input rows from arbitrary third-party platforms. These stages
245
+ are usually extensible through defining parameters in the `config.yml`.
246
+
247
+ #### Metafields
248
+
249
+ The `convert` command converts the most popular metafields or custom fields from the third-party platforms into
250
+ Shopify [metafields](https://help.shopify.com/manual/products/metafields). You can view the default metafields and
251
+ add others in your `config.yml` file.
252
+
253
+ ## Adding customized stages
254
+
255
+ If the Shopify object that is generated is missing attributes or if a pipeline stage fails, it could be because there are
256
+ unexpected headers defined in the third-party CSV data.
257
+
258
+ To define your own customized stages, run the following command:
259
+
260
+ `shopify_transporter generate YourCustomStage --object customer`
261
+
262
+ A file named `your_custom_stage.rb` is added to the `lib/magento/custom_pipeline_stages` directory. You can modify this file to add your custom conversion logic.
263
+
264
+ ## Limitations
265
+
266
+ The `convert` command currently only converts customer and order JSON objects that have been exported by SOAP API
267
+ from Magento 1.x.
268
+
269
+ ## Running unit tests
270
+
271
+ We use rspec to run our test suite:
272
+ `bundle exec rspec`
273
+
274
+ ## Running the linter
275
+
276
+ It's important that all of the code introduced passes linting. To run it manually:
277
+ `bundle exec rake rubocop`
278
+ To automatically resolve any basic linting issues:
279
+ `bundle exec rake rubocop:autocorrect`
280
+
281
+ # Submitting Issues
282
+
283
+ Please open an issue here if you encounter a specific bug with this library or if something is documented
284
+ incorrectly.
285
+
286
+ When filing an issue, please ensure that:
287
+
288
+ - The issue is not already covered in another open issue
289
+ - The issue does not contain any confidential or personally identifying information
290
+ - The issue is specifically regarding the `shopify_transporter` gem and not related to the Transporter App.
data/RELEASING ADDED
@@ -0,0 +1,16 @@
1
+ Releasing ShopifyTransporter
2
+
3
+ 1. Check the Semantic Versioning page for info on how to version the new release: http://semver.org
4
+ 2. Update the version of ShopifyTransporter in lib/shopify_transporter/version.rb
5
+ 3. Add a CHANGELOG entry for the new release with the date
6
+ 4. Commit the changes with a commit message like "Packaging for release X.Y.Z"
7
+ 5. Tag the release with the version (Leave REV blank for HEAD or provide a SHA)
8
+ $ git tag vX.Y.Z REV
9
+ 6. Push out the changes
10
+ $ git push
11
+ 7. Push out the tags
12
+ $ git push --tags
13
+ 8. Build the new .gem from the updated .gemspec
14
+ $ gem build shopify_transporter.gemspec
15
+ 9. Publish the Gem to gemcutter
16
+ $ gem push shopify_transporter-X.Y.Z.gem
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+ require 'rubocop/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+ RuboCop::RakeTask.new
8
+
9
+ task default: :spec
10
+
11
+ Dir.glob('lib/tasks/*.rake').each { |r| import r }
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift("#{__dir__}/../lib")
5
+
6
+ require 'thor'
7
+ require 'thor/group'
8
+ require 'shopify_transporter'
9
+ require 'yaml'
10
+
11
+ module ShopifyTransporter
12
+ class CLI < Thor
13
+ def self.exit_on_failure?
14
+ true
15
+ end
16
+
17
+ method_option :config, type: :string, default: 'config.yml',
18
+ desc: "The config file that lists the conversions to run."
19
+ method_option :object, type: :string, required: true,
20
+ desc: "The object type (customer, product, or order) to convert.",
21
+ enum: %w(customer product order)
22
+
23
+ desc 'convert FILE_NAMES', 'Converts objects into a Shopify-format. (accepts a list of space-separated files).'
24
+ def convert(*files)
25
+ conversion_error('Missing file name at the end of the command.') if files.empty?
26
+ migration_tool = TransporterTool.new(*files, options[:config], options[:object])
27
+ migration_tool.run
28
+ rescue TransporterTool::ConversionError => error
29
+ conversion_error(error.message)
30
+ end
31
+
32
+ no_commands do
33
+ def conversion_error(message)
34
+ say("Conversion error: #{message}", :red)
35
+ exit 1
36
+ end
37
+ end
38
+
39
+ register ShopifyTransporter::New,
40
+ 'new', 'new PROJECTNAME --platform=PLATFORM', 'Generate a project for the platform'
41
+ register ShopifyTransporter::Generate,
42
+ 'generate', 'generate STAGE_NAME --object=OBJECT',
43
+ 'Generate a custom pipeline stage for the object'
44
+ end
45
+ end
46
+
47
+ ShopifyTransporter::CLI.start(ARGV)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ module ShopifyTransporter
3
+ class BaseGroup < Thor::Group
4
+ include Thor::Actions
5
+ argument :name
6
+
7
+ SUPPORTED_PLATFORMS_MAPPING = {
8
+ 'Magento' => 'magento',
9
+ }
10
+
11
+ def name_components
12
+ @name_components ||= name.scan(/[[:alnum:]]+/)
13
+ end
14
+
15
+ def config_filename
16
+ 'config.yml'
17
+ end
18
+
19
+ class << self
20
+ def source_root
21
+ File.expand_path('../../', File.dirname(__FILE__))
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ require_relative './base_group.rb'
3
+
4
+ module ShopifyTransporter
5
+ class Generate < BaseGroup
6
+ class_option :object, type: :string, required: :true,
7
+ enum: %w(customer order), aliases: '-O',
8
+ desc: 'The object to add to the pipeline stage'
9
+
10
+ def object_type
11
+ @object_type ||= options[:object].capitalize
12
+ end
13
+
14
+ def validate_config_exists
15
+ unless File.exist?(config_filename)
16
+ say("Cannot find config.yml at project root", :red)
17
+ exit 1
18
+ end
19
+ end
20
+
21
+ def pipeline_snake_name
22
+ name_components.map(&:underscore).join('_')
23
+ end
24
+
25
+ def pipeline_class
26
+ @pipeline_class ||= @name_components.map(&:classify).join('')
27
+ end
28
+
29
+ def platform
30
+ platform_type = YAML.load_file(config_filename)['platform_type']
31
+ @platform_file_path ||= SUPPORTED_PLATFORMS_MAPPING[platform_type]
32
+ @platform ||= platform_type.underscore
33
+ end
34
+
35
+ def generate_stage
36
+ template(
37
+ "templates/custom_stage.tt",
38
+ "lib/custom_pipeline_stages/#{pipeline_snake_name}.rb"
39
+ )
40
+ end
41
+
42
+ def add_stage_to_config
43
+ config_file = YAML.load_file(config_filename)
44
+ unless class_included_in?(config_file)
45
+ pipeline_class_hash = { @pipeline_class.to_s => nil, "type" => "custom" }
46
+ config_file['object_types'][@object_type.downcase]['pipeline_stages'] << pipeline_class_hash
47
+ File.open(config_filename, 'w') { |f| f.write config_file.to_yaml }
48
+ say('Updated config.yml with the new pipeline stage', :green)
49
+ return
50
+ end
51
+
52
+ say("Warning: The pipeline stage #{@pipeline_class} already exists in the config.yml", :blue)
53
+ end
54
+
55
+ private
56
+
57
+ def class_included_in?(config_file)
58
+ hash_stages = config_file['object_types'][@object_type.downcase]['pipeline_stages'].select do |stage|
59
+ stage.is_a? Hash
60
+ end
61
+ hash_stages.find { |h| h.keys.include? @pipeline_class.to_s }.present?
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ require_relative './base_group.rb'
3
+
4
+ module ShopifyTransporter
5
+ class New < BaseGroup
6
+ include Thor::Actions
7
+ class_option :platform, type: :string, required: :true, enum: %w(magento)
8
+
9
+ def snake_name
10
+ @snake_name ||= name_components.map(&:downcase).join("_")
11
+ end
12
+
13
+ def platform
14
+ @platform ||= options[:platform]
15
+ end
16
+
17
+ def generate_config
18
+ template("templates/#{@platform}/config.tt", "#{@snake_name}/config.yml")
19
+ template("templates/gemfile.tt", "#{@snake_name}/Gemfile")
20
+ empty_directory("#{@snake_name}/lib/custom_pipeline_stages")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'generators/generate.rb'
3
+ require_relative 'generators/new.rb'
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../stage'
3
+ require_relative '../../shopify'
4
+
5
+ module ShopifyTransporter
6
+ module Pipeline
7
+ module AllPlatforms
8
+ class Metafields < ShopifyTransporter::Pipeline::Stage
9
+ def convert(input, record)
10
+ raise 'Metafields not specified.' unless metafields_specified(params)
11
+
12
+ accumulator = MetafieldAttributesAccumulator.new(
13
+ initial_value: record,
14
+ metafields_to_extract: params['metafields'],
15
+ metafield_namespace: params['metafield_namespace'] || ShopifyTransporter::DEFAULT_METAFIELD_NAMESPACE,
16
+ )
17
+ accumulator.accumulate(input)
18
+ end
19
+
20
+ private
21
+
22
+ def metafields_specified(params)
23
+ params['metafields'].class == Array && params['metafields'].any?
24
+ end
25
+
26
+ class MetafieldAttributesAccumulator < ShopifyTransporter::Shopify::AttributesAccumulator
27
+ attr_reader :metafields_to_extract, :metafield_namespace
28
+
29
+ def initialize(initial_value:, metafields_to_extract:, metafield_namespace:)
30
+ @metafields_to_extract = metafields_to_extract
31
+ @metafield_namespace = metafield_namespace
32
+ super(initial_value)
33
+ end
34
+
35
+ private
36
+
37
+ def input_applies?(input)
38
+ attributes_present?(input, *metafields_to_extract)
39
+ end
40
+
41
+ def attributes_from(input)
42
+ {
43
+ 'metafields' => extract_metafields(input),
44
+ }
45
+ end
46
+
47
+ def extract_metafields(input)
48
+ metafields_to_extract.map do |metafield_key|
49
+ next if input[metafield_key].nil?
50
+ shopify_metafield_hash(
51
+ key: metafield_key,
52
+ value: input[metafield_key],
53
+ namespace: metafield_namespace
54
+ )
55
+ end.compact
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ require 'shopify_transporter/pipeline/stage'
3
+ require 'shopify_transporter/shopify'
4
+
5
+ module ShopifyTransporter
6
+ module Pipeline
7
+ module Magento
8
+ module Customer
9
+ class AddressesAttribute < Pipeline::Stage
10
+ def convert(input, record)
11
+ addresses = magento_addresses_from(input)
12
+ return unless addresses
13
+
14
+ record.merge!(convert_addresses(addresses))
15
+ end
16
+
17
+ private
18
+
19
+ COLUMN_MAPPING = {
20
+ 'firstname' => 'first_name',
21
+ 'lastname' => 'last_name',
22
+ 'street' => 'address1',
23
+ 'city' => 'city',
24
+ 'region' => 'province',
25
+ 'country_id' => 'country_code',
26
+ 'postcode' => 'zip',
27
+ 'company' => 'company',
28
+ 'telephone' => 'phone',
29
+ }
30
+
31
+ def convert_addresses(addresses)
32
+ {
33
+ 'addresses' => [
34
+ *addresses_from(addresses.select { |address| address['is_default_shipping'] }),
35
+ *addresses_from(addresses.select { |address| !address['is_default_shipping'] }),
36
+ ].compact,
37
+ }
38
+ end
39
+
40
+ def addresses_from(addresses)
41
+ return nil unless addresses.present?
42
+
43
+ addresses.map do |address|
44
+ COLUMN_MAPPING.each_with_object({}) do |(key, value), obj|
45
+ obj[value] = address[key] if address[key]
46
+ end
47
+ end
48
+ end
49
+
50
+ def magento_addresses_from(input)
51
+ addresses = input.dig('address_list', 'customer_address_list_response', 'result', 'item')
52
+ return nil unless addresses
53
+
54
+ addresses.is_a?(Array) ? addresses : [addresses]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end