Zuora REST API: Ruby Client
This library implements a Ruby client wrapping Zuora's REST API.
Model
A base module called DirtyValidAttr provides dirty_model_attr
- Accessors: attribute name provides getters and setters, as in
attr_accessor - Validations
valid: max_length(3)- Includes a library of predicate higher order validation functions
- Coercions
coerce: ->(value) { value.to_s } - Type Checks
type: String - Required Attributes:
:required: true
Resource
- HTTP requests: that are authenticated with provided credentials
- Zuora API endpoints:
Utilities
- Serialization: Ruby <=> JSON serializer provided, just provide a module or class that respnds to
.serialize(hash)
Development: Specs and Testing
- Factories: for generating sample valid and invalid data that works with the API
- Unit: factories for testing model valdiations
- Integration: Tests against or memoized (via
VCR) HTTP responses
Quickstart
# Connect
client = Zuora::Client.new(username, password)
# Create a model
account = Zuora::Models::Account.new(...)
serializer = Zuora::Serializers::Attribute
# Low level HTTP API
client.get('/rest/v1/accounts', serializer.serialze account)
# High Level Resource API
Zuora::Resources::Account.create! client, account, serializer
Key Features & Concepts
Client: Create a client by providing username and password. This authenticates and stores the returned session cookie used in subsequent requests. An optional third, truthy value enables Sandbox instead of production mode.
HTTP: 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 viafarraday_middleware.Models: Ruby interface for constructing valid Zuora objects.
- Documentation coming soon. In the mean time, check comments in
Zuora::Models::Dirty.
- Documentation coming soon. In the mean time, check comments in
Serializers: Recursive data transformations for mapping between formats; a Ruby -> JSON serializer is included;
snake_caseattributes are transformed into JSONlowerCamelCaserecursively in a nested structure.- ex.
Zuora::Serializers::Attribute.serialize account
- ex.
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::Responseobject will be returned (responding to.status,.headers, and.body).Factories: Factories are set up for easily constructing Zuora requests in development (via
factory_girl)account = create :account, :credit_card => create(:credit_card), :sold_to_contact => create(:contact), :bill_to_contact => create(:contact)
account.valid? # => true
7. **Test coverage:** Unit and integration specs coverage via `rspec`. Coming soon: HTTP response caching (using `VCR`)
## Models
Models implement (recursive, nested) Zuora validations using `ActiveModel::Model` and soon, dirty attribute tracking via `ActiveModel::Dirty`
* Account
* CardHolder
* Contact
* PaymentMethod::CreditCard
* RatePlan
* RatePlanCharge
* Subscription
* Tier
## Resources
In module `Zuora::Resources::`
* `Account.create!` **[working]**
* `Account.update!` **[working]**
* `Subscription.create!` **[working]**
* `Subscription.update!` **[working]**
* `Subscription.cancel!` [in progress]
* `PaymentMethod.update!` [in progress]
## Examples
### Creating an Account
```ruby
username = '[email protected]'
password = 'super_secure_password'
client = Zuora::Client.new(username, password, true) # true for sandbox
account = Zuora::Models::Account.new(
:name => 'Abc',
:auto_pay => true,
:currency => 'USD',
:bill_cycle_day => '0',
:payment_term => 'Net 30',
:bill_to_contact => Zuora::Models::Contact.new(
:first_name => 'Abc',
:last_name => 'Def',
:address_1 => '123 Main St',
:city => 'Palm Springs',
:state => 'FL',
:zip_code => '90210',
:country => 'US'
),
:sold_to_contact => Zuora::Models::Contact.new(
:first_name => 'Abc',
:last_name => 'Def',
:country => 'US'
),
:credit_card => Zuora::Models::PaymentMethod.new(
:card_type => 'Visa',
:card_number => '4111111111111111',
:expiration_month => '03',
:expiration_year => '2017',
:security_code => '122',
)
)
# Create an account in one of two ways:
serializer = Zuora::Serializers::Attribute
# Using the low-level API exposed by `Client`
response = client.post('/rest/v1/accounts', serializer.serialize accont)
# or using the higher-level resource API
response = Zuora::Resources::Accounts.create!(client, account, serializer)
# Le response
pp response
#<Faraday::Response:0x007f8033b05f08
@env=
#<struct Faraday::Env
method=:post,
body=
{"success"=>true,
"accountId"=>"2c92c0fa521b466c0152250822741a71",
"accountNumber"=>"A00000038",
"paymentMethodId"=>"2c92c0fa521b466c0152250829c81a7b"},
url=#<URI::HTTPS https://apisandbox-api.zuora.com/rest/v1/accounts>,
request=
#<struct Faraday::RequestOptions
params_encoder=nil,
proxy=nil,
bind=nil,
timeout=nil,
open_timeout=nil,
boundary=nil,
oauth=nil>,
request_headers=
{"User-Agent"=>"Faraday v0.9.2",
"Content-Type"=>"application/json",
"Cookie"=>
"ZSession=LBToVw72ZCAQLjdZ9Ksj8rx2BlP3NbgmMYwCzuf_slSJqIhMbJjdQ1T-4otbdfjUOImQ_XJOCbJgdCd7jHmGsnnJyG49NyRkI7FVKOukVQtdJssJ5n1xAXJeVjxj3qj97iiIZp697v3G2w86iCTN6kWycUlSVezBElbC8_EhScbx8YmaP4QJxXRIFHHdOQPq3IN-9ezk21Cpq3fdXn6s0fIPMU7NUFj7-kD4dcYNBAyd7i2fJVAIV31mXNBH2MuU;"},
ssl=
#<struct Faraday::SSLOptions
verify=false,
ca_file=nil,
ca_path=nil,
verify_mode=nil,
cert_store=nil,
client_cert=nil,
client_key=nil,
certificate=nil,
private_key=nil,
verify_depth=nil,
version=nil>,
parallel_manager=nil,
params=nil,
response=#<Faraday::Response:0x007f8033b05f08 ...>,
response_headers=
{"server"=>"Zuora App",
"content-type"=>"application/json;charset=utf-8",
"expires"=>"Sat, 09 Jan 2016 06:17:18 GMT",
"cache-control"=>"max-age=0, no-cache, no-store",
"pragma"=>"no-cache",
"date"=>"Sat, 09 Jan 2016 06:17:18 GMT",
"content-length"=>"165",
"connection"=>"close",
"set-cookie"=>
"ZSession=dOz9WgdPQbb9J9wzwhuR_t1j9feD4dYBUEZ_sjK6pS9KAaJtPdKN-jAivNELsaANWMJrvHW_1eLxT7XqzjLVBJKzLDJT7_0ucvzcrwNcwMW8mUGpeUhQQu_h2HzNH1kZjc1HX6pfw-BH66BafLemLIdqL75ifmglk8YuTOf_wTg54GsovkrgJCAp9zferw6pYHkZoQUXyH7zmUmmWvMAZ1ZVamhLOf1P3FrrHaw6eIiUj0ehlKvrtxB-GHIgYxh6; Path=/; Secure; HttpOnly"},
status=200>,
@on_complete_callbacks=[]>
Changelog
- [0.1.0 - 2016-01-12] Initial release
- [0.2.0] - 2016-01-14] Models
- Refactored client to clarify logic
- Replaces
ActiveRecord::Modeland::Validationswith a base module that provides powerful and extensible facilities for modeling remote resources in Ruby.- required attributes, coercions, validations, type checking
- dirty attribute tracking
- extensible predicate library
- Implements fine-grained validations per Zuora spec
- Removes invalid model state paradigm used via
ActiveModelin version 0.1.0. - A model now performs its validations on
.new, and will raise a detailed exception on mistyped, invalid or uncoercable data. - Adds VCR for mocking out HTTP requests
- Adds integration specs for
Subscribecreate!andupdate!andAccountcreate!andupdate!
Commit rights
Anyone who has a patch accepted may request commit rights. Please do so inside the pull request post-merge.
Contributors
License
MIT License. Copyright 2016 Contactually, Inc. http://contactually.com