pwinty 1.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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