zuora-ruby 0.1.0 → 0.2.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.
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