diff --git a/dev/lib/product_taxonomy/models/integration_version.rb b/dev/lib/product_taxonomy/models/integration_version.rb index d605dc8c1..ac2095b96 100644 --- a/dev/lib/product_taxonomy/models/integration_version.rb +++ b/dev/lib/product_taxonomy/models/integration_version.rb @@ -50,12 +50,9 @@ def load_all_from_source(current_shopify_version: nil, base_path: INTEGRATIONS_P # # @param versions [Array] The versions to resolve, ordered from oldest to newest. def resolve_to_shopify_mappings_chain(versions) - # Resolve newest version against current taxonomy - versions.last.resolve_to_shopify_mappings(nil) - - # Resolve each older version against the one following it - versions.each_cons(2).reverse_each do |previous, next_version| - previous.resolve_to_shopify_mappings(next_version) + versions.reverse.each_with_object([]) do |version, resolved_versions| + version.resolve_to_shopify_mappings(resolved_versions) + resolved_versions.prepend(version) end end @@ -177,16 +174,23 @@ def generate_distribution(output_path:, direction:) ) end - # Resolve the output categories of to_shopify mappings to the next version of the Shopify taxonomy. + # Resolve the output categories of to_shopify mappings to the current version of the Shopify taxonomy, taking into + # any mappings from later versions. # - # @param next_integration_version [IntegrationVersion | nil] The IntegrationVersion defining mappings to the next - # newer version of the Shopify taxonomy. If nil, the latest version of the Shopify taxonomy is used. - def resolve_to_shopify_mappings(next_integration_version) + # @param next_integration_versions [Array] An array of Shopify integration versions coming + # after the current version. + def resolve_to_shopify_mappings(next_integration_versions) @to_shopify_mappings.each do |mapping| - newer_mapping = next_integration_version&.to_shopify_mappings&.find do - _1.input_category["id"] == mapping.output_category + newer_mapping = next_integration_versions.flat_map(&:to_shopify_mappings).find do |mapping_rule| + mapping_rule.input_category["id"] == mapping.output_category end mapping.output_category = newer_mapping&.output_category || Category.find_by(id: mapping.output_category) + + next unless mapping.output_category.nil? + + raise ArgumentError, "Failed to resolve Shopify mapping: " \ + "\"#{mapping.input_category["id"]}\" to \"#{mapping.output_category}\" " \ + "(input version: #{version})" end end diff --git a/dev/test/models/integration_version_test.rb b/dev/test/models/integration_version_test.rb index f20cd6fd2..8f492a0b8 100644 --- a/dev/test/models/integration_version_test.rb +++ b/dev/test/models/integration_version_test.rb @@ -23,7 +23,7 @@ class IntegrationVersionTest < TestCase integration_path: File.expand_path("integrations/shopify/2020-01", DATA_PATH), current_shopify_version: @current_shopify_version, ) - @shopify_integration.resolve_to_shopify_mappings(nil) # Resolve against current version + @shopify_integration.resolve_to_shopify_mappings([]) # Resolve against current version @external_integration = IntegrationVersion.load_from_source( integration_path: File.expand_path("integrations/foocommerce/1.0.0", DATA_PATH), current_shopify_version: @current_shopify_version, @@ -236,7 +236,7 @@ class IntegrationVersionTest < TestCase end test "unmapped_external_category_ids returns IDs of external categories not mapped from the Shopify taxonomy" do - external_category1 = { "id" => "1", "full_name" => "External category 1" } + external_category1 = category_hash("1") from_shopify_mappings = [ MappingRule.new( input_category: Category.new(id: "aa", name: "aa"), @@ -257,5 +257,153 @@ class IntegrationVersionTest < TestCase assert_equal ["2", "3"], integration_version.unmapped_external_category_ids end + + test "resolve_to_shopify_mappings resolves mappings to the latest version of the Shopify taxonomy" do + mapping = MappingRule.new(input_category: category_hash("aa-1"), output_category: "aa-2") + integration_version = IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [mapping], + full_names_by_id: {}, + ) + integration_version.resolve_to_shopify_mappings([]) + assert_equal "gid://shopify/TaxonomyCategory/aa-2", mapping.output_category.gid + end + + test "resolve_to_shopify_mappings raises an error if a mapping cannot be resolved" do + mapping = MappingRule.new(input_category: category_hash("aa-1"), output_category: "invalid") + integration_version = IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [mapping], + full_names_by_id: {}, + ) + assert_raises(ArgumentError) { integration_version.resolve_to_shopify_mappings([]) } + end + + test "IntegrationVersion.resolve_to_shopify_mappings_chain resolves mappings to the current version of the Shopify taxonomy" do + version = IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-1"), output_category: "aa-2")], + full_names_by_id: {}, + ) + IntegrationVersion.resolve_to_shopify_mappings_chain([version]) + assert_equal "gid://shopify/TaxonomyCategory/aa-2", version.to_shopify_mappings[0].output_category.gid + end + + test "IntegrationVersion.resolve_to_shopify_mappings_chain resolves mappings through two versions without chained mappings" do + versions = [ + IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-1"), output_category: "aa-2")], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2021-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("bb-1"), output_category: "aa-1")], + full_names_by_id: {}, + ), + ] + IntegrationVersion.resolve_to_shopify_mappings_chain(versions) + assert_equal "gid://shopify/TaxonomyCategory/aa-2", versions[0].to_shopify_mappings[0].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-1", versions[1].to_shopify_mappings[0].output_category.gid + end + + test "IntegrationVersion.resolve_to_shopify_mappings_chain resolves mappings through two versions with chained mappings" do + versions = [ + IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-1"), output_category: "aa-2")], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2021-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-2"), output_category: "aa-3")], + full_names_by_id: {}, + ), + ] + IntegrationVersion.resolve_to_shopify_mappings_chain(versions) + assert_equal "gid://shopify/TaxonomyCategory/aa-3", versions[0].to_shopify_mappings[0].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-3", versions[1].to_shopify_mappings[0].output_category.gid + end + + test "IntegrationVersion.resolve_to_shopify_mappings_chain resolves mappings through four versions with non-consecutive chained mappings" do + versions = [ + IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-1"), output_category: "aa-2")], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2021-01", + to_shopify_mappings: [], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2022-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-2"), output_category: "aa-3")], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2023-01", + to_shopify_mappings: [], + full_names_by_id: {}, + ), + ] + IntegrationVersion.resolve_to_shopify_mappings_chain(versions) + assert_equal "gid://shopify/TaxonomyCategory/aa-3", versions[0].to_shopify_mappings[0].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-3", versions[2].to_shopify_mappings[0].output_category.gid + end + + test "IntegrationVersion.resolve_to_shopify_mappings_chain resolves mappings through four versions with a mix of chained and non-chained mappings" do + versions = [ + IntegrationVersion.new( + name: "shopify", + version: "2020-01", + to_shopify_mappings: [ + MappingRule.new(input_category: category_hash("aa-1"), output_category: "aa-2"), + MappingRule.new(input_category: category_hash("bb-1"), output_category: "aa-1"), + ], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2021-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("cc-1"), output_category: "aa-1")], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2022-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("aa-2"), output_category: "aa-3")], + full_names_by_id: {}, + ), + IntegrationVersion.new( + name: "shopify", + version: "2023-01", + to_shopify_mappings: [MappingRule.new(input_category: category_hash("dd-1"), output_category: "aa-2")], + full_names_by_id: {}, + ), + ] + IntegrationVersion.resolve_to_shopify_mappings_chain(versions) + assert_equal "gid://shopify/TaxonomyCategory/aa-3", versions[0].to_shopify_mappings[0].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-1", versions[0].to_shopify_mappings[1].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-1", versions[1].to_shopify_mappings[0].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-3", versions[2].to_shopify_mappings[0].output_category.gid + assert_equal "gid://shopify/TaxonomyCategory/aa-2", versions[3].to_shopify_mappings[0].output_category.gid + end + + def category_hash(id) + { "id" => id, "full_name" => "Category #{id}" } + end end end