pwinty 1.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,11 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
2
3
 
3
- require "bundler/gem_tasks"
4
- require 'rake/testtask'
5
-
6
- Rake::TestTask.new do |t|
7
- t.libs << 'test'
8
- end
4
+ RSpec::Core::RakeTask.new(:spec)
9
5
 
10
- desc "Run tests"
11
- task :default => :test
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pwinty3"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -1,88 +1,62 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+
4
+ require "pwinty/base"
5
+ require "pwinty/country"
6
+ require "pwinty/http_errors"
7
+ require "pwinty/image"
8
+ require "pwinty/order"
9
+ require "pwinty/order_status"
10
+ require 'pwinty/photo_status'
11
+ require "pwinty/shipment"
12
+ require "pwinty/shipping_info"
1
13
  require "pwinty/version"
2
- require "multipart"
3
- require "rest_client"
4
14
 
5
15
  module Pwinty
16
+ class Error < StandardError; end
17
+ class AuthenticationError < Pwinty::Error; end
18
+ class OrderNotFound < Pwinty::Error; end
19
+ class StateIsInvalid < Pwinty::Error; end
6
20
 
7
- def self.client(args={})
8
- @@client ||= Pwinty::Client.new(args)
9
- @@client
10
- end
21
+ MERCHANT_ID = ENV['PWINTY_MERCHANT_ID']
22
+ API_KEY = ENV['PWINTY_API_KEY']
23
+ BASE_URL = ENV['PWINTY_BASE_URL'] || 'https://sandbox.pwinty.com'
24
+ API_VERSION = 'v3.0'
11
25
 
12
- class Client
13
- attr_accessor :pwinty
14
- def initialize(args={})
15
- options = { merchant_id: ENV['PWINTY_MERCHANT_ID'], api_key: ENV['PWINTY_API_KEY'], production: ENV['PWINTY_PRODUCTION'] == 'true' }.merge(args)
16
- subdomain = options[:production] == true ? "api" : "sandbox"
17
- apiVersion = options[:api_version] || 'v2.1'
18
- domain = "https://#{subdomain}.pwinty.com/#{apiVersion}"
19
-
20
- @pwinty = RestClient::Resource.new(domain, :headers => {
21
- "X-Pwinty-MerchantId" => options[:merchant_id],
22
- "X-Pwinty-REST-API-Key" => options[:api_key],
23
- 'Accept' => 'application/json'
24
- })
25
- end
26
-
27
- def catalog(countryCode: 'US', qualityLevel: 'Standard')
28
- JSON.parse @pwinty["/Catalogue/#{countryCode}/#{qualityLevel}"].get
29
- end
30
-
31
- # Orders
32
- def get_orders
33
- JSON.parse @pwinty["/Orders"].get
34
- end
35
-
36
- def create_order(**args)
37
- JSON.parse @pwinty["/Orders"].post(args)
38
- end
39
-
40
- def update_order(**args)
41
- JSON.parse @pwinty["/Orders/#{args[:id]}"].put(args)
42
- end
43
-
44
- # Order Status
45
- def get_order_status(id)
46
- JSON.parse @pwinty["/Orders/#{id}/SubmissionStatus"].get
47
- end
48
-
49
- def update_order_status(id, status)
50
- JSON.parse @pwinty["/Orders/#{id}/Status"].post(status: status)
51
- end
52
-
53
- # Order Photos
54
- def get_photos(orderId)
55
- JSON.parse @pwinty["/Orders/#{orderId}/Photos"].get
26
+ class << self
27
+ attr_accessor :logger
28
+ def logger
29
+ @logger ||= Logger.new($stdout).tap do |log|
30
+ log.progname = self.name
31
+ end
32
+ end
56
33
  end
57
34
 
58
- def get_photo(orderId, photoId)
59
- JSON.parse @pwinty["/Orders/#{orderId}/Photos/#{photoId}"].get
60
- end
61
-
62
-
63
- def add_photo(**args)
64
- headers = {}
65
- orderId = args.delete(:orderId)
66
-
67
- unless args[:file].nil?
68
- args, headers = Multipart::Post.prepare_query(args)
69
- end
70
-
71
- JSON.parse @pwinty["/Orders/#{orderId}/Photos"].post(args, headers)
35
+ def self.url
36
+ "#{Pwinty::BASE_URL}/#{Pwinty::API_VERSION}/"
72
37
  end
73
38
 
74
- def add_photos(orderId, photos)
75
- body = photos.is_a?(String) ? photos : photos.to_json
76
- JSON.parse @pwinty["/Orders/#{orderId}/Photos/Batch"].post(body, {'Content-Type' => 'application/json'} )
39
+ def self.headers
40
+ {
41
+ 'X-Pwinty-MerchantId' => Pwinty::MERCHANT_ID,
42
+ 'X-Pwinty-REST-API-Key' => Pwinty::API_KEY,
43
+ }
77
44
  end
78
45
 
79
- def delete_photo(orderId, photoId)
80
- JSON.parse @pwinty["/Orders/#{orderId}/Photos/#{photoId}"].delete
46
+ def self.conn
47
+ Faraday.new(url: url, headers: headers) do |config|
48
+ config.request :json
49
+ config.response :json
50
+ config.use Pwinty::HttpErrors
51
+ config.adapter Faraday.default_adapter
52
+ end
81
53
  end
82
54
 
83
- # Countries
84
- def countries
85
- JSON.parse @pwinty["/Country"].get
55
+ def self.collate_results(response_data, classType)
56
+ collection = []
57
+ response_data.each do |individual_attr|
58
+ collection << classType.new(individual_attr)
59
+ end
60
+ collection
86
61
  end
87
- end
88
62
  end
@@ -0,0 +1,12 @@
1
+ require 'dry-struct'
2
+
3
+ module Pwinty
4
+
5
+ module Types
6
+ include Dry::Types()
7
+ end
8
+
9
+ class Base < Dry::Struct
10
+ transform_keys(&:to_sym)
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Pwinty
2
+
3
+ class Country < Pwinty::Base
4
+ attribute :name, Types::String
5
+ attribute :isoCode, Types::String
6
+
7
+ def self.list
8
+ response = Pwinty.conn.get("countries")
9
+ Pwinty.collate_results(response.body['data'], self)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Pwinty
2
+ class HttpErrors < Faraday::Response::Middleware
3
+ def on_complete(env)
4
+ msg = env[:body]
5
+ case env[:status]
6
+ when 401; raise Pwinty::AuthenticationError, msg
7
+ when 403; raise Pwinty::StateIsInvalid, msg
8
+ when 404; raise Pwinty::OrderNotFound, msg
9
+ when 500; raise Pwinty::Error, msg
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Pwinty
2
+
3
+ class Image < Pwinty::Base
4
+ attribute :id, Types::Integer
5
+ attribute :url, Types::String
6
+ attribute :status, Types::String
7
+ attribute :copies, Types::Integer
8
+ attribute :sizing, Types::String
9
+ attribute :price, Types::Integer
10
+ attribute :priceToUser, Types::Integer.optional
11
+ attribute :md5Hash, Types::String.optional
12
+ attribute :previewUrl, Types::String.optional
13
+ attribute :thumbnailUrl, Types::String.optional
14
+ attribute :sku, Types::String
15
+ attribute :attributes, Types::Hash.schema(
16
+ substrateWeight: Types::String,
17
+ frame: Types::String,
18
+ edge: Types::String,
19
+ paperType: Types::String,
20
+ frameColour: Types::String,
21
+ ).optional
22
+ attribute :errorMessage, Types::String.optional
23
+ end
24
+ end
@@ -0,0 +1,110 @@
1
+ require 'dry/struct/with_setters'
2
+
3
+ require "pwinty/shipping_info"
4
+
5
+ module Pwinty
6
+ class Order < Pwinty::Base
7
+ include Dry::Struct::Setters
8
+ include Dry::Struct::Setters::MassAssignment
9
+
10
+ attribute :id, Types::Integer
11
+ attribute :address1, Types::String.optional
12
+ attribute :address2, Types::String.optional
13
+ attribute :postalOrZipCode, Types::String.optional
14
+ attribute :countryCode, Types::String
15
+ attribute :addressTownOrCity, Types::String.optional
16
+ attribute :recipientName, Types::String.optional
17
+ attribute :stateOrCounty, Types::String.optional
18
+ attribute :status, Types::String
19
+ attribute :payment, Types::String
20
+ attribute :paymentUrl, Types::String.optional
21
+ attribute :price, Types::Integer
22
+ attribute :shippingInfo, Pwinty::ShippingInfo
23
+ attribute :images, Types::Array.of(Pwinty::Image)
24
+ attribute :merchantOrderId, Types::String.optional
25
+ attribute :preferredShippingMethod, Types::String
26
+ attribute :mobileTelephone, Types::String.optional
27
+ attribute :created, Types::JSON::DateTime
28
+ attribute :lastUpdated, Types::JSON::DateTime
29
+ attribute :canCancel, Types::Bool
30
+ attribute :canHold, Types::Bool
31
+ attribute :canUpdateShipping, Types::Bool
32
+ attribute :canUpdateImages, Types::Bool
33
+ attribute :errorMessage, Types::String.optional
34
+ attribute :invoiceAmountNet, Types::Integer
35
+ attribute :invoiceTax, Types::Integer
36
+ attribute :invoiceCurrency, Types::String.optional
37
+ attribute :tag, Types::String.optional
38
+
39
+ def self.list
40
+ response = Pwinty.conn.get("orders?count=250&offset=0")
41
+ r_data = response.body['data']
42
+ # TODO There is some bug with offset in the API.
43
+ # total_count = r_data['count']
44
+ Pwinty.collate_results(r_data['content'], self)
45
+ end
46
+
47
+
48
+ def self.count
49
+ response = Pwinty.conn.get("orders?count=1&offset=0")
50
+ response.body['data']['count']
51
+ end
52
+
53
+
54
+ def self.create(**args)
55
+ response = Pwinty.conn.post("orders", args)
56
+ new(response.body['data'])
57
+ end
58
+
59
+ def self.find(id)
60
+ response = Pwinty.conn.get("orders/#{id}")
61
+ new(response.body['data'])
62
+ end
63
+
64
+ def update(**args)
65
+ update_body = self.to_hash.merge(args)
66
+ response = Pwinty.conn.put("orders/#{self.id}/", update_body)
67
+ self.assign_attributes(response.body['data'])
68
+ end
69
+
70
+ def submission_status
71
+ response = Pwinty.conn.get("orders/#{id}/SubmissionStatus")
72
+ Pwinty::OrderStatus.new(response.body['data'])
73
+ end
74
+
75
+ def submit
76
+ self.update_status 'Submitted'
77
+ end
78
+
79
+ def cancel
80
+ self.update_status 'Cancelled'
81
+ end
82
+
83
+ def hold
84
+ self.update_status 'AwaitingPayment'
85
+ end
86
+
87
+ def add_image image
88
+ images = add_images([image])
89
+ self.images
90
+ end
91
+
92
+ def add_images images
93
+ response = Pwinty.conn.post("orders/#{self.id}/images/batch", images)
94
+ images = Pwinty.collate_results(response.body['data']['items'], Pwinty::Image)
95
+ self.images = self.images + images
96
+ end
97
+
98
+ protected
99
+
100
+ def update_status status
101
+ response = Pwinty.conn.post("orders/#{self.id}/status", {status: status})
102
+ success = response.status == 200
103
+ unless success
104
+ Pwinty.logger.warn response.body['statusTxt']
105
+ end
106
+ success
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,17 @@
1
+ require 'pwinty/photo_status'
2
+
3
+ module Pwinty
4
+
5
+ class OrderStatus < Pwinty::Base
6
+ attribute :id, Types::Coercible::Integer
7
+ attribute :isValid, Types::Bool
8
+ attribute :generalErrors, Types::Array.of(Types::String)
9
+ attribute :photos, Types::Array.of(Pwinty::PhotoStatus)
10
+
11
+
12
+ def self.check(id)
13
+ response = Pwinty.conn.get("orders/#{id}/SubmissionStatus")
14
+ new(response.body['data'])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module Pwinty
2
+
3
+ class PhotoStatus < Pwinty::Base
4
+ attribute :id, Types::Coercible::Integer
5
+ attribute :errors, Types::Array.of(Types::String)
6
+ attribute :warnings, Types::Array.of(Types::String)
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ module Pwinty
2
+
3
+ class Shipment < Pwinty::Base
4
+ attribute :shipmentId, Types::String.optional
5
+ attribute :isTracked, Types::Bool
6
+ attribute :trackingNumber, Types::String.optional
7
+ attribute :trackingUrl, Types::String.optional
8
+ attribute :carrier, Types::String.optional
9
+ attribute :photoIds, Types::Array.of(Types::Integer)
10
+ attribute :earliestEstimatedArrivalDate, Types::JSON::DateTime
11
+ attribute :latestEstimatedArrivalDate, Types::JSON::DateTime
12
+ attribute :shippedOn, Types::JSON::DateTime.optional
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require "pwinty/shipment"
2
+
3
+ module Pwinty
4
+
5
+ class ShippingInfo < Pwinty::Base
6
+ attribute :price, Types::Integer
7
+ attribute :shipments, Types::Array.of(Pwinty::Shipment)
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module Pwinty
2
- VERSION = "1.1.0"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -1,24 +1,39 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
4
  require "pwinty/version"
4
5
 
5
- Gem::Specification.new do |s|
6
- s.name = "pwinty"
7
- s.version = Pwinty::VERSION
8
- s.authors = ["Derek Lucas"]
9
- s.email = ["d@derekplucas.com"]
10
- s.homepage = "http://github.com/dereklucas/pwinty"
11
- s.licenses = ['MIT']
12
- s.summary = %q{A Ruby client for the Pwinty API}
13
- s.description = %q{Order photo prints with Ruby}
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pwinty"
8
+ spec.version = Pwinty::VERSION
9
+ spec.authors = ["Thomas Harvey"]
10
+ spec.email = ["tom@alush.co.uk"]
11
+
12
+ spec.summary = %q{Order photo prints through Pwinty}
13
+ spec.description = "This wraps the Pwinty API at version 3 and aims to make your ruby life easier when interacting with the API."
14
+ spec.homepage = "https://github.com/tomharvey/pwinty3-rb"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.17"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
14
29
 
15
- s.files = `git ls-files`.split("\n")
16
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
- s.require_paths = ["lib"]
30
+ spec.add_development_dependency "vcr", "~> 5.0"
31
+ spec.add_development_dependency "webmock", "~> 3.6"
32
+ spec.add_development_dependency "simplecov", "~> 0.17"
19
33
 
20
- s.add_development_dependency "rake"
21
- s.add_development_dependency "test-unit"
22
- s.add_development_dependency "dotenv"
23
- s.add_runtime_dependency "rest-client", '~> 1.8'
34
+ spec.add_dependency "dry-struct", "~> 1.0"
35
+ spec.add_dependency "dry-struct-setters", "~> 0.2"
36
+ spec.add_dependency "faraday", "~> 0.15"
37
+ spec.add_dependency "faraday_middleware", "~> 0.13"
38
+ spec.add_dependency "json", "~> 2.2"
24
39
  end