Last active
April 4, 2022 10:14
-
-
Save saggineumann/201f419980a4d6cacc04e38b234f8fc4 to your computer and use it in GitHub Desktop.
SFTP Shopify File Synchornization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FileServices | |
class FileImporterService < ApplicationService | |
attr_reader :shop | |
class FileCreateError < StandardError | |
end | |
def initialize(shop) | |
@shop = shop | |
end | |
def call(file_name) | |
shop.with_shopify_session do | |
execute_query(file_name) | |
end | |
true | |
end | |
private | |
def execute_query(file_name) | |
full_url = "#{ENV.fetch('SFTPTOGO_PUBLIC_URL')}#{ERB::Util.url_encode(file_name)}" | |
Rails.logger.info "Creating #{file_name}" | |
result = ShopifyAPI::GraphQL.client.query(file_create_query, variables: { | |
"files": { | |
"originalSource": full_url | |
} | |
}) | |
error = result&.errors&.details || result.to_h['data']['fileCreate']['userErrors'].first | |
raise FileCreateError, error['message'] if error.present? | |
end | |
def file_create_query | |
ShopifyAPI::GraphQL.client.parse <<-'GRAPHQL' | |
mutation ($files: [FileCreateInput!]!) { | |
fileCreate(files: $files) { | |
files { | |
fileStatus | |
} | |
userErrors { | |
field | |
message | |
} | |
} | |
} | |
GRAPHQL | |
end | |
end | |
End |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FileServices | |
## reads existing filenames from Shopify to avoid duplicates | |
# then starts reading filenames from FTP and creates new file entries in Shopify | |
class FileOrganizerService < ApplicationService | |
attr_reader :shop | |
def initialize(shop) | |
@shop = shop | |
end | |
def call | |
# get an array of the existing filenames in the Shopify store to avoid duplicate uploads | |
existing_filenames = query_existing_files | |
# get all filenames that are not yet existing in the shopify files | |
file_names = FileServices::SftpFileLoaderService.new(ENV.fetch('SFTPTOGO_URL'), existing_filenames).call | |
file_names.each do |file_name| | |
# Create a new file entry | |
FileServices::FileImporterService.new(shop).call(file_name) | |
rescue StandardError => e | |
# report any errors to everyone's favourite error tracker | |
Honeybadger.notify("ERROR Uploading #{file_name}: #{e.message}") | |
end | |
end | |
private | |
def query_existing_files | |
existing_filenames = [] | |
shop.with_shopify_session do | |
file_result = ShopifyAPI::GraphQL.client.query(file_search_query) | |
existing_filenames << query_result_to_array(file_result&.data&.files&.edges) | |
# if more than one page | |
while file_result&.data&.files&.page_info&.has_next_page | |
cursor = file_result&.data&.files&.edges&.last&.cursor | |
file_result = ShopifyAPI::GraphQL.client.query(file_search_query, variables: { cursor: cursor }) | |
existing_filenames << query_result_to_array(file_result&.data&.files&.edges) | |
end | |
end | |
existing_filenames.flatten | |
end | |
def file_search_query | |
ShopifyAPI::GraphQL.client.parse <<-'GRAPHQL' | |
query ($cursor: String){ | |
files(first:250, after: $cursor){ | |
pageInfo { | |
hasNextPage | |
} | |
edges{ | |
cursor | |
node{... on GenericFile { | |
url | |
id | |
}} | |
node{... on MediaImage { | |
id | |
}} | |
} | |
} | |
} | |
GRAPHQL | |
end | |
private | |
def query_result_to_array(edges) | |
return unless edges.present? | |
edges.map do |edge| | |
if edge&.node&.__typename == 'GenericFile' | |
File.basename(URI.parse(edge&.node&.url).path) if edge&.node&.url.present? | |
else | |
edge&.node&.id | |
end | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FileServices | |
class SftpFileLoaderService < ApplicationService | |
attr_reader :sftp_uri, :filename, :existing_filenames | |
def initialize(sftp_uri, existing_filenames) | |
@sftp_uri = URI(sftp_uri) | |
@existing_filenames = existing_filenames | |
end | |
def call | |
load_files | |
end | |
private | |
def sftp | |
# establish connection to SFTP Server | |
@sftp ||= Net::SFTP.start(@sftp_uri.host, @sftp_uri.user, password: @sftp_uri.password) | |
end | |
# fetches all filenames from the configured directory, that are not in the existing_filenames list | |
def load_files | |
file_names = [] | |
dir = ENV.fetch('FTP_FILE_DIR') | |
sftp.dir.foreach(dir) do |entry| | |
# skip if file already exists on server | |
next if existing_filenames.include? entry.name | |
file_names << entry.name | |
rescue Net::SFTP::StatusException => e | |
Rails.logger.error "Error while downloading data: #{e.description}" | |
end | |
file_names | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment