Created
May 2, 2018 08:02
-
-
Save foton/9a37127f6bda643866960e89678da460 to your computer and use it in GitHub Desktop.
Base Service class `.call` with any type of params, returning instace with `#success?`,`#failure?`, `#errors`, `#result` methods.
This file contains hidden or 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 Service | |
class Base | |
attr_reader :result | |
def self.call(*args, **keyword_args) | |
new(*args, **keyword_args).tap do |service| | |
service.instance_variable_set( | |
"@result", | |
service.call | |
) | |
end | |
end | |
def call | |
raise NotImplementedError | |
end | |
def success? | |
!failure? | |
end | |
def failure? | |
errors.any? | |
end | |
def errors | |
@errors ||= Errors.new | |
end | |
end | |
end |
This file contains hidden or 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 Service | |
class Errors < Hash | |
def add(key, value, _opts = {}) | |
self[key] ||= [] | |
self[key] << value | |
self[key].uniq! | |
end | |
def add_multiple_errors(errors_hash) | |
errors_hash.each do |key, values| | |
values.each { |value| add key, value } | |
end | |
end | |
def each | |
each_key do |field| | |
self[field].each { |message| yield field, message } | |
end | |
end | |
end | |
end |
This file contains hidden or 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
# frozen_string_literal: true | |
require 'uri' | |
require 'net/sftp' | |
module Service | |
module Sftp | |
class Uploader < Service::Base | |
STATUS_START = :start | |
STATUS_UPLOAD_RUNNING = :running | |
STATUS_UPLOADED = :uploaded | |
STATUS_UPLOAD_FAILED = :upload_failed | |
def initialize(file_path:, config:) | |
@from = file_path | |
@config = config | |
end | |
def call | |
@status = STATUS_START | |
if validate_config && validate_file | |
do_sftp_upload | |
else | |
@status = STATUS_UPLOAD_FAILED | |
end | |
@result = info | |
end | |
private | |
def validate_config | |
if @config['uri'].blank? | |
errors.add(:config, "SFTP config have empty 'uri' key") | |
@sftp_uri = OpenStruct.new(host: '', port: 0, path: '') | |
else | |
@sftp_uri = URI(@config['uri']) | |
# these can have empty values, but keys must be present | |
%w[username password].each do |key| | |
errors.add(:config, "SFTP config do not have #{key} key") unless @config.key?(key) | |
end | |
end | |
errors[:config].nil? | |
end | |
def validate_file | |
return true if File.file?(@from) | |
errors.add(:file, "File '#{@from}' was not found") | |
end | |
def do_sftp_upload | |
@status = STATUS_UPLOAD_RUNNING | |
upload! | |
@status = STATUS_UPLOADED | |
rescue Net::SFTP::StatusException => e | |
@status = STATUS_UPLOAD_FAILED | |
errors.add(:network, e.message) | |
end | |
def info | |
{ from: @from.to_s, | |
to: "sftp://#{@sftp_uri.host}:#{@sftp_uri.port || 115}#{to_path}", | |
status: @status } | |
end | |
def upload! | |
within_connection do |conn| | |
conn.upload!(@from.to_s, to_path) | |
end | |
end | |
def within_connection | |
Net::SFTP.start(sftp_uri.host, | |
config['username'], | |
port: (sftp_uri.port || 115), password: config['password']) do |conn| | |
yield conn | |
end | |
end | |
def filename | |
File.basename(@from.to_s) | |
end | |
def to_path | |
(@sftp_uri.path + '/' + filename).gsub('//', '/') | |
end | |
end | |
end | |
end |
This file contains hidden or 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
# frozen_string_literal: true | |
require 'rails_helper.rb' | |
RSpec.describe Service::Sftp::Uploader do | |
let(:service_class) { Service::Sftp::Uploader } | |
before(:context) do | |
@sftp_config = { 'uri' => Figaro.env.NIKE_SFTP_URI, | |
'username' => Figaro.env.NIKE_SFTP_USERNAME, | |
'password' => Figaro.env.NIKE_SFTP_PASSWORD } | |
@valid_path = __FILE__ # this file always exists, right? | |
@filename = File.basename(@valid_path) | |
end | |
it 'fails if config is not present' do | |
expect_any_instance_of(service_class).not_to receive(:upload!) | |
uploader = service_class.call(file_path: @valid_path, config: {}) | |
expect(uploader.failure?).to be(true) | |
expect(uploader.result).to eq(from: @valid_path, | |
to: 'sftp://:0/' + @filename, | |
status: :upload_failed) | |
expect(uploader.errors[:config]).to eq(["SFTP config have empty 'uri' key"]) | |
end | |
it 'uploads the file from path' do | |
expect_any_instance_of(service_class).to receive(:upload!).and_return(true) | |
uploader = service_class.call(file_path: @valid_path, config: @sftp_config) | |
expect(uploader.success?).to be(true) | |
expect(uploader.result).to eq(from: @valid_path, | |
to: @sftp_config['uri'].gsub(%r{\/$}, '') + '/' + @filename, | |
status: :uploaded) | |
end | |
it 'raise error if file do not exists' do | |
filename = 'my_file.xml' | |
not_valid_path = '/deep/into/dark/' + filename | |
expect_any_instance_of(service_class).not_to receive(:upload!) | |
uploader = service_class.call(file_path: not_valid_path, config: @sftp_config) | |
expect(uploader.success?).to be(false) | |
expect(uploader.failure?).to be(true) | |
expect(uploader.result).to eq(from: not_valid_path, | |
to: @sftp_config['uri'].gsub(%r{\/$}, '') + '/' + filename, | |
status: :upload_failed) | |
expect(uploader.errors[:file]).to eq(["File '#{not_valid_path}' was not found"]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment