zuora-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5415a26e1054e37bd49d820c0a79ce67afee2b23
4
+ data.tar.gz: be862d326b31f2f0133fd7b19084034ca30b55bf
5
+ SHA512:
6
+ metadata.gz: 818e018dfaac566ef45e4824cc2de1c7c7910be67e89486a068b185f26a95b9b9c182c8d443b6dcb03fa5a7b6789dc487c6ccb45cef2d874fb70afd6935dbe3f
7
+ data.tar.gz: 24bed325e1caab98bda4d1fa7f9492346215392c2a5710106775102a0679673b55f484802126efba89de796117fdb2d889ae031083736bd3e4ae979c12e716af
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .env
11
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,22 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'db/**/*'
4
+ - 'config/**/*'
5
+ - 'vendor/**/*'
6
+
7
+ Style/Encoding:
8
+ Enabled: true
9
+
10
+ Style/Documentation:
11
+ Description: 'Document classes and non-namespace modules.'
12
+ Enabled: false
13
+
14
+ Style/Encoding:
15
+ Enabled: false
16
+
17
+ AbcSize:
18
+ Enabled: false
19
+
20
+ MethodLength:
21
+ Enabled: true
22
+ Max: 25
@@ -0,0 +1 @@
1
+ ruby-2.2.3
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+ source 'https://rubygems.org'
3
+
4
+ # Specify your gem's dependencies in zuora.gemspec
5
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 John Gerhardt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,198 @@
1
+ [![Circle CI](https://circleci.com/gh/contactually/zuora-ruby.svg?style=shield&circle-token=808be5d625e91e331bedb37a2fe94412bb3bc15e)](https://circleci.com/gh/contactually/zuora-ruby)
2
+ [![Code Climate](https://codeclimate.com/repos/569444dfa3d810003a00313f/badges/416bae00acf65d690efe/gpa.svg)](https://codeclimate.com/repos/569444dfa3d810003a00313f/feed)
3
+ [![Test Coverage](https://codeclimate.com/repos/569444dfa3d810003a00313f/badges/416bae00acf65d690efe/coverage.svg)](https://codeclimate.com/repos/569444dfa3d810003a00313f/coverage)
4
+
5
+ # Zuora REST API: Ruby Client
6
+
7
+ This library implements a Ruby client wrapping Zuora's REST API.
8
+ - **Validations** (via `activemodel`)
9
+ - **HTTP** (via `faraday`)
10
+ - **Serialization**: To/from Ruby / JSON
11
+
12
+ ## Quickstart
13
+ ```ruby
14
+ # Connect
15
+ client = Zuora::Client.new(username, password)
16
+ # Create a model
17
+ account = Zuora::Models::Account.new(...)
18
+ # Validate
19
+ account.valid? # true or false, check account.errors if false
20
+ # Select a serializer (snake_case -> lowerCamelCase)
21
+ serializer = Zuora::Serializers::Attribute
22
+ # Low level HTTP API
23
+ client.get('/rest/v1/accounts', serializer.serialze account)
24
+ # High Level Resource API
25
+ Zuora::Resources::Account.create! client, account, serializer
26
+ ```
27
+ ## Key Features & Concepts
28
+ 1. ***Client:*** Create a client by providing username and password.
29
+ This authenticates and stores the returned session cookie
30
+ used in subsequent requests. An optional third, truthy value enables Sandbox instead of production mode.
31
+
32
+ 2. ***HTTP:***
33
+ Use `client.<get|post|put>(url, params)` to make HTTP requests via the authenticated client. Request and response body will be converted to/from Ruby via `farraday_middleware`.
34
+
35
+ 3. ***Models:*** Ruby interface for constructing valid Zuora objects.
36
+ - `account = Zuora::Models::Account.new(:attribute => 'name')`
37
+ - `account.valid?` a boolean indicating validity
38
+ - `account.errors` a hash of error message(s)
39
+ - `account.attributes` an array of attribute names
40
+
41
+ 4. **Serializers:** Recursive data transformations for mapping between formats; a Ruby -> JSON serializer is included; `snake_case` attributes are transformed into JSON `lowerCamelCase` recursively in a nested structure.
42
+ - ex. `Zuora::Serializers::Attribute.serialize account`
43
+
44
+
45
+ 5. **Resources:** Wraps Zuora REST API endpoints. Hand a valid model and (optionally) a serializer to a Resource to trigger a request. Request will be made for valid models only. An exception will be raised if the model is invalid. Otherwise, a `Farraday::Response` object will be returned (responding to `.status`, `.headers`, and `.body`).
46
+
47
+ 6. **Factories:** Factories are set up for easily constructing Zuora requests in development (via `factory_girl`)
48
+ ```ruby
49
+ account = create :account, :credit_card => create(:credit_card),
50
+ :sold_to_contact => create(:contact),
51
+ :bill_to_contact => create(:contact)
52
+
53
+ account.valid? # => true
54
+ ```
55
+ 7. **Test coverage:** Unit and integration specs coverage via `rspec`. Coming soon: HTTP response caching (using `VCR`)
56
+
57
+ ## Models
58
+ Models implement (recursive, nested) Zuora validations using `ActiveModel::Model` and soon, dirty attribute tracking via `ActiveModel::Dirty`
59
+ * Account
60
+ * CardHolder
61
+ * Contact
62
+ * PaymentMethod::CreditCard
63
+ * RatePlan
64
+ * RatePlanCharge
65
+ * Subscription
66
+ * Tier
67
+
68
+ ## Resources
69
+ In module `Zuora::Resources::`
70
+ * `Account.create!` **[working]**
71
+ * `Account.update!` [in progress]
72
+ * `Subscription.create!` [in progress]
73
+ * `Subscription.update!` [in progress]
74
+ * `Subscription.cancel!` [in progress]
75
+ * `PaymentMethod.update!` [in progress]
76
+
77
+ ## Examples
78
+ ### Creating an Account
79
+
80
+ ```ruby
81
+ username = 'your@username.com'
82
+ password = 'super_secure_password'
83
+
84
+ client = Zuora::Client.new(username, password, true) # true for sandbox
85
+
86
+ account = Zuora::Models::Account.new(
87
+ :name => 'Abc',
88
+ :auto_pay => true,
89
+ :currency => 'USD',
90
+ :bill_cycle_day => '0',
91
+ :payment_term => 'Net 30',
92
+ :bill_to_contact => Zuora::Models::Contact.new(
93
+ :first_name => 'Abc',
94
+ :last_name => 'Def',
95
+ :address_1 => '123 Main St',
96
+ :city => 'Palm Springs',
97
+ :state => 'FL',
98
+ :zip_code => '90210',
99
+ :country => 'US'
100
+ ),
101
+ :sold_to_contact => Zuora::Models::Contact.new(
102
+ :first_name => 'Abc',
103
+ :last_name => 'Def',
104
+ :country => 'US'
105
+ ),
106
+ :credit_card => Zuora::Models::PaymentMethod.new(
107
+ :card_type => 'Visa',
108
+ :card_number => '4111111111111111',
109
+ :expiration_month => '03',
110
+ :expiration_year => '2017',
111
+ :security_code => '122',
112
+ )
113
+ )
114
+
115
+ # Create an account in one of two ways:
116
+
117
+ serializer = Zuora::Serializers::Attribute
118
+
119
+ # Using the low-level API exposed by `Client`
120
+ response = client.post('/rest/v1/accounts', serializer.serialize accont)
121
+
122
+ # or using the higher-level resource API
123
+ response = Zuora::Resources::Accounts.create!(client, account, serializer)
124
+
125
+ # Le response
126
+
127
+ pp response
128
+
129
+ #<Faraday::Response:0x007f8033b05f08
130
+ @env=
131
+ #<struct Faraday::Env
132
+ method=:post,
133
+ body=
134
+ {"success"=>true,
135
+ "accountId"=>"2c92c0fa521b466c0152250822741a71",
136
+ "accountNumber"=>"A00000038",
137
+ "paymentMethodId"=>"2c92c0fa521b466c0152250829c81a7b"},
138
+ url=#<URI::HTTPS https://apisandbox-api.zuora.com/rest/v1/accounts>,
139
+ request=
140
+ #<struct Faraday::RequestOptions
141
+ params_encoder=nil,
142
+ proxy=nil,
143
+ bind=nil,
144
+ timeout=nil,
145
+ open_timeout=nil,
146
+ boundary=nil,
147
+ oauth=nil>,
148
+ request_headers=
149
+ {"User-Agent"=>"Faraday v0.9.2",
150
+ "Content-Type"=>"application/json",
151
+ "Cookie"=>
152
+ "ZSession=LBToVw72ZCAQLjdZ9Ksj8rx2BlP3NbgmMYwCzuf_slSJqIhMbJjdQ1T-4otbdfjUOImQ_XJOCbJgdCd7jHmGsnnJyG49NyRkI7FVKOukVQtdJssJ5n1xAXJeVjxj3qj97iiIZp697v3G2w86iCTN6kWycUlSVezBElbC8_EhScbx8YmaP4QJxXRIFHHdOQPq3IN-9ezk21Cpq3fdXn6s0fIPMU7NUFj7-kD4dcYNBAyd7i2fJVAIV31mXNBH2MuU;"},
153
+ ssl=
154
+ #<struct Faraday::SSLOptions
155
+ verify=false,
156
+ ca_file=nil,
157
+ ca_path=nil,
158
+ verify_mode=nil,
159
+ cert_store=nil,
160
+ client_cert=nil,
161
+ client_key=nil,
162
+ certificate=nil,
163
+ private_key=nil,
164
+ verify_depth=nil,
165
+ version=nil>,
166
+ parallel_manager=nil,
167
+ params=nil,
168
+ response=#<Faraday::Response:0x007f8033b05f08 ...>,
169
+ response_headers=
170
+ {"server"=>"Zuora App",
171
+ "content-type"=>"application/json;charset=utf-8",
172
+ "expires"=>"Sat, 09 Jan 2016 06:17:18 GMT",
173
+ "cache-control"=>"max-age=0, no-cache, no-store",
174
+ "pragma"=>"no-cache",
175
+ "date"=>"Sat, 09 Jan 2016 06:17:18 GMT",
176
+ "content-length"=>"165",
177
+ "connection"=>"close",
178
+ "set-cookie"=>
179
+ "ZSession=dOz9WgdPQbb9J9wzwhuR_t1j9feD4dYBUEZ_sjK6pS9KAaJtPdKN-jAivNELsaANWMJrvHW_1eLxT7XqzjLVBJKzLDJT7_0ucvzcrwNcwMW8mUGpeUhQQu_h2HzNH1kZjc1HX6pfw-BH66BafLemLIdqL75ifmglk8YuTOf_wTg54GsovkrgJCAp9zferw6pYHkZoQUXyH7zmUmmWvMAZ1ZVamhLOf1P3FrrHaw6eIiUj0ehlKvrtxB-GHIgYxh6; Path=/; Secure; HttpOnly"},
180
+ status=200>,
181
+ @on_complete_callbacks=[]>
182
+ ```
183
+ # Changelog
184
+ * **[0.1.0] Initial release**
185
+ # Roadmap
186
+ * **[0.1.1] Additional resources** See Resource list above
187
+ * **[0.2.0] Add VCR** Fast, deterministic HTTP requests and responses
188
+ * **[0.3.0] Dirty attribute tracking:** only serialize attributes that have been explicitly set. Currently, unset attributes are sent as `nil` which might override Zuora defaults.
189
+
190
+ # Commit rights
191
+ Anyone who has a patch accepted may request commit rights. Please do so inside the pull request post-merge.
192
+
193
+ # Contributors
194
+ * [John Gerhardt](https://github.com/jwg2s)
195
+ * [Shaun Robinson](https://github.com/env)
196
+
197
+ # License
198
+ MIT License. Copyright 2016 Contactually, Inc. http://contactually.com
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'bundler/setup'
5
+ require 'zuora'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'factory_girl'
15
+ FactoryGirl.definition_file_paths = ['spec/zuora/factories']
16
+ FactoryGirl.find_definitions
17
+ # Short hand factory girl syntax
18
+ include FactoryGirl::Syntax::Methods
19
+
20
+ require 'irb'
21
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ test:
2
+ post:
3
+ - bundle exec rubocop
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ # Dependencies
4
+ require 'faraday'
5
+ require 'json'
6
+ require 'active_model'
7
+ require 'active_model_serializers'
8
+ require 'active_support'
9
+
10
+ module Zuora
11
+ API_URL = 'https://api.zuora.com/rest/v1/'
12
+ SANDBOX_URL = 'https://apisandbox-api.zuora.com/rest/v1/'
13
+
14
+ STATE_ABBREVIATIONS = %w(AA AE AP AK AL AR AZ CA CO CT DC DE FL
15
+ GA GU HI IA ID IL IN KS KY LA MA MD ME
16
+ MI MN MO MS MT NC ND NE NH NJ NM NV NY
17
+ OH OK OR PA PR RI SC SD TN TX UT VA VI
18
+ VT WA WI WV WY)
19
+
20
+ CREDIT_CARD_TYPES = %w(Visa MasterCard Amex Discover)
21
+
22
+ MONTHS = %w(01 02 03 04 05 06 07 08 09 10 11 12)
23
+
24
+ PAYMENT_TERMS = ['Due Upon Receipt', 'Net 30', 'Net 60', 'Net 90']
25
+
26
+ SUBSCRIPTION_TERM_TYPES = %w(TERMED EVERGREEN)
27
+ end
28
+
29
+ require_relative 'zuora/version'
30
+ require_relative 'zuora/client'
31
+ require_relative 'zuora/models'
32
+ require_relative 'zuora/serializers'
33
+ require_relative 'zuora/resources'
@@ -0,0 +1,113 @@
1
+ # encoding: utf-8
2
+ require 'faraday'
3
+ require 'faraday_middleware'
4
+ require 'json'
5
+
6
+ module Zuora
7
+ # Unable to connect. Check username / password
8
+ ConnectionError = Class.new StandardError
9
+
10
+ # Non-success response
11
+ ErrorResponse = Class.new StandardError
12
+
13
+ class Client
14
+ attr_accessor :connection
15
+
16
+ # Creates a connection instance.
17
+ # Makes an initial HTTP request to fetch session token.
18
+ # Subsequent requests made with .get, .post, and .put
19
+ # contain the authenticated session id in their headers.
20
+ # @param [String] username
21
+ # @param [String] password
22
+ # @param [Boolean] sandbox
23
+ # @return [Zuora::Client] with .connection, .put, .post
24
+ def initialize(username, password, sandbox = false)
25
+ url = api_url sandbox
26
+
27
+ response = connection(url).post do |req|
28
+ req.url '/rest/v1/connections'
29
+ req.headers['apiAccessKeyId'] = username
30
+ req.headers['apiSecretAccessKey'] = password
31
+ req.headers['Content-Type'] = 'application/json'
32
+ end
33
+
34
+ if response.status == 200
35
+ @auth_cookie = response.headers['set-cookie'].split(' ')[0]
36
+ @connection = connection(url)
37
+ else
38
+ fail ConnectionError, response.body['reasons']
39
+ end
40
+ end
41
+
42
+ # @param [String] url - URL of request
43
+ # @return [Faraday::Response] A response, with .headers, .status & .body
44
+ def get(url)
45
+ @connection.get do |req|
46
+ req.url url
47
+ req.headers['Content-Type'] = 'application/json'
48
+ req.headers['Cookie'] = @auth_cookie
49
+ end
50
+ end
51
+
52
+ # @param [String] url - URL for HTTP POST request
53
+ # @param [Params] params - Data to be sent in request body
54
+ # @return [Faraday::Response] A response, with .headers, .status & .body
55
+ def post(url, params)
56
+ response = @connection.post do |req|
57
+ req.url url
58
+ req.headers['Content-Type'] = 'application/json'
59
+ req.headers['Cookie'] = @auth_cookie
60
+ req.body = JSON.generate params
61
+ end
62
+
63
+ response
64
+ # if response.body['success']
65
+ # return response
66
+ # else
67
+ # raise ErrorResponse.new(response)
68
+ # end
69
+ end
70
+
71
+ # @param [String] url - URL for HTTP PUT request
72
+ # @param [Params] params - Data to be sent in request body
73
+ # @return [Faraday::Response] A response, with .headers, .status & .body
74
+ def put(url, params)
75
+ response = @connection.put do |req|
76
+ req.url url
77
+ req.headers['Content-Type'] = 'application/json'
78
+ req.headers['Cookie'] = @auth_cookie
79
+ req.body = JSON.generate params
80
+ end
81
+
82
+ response
83
+ # if response.body['success']
84
+ # return response
85
+ # else
86
+ # raise ErrorResponse.new(response)
87
+ # end
88
+ end
89
+
90
+ private
91
+
92
+ # @param [String] url
93
+ # @return [Faraday::Client]
94
+ def connection(url)
95
+ Faraday.new(url, ssl: { verify: false }) do |conn|
96
+ conn.request :json
97
+ conn.response :json, content_type: /\bjson$/
98
+ conn.use :instrumentation
99
+ conn.adapter Faraday.default_adapter
100
+ end
101
+ end
102
+
103
+ # @param [Boolean] sandbox - Use the sandbox url?
104
+ # @return [String] the API url
105
+ def api_url(sandbox)
106
+ if sandbox
107
+ Zuora::SANDBOX_URL
108
+ else
109
+ Zuora::API_URL
110
+ end
111
+ end
112
+ end
113
+ end