Skip to content

Instantly share code, notes, and snippets.

@berniechiu
Created January 20, 2020 15:48
Show Gist options
  • Save berniechiu/8e113e555fd30b54b4b7d16492adad66 to your computer and use it in GitHub Desktop.
Save berniechiu/8e113e555fd30b54b4b7d16492adad66 to your computer and use it in GitHub Desktop.
require_dependency Rails.root.join('app/models/platform_api/sync_service/error_handlers')
module PlatformApi
module SyncService
class ProductsSync
BATCH_IMPORT_CONFIG = {
on_duplicate_key_update: {
conflict_target: [:identifier, :platform_product_id, :company_id, :store_id],
columns: [:name, :length, :width, :height, :weight, :cost_price, :cost_price_currency, :selling_price, :selling_price_currency, :image_url]
},
validate: true
}.freeze
BATCH_UPDATE_CONFIG = {
on_duplicate_key_update: {
conflict_target: [:id],
columns: [:name, :length, :width, :height, :weight, :identifier, :platform_product_id, :cost_price, :cost_price_currency, :selling_price, :selling_price_currency, :image_url]
},
validate: true
}.freeze
attr_reader :store, :report, :platform_connection, :error
def initialize(store, user: nil, report: nil)
@store = store
@user = user || User.find_by(id: @store.company.owner&.id)
@report = report || Report.new(user: @user, store: @store, job_type: :api_upload)
@platform_connection = PlatformApi::StoreAdapter.new(store: @store)
@error = nil
end
def process
@report.calculate!
# TODO: Can think of a thread-safe parallel requests for faster processing
platform_connection.fetch_all_products do |batch|
normalized_products = Array(platform_connection.products_fetch_service.normalize(batch))
normalized_products = build_existing_identifier_products(normalized_products)
existing_products, new_products = normalized_products.partition { |data| data.id.present? }
updated_result = Product.import(existing_products, BATCH_UPDATE_CONFIG)
imported_result = Product.import(new_products, BATCH_IMPORT_CONFIG)
generate_report_records(updated_result)
generate_report_records(imported_result)
end
@report.complete!
rescue => e
raise @error = SyncError.new(e, store_id: store.id, store_name: store.name, platform: store.platform.name, report_id: report.id)
end
def build_existing_identifier_products(normalized_products)
existing_products = store.products.where(identifier: normalized_products.map(&:identifier), platform_product_id: nil).pluck(:identifier, :id).to_h
normalized_products.map { |product| product.id = existing_products[product.identifier]; product }
end
def generate_report_records(import_result)
successes = import_result.ids.map do |product_id|
@report.report_records.new(product_id: product_id, state: ::ReportRecord::SUCCEEDED)
end
failed = import_result.failed_instances.map do |product|
@report.report_records.new(state: ::ReportRecord::FAILED, log: { errors: product.errors.messages, data: product.attributes.compact })
end
import_records = successes.concat(failed)
@report.total ||= 0
@report.total += import_records.size
ReportRecord.import(import_records)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment