whitehouse 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a6d7e235ec57af1cf16316c8dc68db684a1276b5
4
+ data.tar.gz: db7299985bfb54caa505228c8a6f0f3f665cba97
5
+ SHA512:
6
+ metadata.gz: b6e56a35c6cff590da279b07b43194ec50fff942becbdd00e7b8456e48ad28a199039f720a2cb4f8884820576b06a4f2384db282ac3caa6c4d84f0d318e24771
7
+ data.tar.gz: 9d0116ccbe9b8b6b7269a20d0fe29c7b89738f2b8c5142508aa88c7250c0e00917aeac42b218d2405ca2a0c3bf514236c0661149c761c17daf73784ba4cc6f7a
data/.env.test ADDED
@@ -0,0 +1,4 @@
1
+ # Test keys
2
+ WHCC_API_ENDPOINT='https://sandbox.apps.whcc.com/api/'
3
+ WHCC_CONSUMER_KEY='FA2DBC52662850A7B53B'
4
+ WHCC_CONSUMER_SECRET='rPTa7aIydCI='
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .env
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ *.bundle
20
+ *.so
21
+ *.o
22
+ *.a
23
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in whitehouse.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Travis Dahlke
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Whitehouse [![Build Status](https://travis-ci.org/whcc/whcc_ruby.svg?branch=master)](https://travis-ci.org/whcc/whcc_ruby)
2
+
3
+ Ruby client for the WHCC API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'whitehouse'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install whitehouse
18
+
19
+ ## Usage
20
+
21
+ ### Authentication
22
+ Configuration can be done at the module level:
23
+ ```ruby
24
+ Whitehouse.configure do |c|
25
+ c.consumer_key = 'FA2DBC52662850A7B53B'
26
+ c.consumer_secret = 'rPTa7aIydCI='
27
+ end
28
+ ```
29
+
30
+ at the client instance level:
31
+ ```ruby
32
+ client = Whitehouse::Client.new(consumer_key: 'FA2DBC52662850A7B53B', consumer_secret: 'rPTa7aIydCI=')
33
+ ```
34
+
35
+ or via environment variables:
36
+ ```
37
+ WHCC_CONSUMER_KEY='FA2DBC52662850A7B53B'
38
+ WHCC_CONSUMER_SECRET='rPTa7aIydCI='
39
+ ```
40
+
41
+ ### Fetch the catalog
42
+ ```ruby
43
+ Whitehouse.request_catalog
44
+ ```
45
+
46
+ ### Submitting an Order
47
+ ```ruby
48
+ # Build the order object
49
+ order = Whitehouse::Order.new
50
+ order.entry_id = '123abc'
51
+ order.order_attributes = [95,97]
52
+
53
+ # Set the shipping address
54
+ shipping_address = Whitehouse::Order::Address.new("Bob","123 Fake St","","Somewhere","MN","55121")
55
+ order.to_address = shipping_address
56
+
57
+ # Add the line items
58
+ item = Whitehouse::Order::OrderItem.new(3,"http://lab.whcc.com/ApostleIslandMarina.jpg","60ee3ed946def317eae764516b727f50", [5,1])
59
+ order.add_item(item)
60
+
61
+ # Submit order
62
+ confirm = Whitehouse.submit_order(order)
63
+
64
+ # Confirm order
65
+ if (confirm)
66
+ confirmed = Whitehouse.confirm_order(confirm)
67
+ end
68
+ ```
69
+
70
+ ### Creating Webhooks
71
+ ```ruby
72
+ Whitehouse.create_webhook('http://example.com/webhook')
73
+
74
+ # WHCC will POST to the uri provided with a 'verifier' token.
75
+ Whitehouse.verify_webhook(verifier)
76
+ ```
77
+
78
+ ## Contributing
79
+
80
+ 1. Fork it ( https://github.com/whcc/whcc_ruby/fork )
81
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
82
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "rspec/core/rake_task"
2
+ require "bundler/gem_tasks"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ['--color']
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,22 @@
1
+ module Whitehouse
2
+
3
+ # Methods for the Request Client Access Token API
4
+ #
5
+ # @see https://developers.whcc.com/API/OrderSubmit/RequestToken.aspx
6
+ module Authentication
7
+
8
+ def access_token
9
+ @access_token ||= begin
10
+ init_connection unless @connection
11
+ response = @connection.get 'AccessToken', {grant_type: 'consumer_credentials',
12
+ consumer_key: consumer_key,
13
+ consumer_secret: consumer_secret}
14
+ response.body.fetch("Token")
15
+ end
16
+ rescue StandardError => ex
17
+ raise Whitehouse::Error, "Failed to authenticate with WHCC. Check your credentials."
18
+ end
19
+ private :access_token
20
+
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module Whitehouse
2
+ class Client
3
+
4
+ # Methods for the Request Catalog API
5
+ #
6
+ # @see https://developers.whcc.com/API/OrderSubmit/RequestCatalog.aspx
7
+ module Catalog
8
+
9
+ # List entire catalog for account
10
+ def request_catalog
11
+ response = get 'Catalog'
12
+ response.body
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ require 'tempfile'
2
+
3
+ module Whitehouse
4
+ class Client
5
+
6
+ # Methods for the Order Submit API
7
+ #
8
+ # @see https://developers.whcc.com/API/OrderSubmit/OrderSubmit.aspx
9
+ module Order
10
+
11
+ BOUNDARY = "-----------RubyMultipartPost"
12
+
13
+ # Submit order
14
+ # @param order [Whitehouse::Order]
15
+ # @return [Integer, false] Confirmation ID of order if successful, otherwise false
16
+ def submit_order(order)
17
+ file = Tempfile.new('entry.json')
18
+ file.write(order.to_json)
19
+ file.close
20
+ response = post 'OrderImport', {entry: Faraday::UploadIO.new(file.path, "application/json")}
21
+ file.unlink
22
+ if response.success? && !response.body.ErrorNumber
23
+ response.body.ConfirmationID
24
+ else
25
+ false
26
+ end
27
+ end
28
+
29
+ # Confirm order
30
+ # @param [String] The confirmation id received when the order was submitted
31
+ # @return [Boolean] Success
32
+ def confirm_order(confirmation_id)
33
+ # Couldn't get faraday to build this request correctly
34
+ # connection.post "OrderImport/Submit/#{confirmation_id}"
35
+ url = connection.url_prefix + URI.parse("OrderImport/Submit/#{confirmation_id}")
36
+
37
+ header = {"Accept" => "application/json", "Content-Type"=> "multipart/form-data; boundary=#{BOUNDARY}"}
38
+
39
+ post_body = []
40
+ post_body << "--#{BOUNDARY}\r\n"
41
+ post_body << "Content-Disposition: form-data; name=\"access_token\"\r\n\r\n"
42
+ post_body << access_token
43
+ post_body << "\r\n--#{BOUNDARY}--\r\n"
44
+
45
+ # Create the HTTP objects
46
+ request = Net::HTTP::Post.new(url.path, header)
47
+ request.body = post_body.join
48
+ http = Net::HTTP.new(url.host, url.port)
49
+ http.use_ssl = true
50
+ res = http.request(request)
51
+ body = JSON.parse(res.body)
52
+ return res.is_a?(Net::HTTPSuccess) && !body["ErrorNumber"]
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,41 @@
1
+ module Whitehouse
2
+ class Client
3
+
4
+ # Methods for the Request Catalog API
5
+ #
6
+ # @see https://developers.whcc.com/API/OrderSubmit/Webhooks.aspx
7
+ module Webhook
8
+
9
+ # Create a callback
10
+ # @param [String] Callback URI
11
+ # @return [Boolean] Success
12
+ def create_webhook(uri)
13
+ response = post 'callback/create', {callbackUri: uri}
14
+ response.body
15
+ response.success? && !response.body.ErrorNumber
16
+ end
17
+
18
+ # Verify callback
19
+ # @param [String] Verifier token provided by WHCC POSTing to the callback uri upon creation.
20
+ # @return [Boolean] Success
21
+ def verify_webhook(verifier)
22
+ # This endpoint does not respond with json, so not using faraday connection
23
+ # response = connection.post 'callback/create', {callbackUri: uri}
24
+ # response.body
25
+ # response.success? && !response.body.ErrorNumber
26
+
27
+ url = connection.url_prefix + URI.parse("callback/verify")
28
+ header = {"Accept" => "text/xml"}
29
+
30
+ # Create the HTTP objects
31
+ request = Net::HTTP::Post.new(url.path, header)
32
+ request.set_form_data(access_token: access_token, verifier: verifier)
33
+ http = Net::HTTP.new(url.host, url.port)
34
+ http.use_ssl = true
35
+ res = http.request(request)
36
+ return res.is_a?(Net::HTTPSuccess) && res.body.scan('Failed').empty?
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,107 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+ require "whitehouse/error"
4
+ require "whitehouse/configurable"
5
+ require "whitehouse/authentication"
6
+ require "whitehouse/client/catalog"
7
+ require "whitehouse/client/order"
8
+ require "whitehouse/client/webhook"
9
+
10
+ module Whitehouse
11
+
12
+ # Client for the WHCC API
13
+ #
14
+ # @see https://developers.whcc.com
15
+ class Client
16
+
17
+ include Whitehouse::Configurable
18
+ include Whitehouse::Authentication
19
+ include Whitehouse::Client::Catalog
20
+ include Whitehouse::Client::Order
21
+ include Whitehouse::Client::Webhook
22
+
23
+ def initialize(options = {})
24
+ Whitehouse::Configurable.keys.each do |key|
25
+ instance_variable_set(:"@#{key}", options[key] || Whitehouse.instance_variable_get(:"@#{key}"))
26
+ end
27
+ end
28
+
29
+ def connection
30
+ @connection || init_connection(:oauth2)
31
+ end
32
+
33
+ # Compares client options to a Hash of requested options
34
+ #
35
+ # @param opts [Hash] Options to compare with current client options
36
+ # @return [Boolean]
37
+ def same_options?(opts)
38
+ opts.hash == options.hash
39
+ end
40
+
41
+ # Text representation of the client, masking tokens
42
+ #
43
+ # @return [String]
44
+ def inspect
45
+ inspected = super
46
+
47
+ # Only show last 4 of token, secret
48
+ if @access_token
49
+ inspected = inspected.gsub! @access_token, "#{'*'*8}#{@access_token[8..-1]}"
50
+ end
51
+ if @consumer_secret
52
+ inspected = inspected.gsub! @consumer_secret, "#{'*'*8}#{@consumer_secret[8..-1]}"
53
+ end
54
+
55
+ inspected
56
+ end
57
+
58
+ def get(*args)
59
+ request :get, *args
60
+ end
61
+
62
+ def post(*args)
63
+ request :post, *args
64
+ end
65
+
66
+ private
67
+
68
+ def reset_connection
69
+ @connection = nil
70
+ @access_token = nil
71
+ end
72
+
73
+ def request(method, *args)
74
+ tries ||= 2
75
+ response = connection.public_send(method, *args)
76
+ check_errors(response)
77
+ response
78
+ rescue Error::AuthorizationExpired => ex
79
+ reset_connection
80
+ if (tries -= 1) > 0
81
+ retry
82
+ else
83
+ raise ex
84
+ end
85
+ end
86
+
87
+ def check_errors(response)
88
+ raise Error, response.status unless response.success?
89
+ raise Error::CODES[response.body.ErrorNumber], response.body.Message if response.body.ErrorNumber
90
+ end
91
+
92
+ def init_connection(oauth = false)
93
+ @connection = Faraday.new(api_endpoint, connection_options) do |faraday|
94
+ faraday.request :multipart
95
+ faraday.request :url_encoded
96
+ faraday.request :oauth2, access_token if oauth
97
+
98
+ # faraday.response :logger
99
+ faraday.response :mashify
100
+ faraday.response :json, :content_type => /\bjson$/
101
+ # faraday.response :raise_error
102
+ faraday.adapter Faraday.default_adapter
103
+ end
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,65 @@
1
+ module Whitehouse
2
+ module Configurable
3
+ # @!attribute access_token
4
+ # @return [String] Client access token
5
+ # @!attribute api_endpoint
6
+ # @return [String] Base URL for API requests. default: https://apps.whcc.com/api
7
+ # @!attribute connection_options
8
+ # @see https://github.com/lostisland/faraday
9
+ # @return [Hash] Configure connection options for Faraday
10
+ # @!attribute consumer_key
11
+ # @return [String] Configure OAuth app key
12
+ # @!attribute [w] consumer_secret
13
+ # @return [String] Configure OAuth app secret
14
+ # @!attribute default_media_type
15
+ # @return [String] Configure preferred media type
16
+ # @!attribute user_agent
17
+ # @return [String] Configure User-Agent header for requests.
18
+
19
+ attr_accessor :access_token, :api_endpoint, :connection_options, :consumer_key,
20
+ :consumer_secret, :default_media_type, :user_agent
21
+
22
+ class << self
23
+
24
+ # List of configurable keys for {Whitehouse::Client}
25
+ # @return [Array] of option keys
26
+ def keys
27
+ @keys ||= [
28
+ :access_token,
29
+ :api_endpoint,
30
+ :connection_options,
31
+ :consumer_key,
32
+ :consumer_secret,
33
+ :default_media_type,
34
+ :user_agent
35
+ ]
36
+ end
37
+ end
38
+
39
+ # Set configuration options using a block
40
+ def configure
41
+ yield self
42
+ end
43
+
44
+ # Reset configuration options to default values
45
+ def reset!
46
+ Whitehouse::Configurable.keys.each do |key|
47
+ instance_variable_set(:"@#{key}", Whitehouse::Default.options[key])
48
+ end
49
+ self
50
+ end
51
+ alias setup reset!
52
+
53
+ def api_endpoint
54
+ File.join(@api_endpoint, "")
55
+ end
56
+
57
+ private
58
+
59
+ def options
60
+ Hash[Whitehouse::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,71 @@
1
+ require 'whitehouse/version'
2
+
3
+ module Whitehouse
4
+ class Default
5
+
6
+ # Default API Endpoint
7
+ API_ENDPOINT = 'https://apps.whcc.com/api/'.freeze
8
+
9
+ # Default User Agent
10
+ USER_AGENT = "Whitehouse Ruby Gem #{Whitehouse::VERSION}".freeze
11
+
12
+ # Default Media Type
13
+ MEDIA_TYPE = "application/json"
14
+
15
+ class << self
16
+ # Configuration options
17
+ # @return [Hash]
18
+ def options
19
+ Hash[Whitehouse::Configurable.keys.map{|key| [key, send(key)]}]
20
+ end
21
+
22
+ # Default API endpoint from ENV or {API_ENDPOINT}
23
+ # @return [String]
24
+ def api_endpoint
25
+ ENV['WHCC_API_ENDPOINT'] || API_ENDPOINT
26
+ end
27
+
28
+ # Default User-Agent header string from ENV or {USER_AGENT}
29
+ # @return [String]
30
+ def user_agent
31
+ ENV['WHCC_USER_AGENT'] || USER_AGENT
32
+ end
33
+
34
+ # Default media type from ENV or {MEDIA_TYPE}
35
+ # @return [String]
36
+ def default_media_type
37
+ ENV['WHCC_DEFAULT_MEDIA_TYPE'] || MEDIA_TYPE
38
+ end
39
+
40
+ # Default access token from ENV
41
+ # @return [String]
42
+ def access_token
43
+ ENV['WHCC_ACCESS_TOKEN']
44
+ end
45
+
46
+ # Default OAuth app key from ENV
47
+ # @return [String]
48
+ def consumer_key
49
+ ENV['WHCC_CONSUMER_KEY']
50
+ end
51
+
52
+ # Default OAuth app secret from ENV
53
+ # @return [String]
54
+ def consumer_secret
55
+ ENV['WHCC_CONSUMER_SECRET']
56
+ end
57
+
58
+ # Default options for Faraday::Connection
59
+ # @return [Hash]
60
+ def connection_options
61
+ {
62
+ :headers => {
63
+ :accept => default_media_type,
64
+ :user_agent => user_agent
65
+ }
66
+ }
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,22 @@
1
+ module Whitehouse
2
+ class Error < StandardError
3
+ ConfigurationError = Class.new(::ArgumentError)
4
+
5
+ NotAuthorized = Class.new(self)
6
+ AuthorizationExpired = Class.new(NotAuthorized)
7
+
8
+ InvalidOrder = Class.new(self)
9
+ IncorrectOrderImport = Class.new(InvalidOrder)
10
+ InvalidBusinessRules = Class.new(InvalidOrder)
11
+ InvalidConfirmationId = Class.new(InvalidOrder)
12
+ EntryAlreadySubmitted = Class.new(InvalidOrder)
13
+
14
+ CODES = {
15
+ "403.01" => AuthorizationExpired,
16
+ "412.01" => IncorrectOrderImport,
17
+ "412.02" => InvalidBusinessRules,
18
+ "412.03" => InvalidConfirmationId,
19
+ "412.04" => EntryAlreadySubmitted,
20
+ }.freeze
21
+ end
22
+ end
@@ -0,0 +1,112 @@
1
+ require 'json'
2
+
3
+ module Whitehouse
4
+ class Order
5
+
6
+ attr_accessor :entry_id, :drop_ship, :from_address_value, :sequence_number,
7
+ :from_address, :to_address, :order_attributes
8
+ attr_reader :items
9
+
10
+ def initialize
11
+ defaults
12
+ yield self if block_given?
13
+ end
14
+
15
+ OrderItem = Struct.new(:uid, :url, :md5, :attributes, :rotation) do
16
+ def assets
17
+ [{"AssetPath" => url,
18
+ "PrintedFileName" => "",
19
+ "ImageHash" => md5,
20
+ "DP2NodeID" => 10000,
21
+ "OffsetX" => 50,
22
+ "OffsetY" => 50,
23
+ "X" => 50,
24
+ "Y" => 50,
25
+ "ZoomX" => 100,
26
+ "ZoomY" => 100,
27
+ "ImageRotation" => 0,
28
+ "isCoverAsset" => false,
29
+ "isJacketAsset" => false,
30
+ "ColorCorrect" => false}]
31
+ end
32
+
33
+ def item_attributes
34
+ return [] unless attributes
35
+ attributes.map{|attr| {"AttributeUID" => attr}}
36
+ end
37
+ end
38
+
39
+ Address = Struct.new(:name, :addr1, :addr2, :city, :state, :zip, :country) do
40
+ def to_hash
41
+ {"Name" => name,
42
+ "Addr1" => addr1,
43
+ "Addr2" => addr2,
44
+ "City" => city,
45
+ "State" => state,
46
+ "Zip" => zip,
47
+ "Country" => country || 'US'
48
+ }
49
+ end
50
+ end
51
+
52
+
53
+ def add_item(item)
54
+ @items << item
55
+ end
56
+
57
+ def to_json
58
+ {"EntryID" => entry_id,
59
+ "Orders" => [
60
+ {"DropShipFlag" => drop_ship,
61
+ "FromAddressValue" => from_address_value,
62
+ "OrderAttributes" => order_attributes,
63
+ # "Reference" => reference,
64
+ # "Instructions" => instructions,
65
+ "SequenceNumber" => sequence_number,
66
+ "ShipToAddress" => to_address,
67
+ # "ShipFromAddress" => from_address,
68
+ "OrderItems" => order_items
69
+ }
70
+ ]
71
+ }.to_json
72
+ end
73
+
74
+ private
75
+
76
+ def defaults
77
+ self.drop_ship = 1
78
+ self.from_address_value = 1
79
+ self.sequence_number = 1
80
+ self.order_attributes = []
81
+ @items = []
82
+ end
83
+
84
+ def order_attributes
85
+ @order_attributes.map{|attr| {"AttributeUID" => attr}}
86
+ end
87
+
88
+ def order_items
89
+ items.each_with_index.map do |item, i|
90
+ {"ItemAssets" => item.assets,
91
+ "ItemAttributes" => item.item_attributes,
92
+ "ProductUID" => item.uid,
93
+ "Quantity" => 1,
94
+ "LineItemID" => i,
95
+ "LayoutRotation" => item.rotation}
96
+ end
97
+ end
98
+
99
+ def to_address
100
+ (@to_address || default_address).to_hash
101
+ end
102
+
103
+ def from_address
104
+ (@from_address || default_address).to_hash
105
+ end
106
+
107
+ def default_address
108
+ Address.new
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,3 @@
1
+ module Whitehouse
2
+ VERSION = "0.0.1"
3
+ end