zuora-ruby 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5415a26e1054e37bd49d820c0a79ce67afee2b23
4
- data.tar.gz: be862d326b31f2f0133fd7b19084034ca30b55bf
3
+ metadata.gz: da6c115de2be80e51003cee17072c6c06e578199
4
+ data.tar.gz: 6e48747729c511bd9fd2892eb8d97b1b9a3786a8
5
5
  SHA512:
6
- metadata.gz: 818e018dfaac566ef45e4824cc2de1c7c7910be67e89486a068b185f26a95b9b9c182c8d443b6dcb03fa5a7b6789dc487c6ccb45cef2d874fb70afd6935dbe3f
7
- data.tar.gz: 24bed325e1caab98bda4d1fa7f9492346215392c2a5710106775102a0679673b55f484802126efba89de796117fdb2d889ae031083736bd3e4ae979c12e716af
6
+ metadata.gz: c1ea116b905657af8fef4d060239583187eefa02fbceb623ce1676a083efd3a2cb46916cec0c0beed5cedab0ca802abf3ccf42aab24b1bf3d2613744ae00e713
7
+ data.tar.gz: 7bdeb438f2581dfa8dc1aa9d01c008ba8cfdad614831bf0de642ef56a3e6ecf197b81a2919082d735dc5c7c62bfcefa3c6af858210e90062482587aabfc932fd
data/.gitignore CHANGED
@@ -8,4 +8,5 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  .env
11
- .byebug_history
11
+ .byebug_history
12
+ zuora-ruby*.gem
@@ -17,6 +17,30 @@ Style/Encoding:
17
17
  AbcSize:
18
18
  Enabled: false
19
19
 
20
- MethodLength:
21
- Enabled: true
22
- Max: 25
20
+ Rails/TimeZone:
21
+ Enabled: false
22
+
23
+ Style/AlignParameters:
24
+ EnforcedStyle: with_fixed_indentation
25
+ SupportedStyles:
26
+ - with_first_parameter
27
+ - with_fixed_indentation
28
+
29
+ Style/CaseIndentation:
30
+ IndentWhenRelativeTo: end
31
+ SupportedStyles:
32
+ - case
33
+ - end
34
+ IndentOneStep: false
35
+
36
+ Style/IndentHash:
37
+ EnforcedStyle: consistent
38
+ SupportedStyles:
39
+ - special_inside_parentheses
40
+ - consistent
41
+
42
+ Style/MultilineOperationIndentation:
43
+ EnforcedStyle: indented
44
+ SupportedStyles:
45
+ - aligned
46
+ - indented
data/README.md CHANGED
@@ -5,9 +5,27 @@
5
5
  # Zuora REST API: Ruby Client
6
6
 
7
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
8
+
9
+ ### Model
10
+ A base module called `DirtyValidAttr` provides `dirty_model_attr`
11
+ * **Accessors**: attribute name provides getters and setters, as in `attr_accessor`
12
+ * **Validations** `valid: max_length(3) `
13
+ * Includes a library of predicate higher order validation functions
14
+ * **Coercions** `coerce: ->(value) { value.to_s } `
15
+ * **Type Checks** `type: String`
16
+ * **Required Attributes**: `:required: true`
17
+
18
+ ### Resource
19
+ * **HTTP requests**: that are authenticated with provided credentials
20
+ * **Zuora API endpoints**:
21
+
22
+ ### Utilities
23
+ - **Serialization**: Ruby <=> JSON serializer provided, just provide a module or class that respnds to `.serialize(hash)`
24
+
25
+ ### Development: Specs and Testing
26
+ - **Factories**: for generating sample valid and invalid data that works with the API
27
+ - **Unit**: factories for testing model valdiations
28
+ - **Integration**: Tests against or memoized (via `VCR`) HTTP responses
11
29
 
12
30
  ## Quickstart
13
31
  ```ruby
@@ -15,9 +33,6 @@ This library implements a Ruby client wrapping Zuora's REST API.
15
33
  client = Zuora::Client.new(username, password)
16
34
  # Create a model
17
35
  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
36
  serializer = Zuora::Serializers::Attribute
22
37
  # Low level HTTP API
23
38
  client.get('/rest/v1/accounts', serializer.serialze account)
@@ -33,14 +48,10 @@ used in subsequent requests. An optional third, truthy value enables Sandbox ins
33
48
  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
49
 
35
50
  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
51
+ - Documentation coming soon. In the mean time, check comments in `Zuora::Models::Dirty`.
40
52
 
41
53
  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
54
  - ex. `Zuora::Serializers::Attribute.serialize account`
43
-
44
55
 
45
56
  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
57
 
@@ -68,9 +79,9 @@ Models implement (recursive, nested) Zuora validations using `ActiveModel::Model
68
79
  ## Resources
69
80
  In module `Zuora::Resources::`
70
81
  * `Account.create!` **[working]**
71
- * `Account.update!` [in progress]
72
- * `Subscription.create!` [in progress]
73
- * `Subscription.update!` [in progress]
82
+ * `Account.update!` **[working]**
83
+ * `Subscription.create!` **[working]**
84
+ * `Subscription.update!` **[working]**
74
85
  * `Subscription.cancel!` [in progress]
75
86
  * `PaymentMethod.update!` [in progress]
76
87
 
@@ -181,11 +192,18 @@ pp response
181
192
  @on_complete_callbacks=[]>
182
193
  ```
183
194
  # 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.
195
+ * **[0.1.0 - 2016-01-12]** Initial release
196
+ * **[0.2.0] - 2016-01-14]** Models
197
+ - Refactored client to clarify logic
198
+ - Replaces `ActiveRecord::Model` and `::Validations` with a base module that provides powerful and extensible facilities for modeling remote resources in Ruby.
199
+ * required attributes, coercions, validations, type checking
200
+ * dirty attribute tracking
201
+ * extensible predicate library
202
+ - Implements fine-grained validations per Zuora spec
203
+ - Removes invalid model state paradigm used via `ActiveModel` in version 0.1.0.
204
+ - A model now performs its validations on `.new`, and will raise a detailed exception on mistyped, invalid or uncoercable data.
205
+ - Adds VCR for mocking out HTTP requests
206
+ - Adds integration specs for `Subscribe` `create!` and `update!` and `Account` `create!` and `update!`
189
207
 
190
208
  # Commit rights
191
209
  Anyone who has a patch accepted may request commit rights. Please do so inside the pull request post-merge.
@@ -23,7 +23,58 @@ module Zuora
23
23
 
24
24
  PAYMENT_TERMS = ['Due Upon Receipt', 'Net 30', 'Net 60', 'Net 90']
25
25
 
26
+ # SUBSCRIPTION
27
+
26
28
  SUBSCRIPTION_TERM_TYPES = %w(TERMED EVERGREEN)
29
+
30
+ DISCOUNT_TYPES = %w(ONETIME
31
+ RECURRING
32
+ USAGE
33
+ ONETIMERECURRING
34
+ ONETIMEUSAGE
35
+ RECURRINGUSAGE
36
+ ONETIMERECURRINGUSAG) # typo in zuora docs?
37
+
38
+ DISCOUNT_LEVELS = %w(rateplan subscription account)
39
+
40
+ LIST_PRICE_BASES = %w(Per_Billing_Period
41
+ Per_Month
42
+ Per_Week)
43
+
44
+ TRIGGER_EVENTS = %w(UCE USA UCA USD)
45
+
46
+ END_DATE_CONDITIONS = %w(Subscription_End
47
+ Fixed_Period
48
+ Specific_End_Date)
49
+
50
+ UP_TO_PERIODS = %w(Days Weeks Months Years)
51
+
52
+ BILLING_PERIODS = %w(Month
53
+ Quarter
54
+ Semi_Annual
55
+ Annual
56
+ Eighteen_Months
57
+ Two_Years
58
+ Three_Years
59
+ Five_Years
60
+ Specific_Months
61
+ Subscription_Term
62
+ Week
63
+ Specific_Weeks)
64
+
65
+ BILLING_TIMINGS = %w(IN_ADVANCE IN_ARREARS)
66
+
67
+ BILL_CYCLE_TYPES = %w(DefaultFromCustomer
68
+ SpecificDayofMonth
69
+ SubscriptionStartDay
70
+ ChargeTriggerDay
71
+ SpecificDayOfWeek)
72
+
73
+ PRICE_CHANGE_OPTIONS = %w(NoChange
74
+ SpecificPercentageValue
75
+ UseLatestProductCatalogPricing)
76
+
77
+ WEEKDAYS = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
27
78
  end
28
79
 
29
80
  require_relative 'zuora/version'
@@ -11,7 +11,7 @@ module Zuora
11
11
  ErrorResponse = Class.new StandardError
12
12
 
13
13
  class Client
14
- attr_accessor :connection
14
+ attr_reader :connection
15
15
 
16
16
  # Creates a connection instance.
17
17
  # Makes an initial HTTP request to fetch session token.
@@ -22,30 +22,19 @@ module Zuora
22
22
  # @param [Boolean] sandbox
23
23
  # @return [Zuora::Client] with .connection, .put, .post
24
24
  def initialize(username, password, sandbox = false)
25
- url = api_url sandbox
25
+ base_url = api_url sandbox
26
+ conn = connection base_url
26
27
 
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
28
+ response = auth_request conn, username, password
33
29
 
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
30
+ handle_response response, conn
40
31
  end
41
32
 
42
33
  # @param [String] url - URL of request
43
34
  # @return [Faraday::Response] A response, with .headers, .status & .body
44
35
  def get(url)
45
36
  @connection.get do |req|
46
- req.url url
47
- req.headers['Content-Type'] = 'application/json'
48
- req.headers['Cookie'] = @auth_cookie
37
+ set_request_headers! req, url
49
38
  end
50
39
  end
51
40
 
@@ -54,9 +43,7 @@ module Zuora
54
43
  # @return [Faraday::Response] A response, with .headers, .status & .body
55
44
  def post(url, params)
56
45
  response = @connection.post do |req|
57
- req.url url
58
- req.headers['Content-Type'] = 'application/json'
59
- req.headers['Cookie'] = @auth_cookie
46
+ set_request_headers! req, url
60
47
  req.body = JSON.generate params
61
48
  end
62
49
 
@@ -72,11 +59,9 @@ module Zuora
72
59
  # @param [Params] params - Data to be sent in request body
73
60
  # @return [Faraday::Response] A response, with .headers, .status & .body
74
61
  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
62
+ response = @connection.put do |request|
63
+ set_request_headers! request, url
64
+ request.body = JSON.generate params
80
65
  end
81
66
 
82
67
  response
@@ -89,6 +74,47 @@ module Zuora
89
74
 
90
75
  private
91
76
 
77
+ # Make connection attempt
78
+ # @param [Faraday::Connection] conn
79
+ # @param [String] username
80
+ # @param [String] password
81
+ def auth_request(conn, username, password)
82
+ conn.post do |request|
83
+ set_auth_request_headers! request, username, password
84
+ end
85
+ end
86
+
87
+ # Sets instance variables or throws Connection error
88
+ # @param [Faraday::Response] response
89
+ # @param [Faraday::Connection] conn
90
+ def handle_response(response, conn)
91
+ if response.status == 200
92
+ @auth_cookie = response.headers['set-cookie'].split(' ')[0]
93
+ @connection = conn
94
+ else
95
+ fail ConnectionError, response.body['reasons']
96
+ end
97
+ end
98
+
99
+ # @param [Faraday::Request] request - Faraday::Request builder
100
+ # @param [String] username - Zuora username
101
+ # @param [String] password - Zuora password
102
+ def set_auth_request_headers!(request, username, password)
103
+ request.url '/rest/v1/connections'
104
+ request.headers['apiAccessKeyId'] = username
105
+ request.headers['apiSecretAccessKey'] = password
106
+ request.headers['Content-Type'] = 'application/json'
107
+ end
108
+
109
+ # @param [Faraday::Request] request - Faraday Request builder
110
+ # @param [String] url - Relative URL for HTTP request
111
+ # @return [Nil]
112
+ def set_request_headers!(request, url)
113
+ request.url url
114
+ request.headers['Content-Type'] = 'application/json'
115
+ request.headers['Cookie'] = @auth_cookie
116
+ end
117
+
92
118
  # @param [String] url
93
119
  # @return [Faraday::Client]
94
120
  def connection(url)
@@ -1,9 +1,10 @@
1
- require_relative 'models/utils'
1
+ require_relative 'models/dirty'
2
+ require_relative 'models/validation_predicates'
2
3
 
3
- require_relative 'models/account'
4
- require_relative 'models/card_holder'
5
4
  require_relative 'models/contact'
6
5
  require_relative 'models/payment_method'
6
+ require_relative 'models/account'
7
+ require_relative 'models/card_holder'
7
8
  require_relative 'models/rate_plan'
8
9
  require_relative 'models/rate_plan_charge'
9
10
  require_relative 'models/subscription'
@@ -1,53 +1,64 @@
1
- # encoding: utf-8
2
-
3
1
  module Zuora
4
2
  module Models
5
3
  class Account
6
- include ActiveModel::Model
7
- # See http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
8
-
9
- ATTRIBUTES = :account_number,
10
- :auto_pay,
11
- :bill_to_contact,
12
- :bill_cycle_day,
13
- :crm_id,
14
- :currency,
15
- :credit_card,
16
- :name,
17
- :hpm_credit_card_payment_method_id,
18
- :notes,
19
- :invoice_template_id,
20
- :communication_profile_id,
21
- :payment_gateway,
22
- :payment_term,
23
- :sold_to_contact,
24
- :subscription
25
-
26
- attr_accessor(*ATTRIBUTES)
27
-
28
- def attributes
29
- ATTRIBUTES
30
- end
31
-
32
- Zuora::Models::Utils.validate_children self,
33
- 'contact',
34
- :bill_to_contact,
35
- :sold_to_contact
36
-
37
- validates :auto_pay,
38
- :bill_to_contact,
39
- :credit_card,
40
- :currency,
41
- :name,
42
- :payment_term,
43
- :sold_to_contact,
44
- presence: true
45
-
46
- validates :currency,
47
- length: { is: 3 }
48
-
49
- validates :payment_term,
50
- inclusion: { in: Zuora::PAYMENT_TERMS }
4
+ include DirtyValidAttr
5
+
6
+ dirty_valid_attr :account_number,
7
+ type: String
8
+
9
+ dirty_valid_attr :auto_pay,
10
+ type: Boolean,
11
+ required?: true
12
+
13
+ dirty_valid_attr :bill_to_contact,
14
+ type: Zuora::Models::Contact,
15
+ required?: true
16
+
17
+ dirty_valid_attr :bill_cycle_day,
18
+ type: String
19
+
20
+ dirty_valid_attr :crm_id,
21
+ type: String
22
+
23
+ dirty_valid_attr :currency,
24
+ type: String,
25
+ valid?: length(3)
26
+
27
+ dirty_valid_attr :credit_card,
28
+ type: Zuora::Models::PaymentMethods::CreditCard,
29
+ required?: true
30
+
31
+ dirty_valid_attr :name,
32
+ type: String
33
+
34
+ dirty_valid_attr :hpm_credit_card_payment_method_id,
35
+ type: String
36
+
37
+ dirty_valid_attr :notes,
38
+ type: String
39
+
40
+ dirty_valid_attr :invoice_template_id,
41
+ type: String
42
+
43
+ dirty_valid_attr :communication_profile_id,
44
+ type: String
45
+
46
+ dirty_valid_attr :payment_gateway,
47
+ type: String
48
+
49
+ dirty_valid_attr :payment_term,
50
+ type: String,
51
+ required?: true,
52
+ valid?: one_of(Zuora::PAYMENT_TERMS)
53
+
54
+ dirty_valid_attr :sold_to_contact,
55
+ type: Zuora::Models::Contact,
56
+ required?: true
57
+
58
+ dirty_valid_attr :subscription,
59
+ type: String
60
+
61
+ alias_method :initialize, :initialize_attributes!
51
62
  end
52
63
  end
53
64
  end