xeroizer 2.20.0 → 3.0.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 +5 -5
- data/README.md +126 -185
- data/lib/xeroizer/connection.rb +49 -0
- data/lib/xeroizer/exceptions.rb +2 -0
- data/lib/xeroizer/generic_application.rb +8 -3
- data/lib/xeroizer/http.rb +5 -80
- data/lib/xeroizer/http_response.rb +154 -0
- data/lib/xeroizer/models/bank_transaction.rb +1 -0
- data/lib/xeroizer/models/batch_payment.rb +4 -1
- data/lib/xeroizer/models/contact.rb +10 -4
- data/lib/xeroizer/models/credit_note.rb +20 -20
- data/lib/xeroizer/models/history_record.rb +72 -0
- data/lib/xeroizer/models/invoice.rb +5 -1
- data/lib/xeroizer/models/line_item.rb +4 -2
- data/lib/xeroizer/models/manual_journal.rb +2 -1
- data/lib/xeroizer/models/option.rb +1 -1
- data/lib/xeroizer/models/payroll/address.rb +53 -0
- data/lib/xeroizer/models/payroll/bank_account.rb +18 -6
- data/lib/xeroizer/models/payroll/benefit_line.rb +26 -0
- data/lib/xeroizer/models/payroll/benefit_type.rb +45 -0
- data/lib/xeroizer/models/payroll/deduction_line.rb +32 -0
- data/lib/xeroizer/models/payroll/deduction_type.rb +49 -0
- data/lib/xeroizer/models/payroll/earnings_line.rb +39 -0
- data/lib/xeroizer/models/payroll/earnings_type.rb +53 -0
- data/lib/xeroizer/models/payroll/employee.rb +30 -8
- data/lib/xeroizer/models/payroll/leave_application.rb +27 -0
- data/lib/xeroizer/models/payroll/leave_line.rb +30 -0
- data/lib/xeroizer/models/payroll/leave_period.rb +15 -0
- data/lib/xeroizer/models/payroll/pay_items.rb +22 -0
- data/lib/xeroizer/models/payroll/pay_run.rb +33 -0
- data/lib/xeroizer/models/payroll/pay_schedule.rb +40 -0
- data/lib/xeroizer/models/payroll/pay_template.rb +24 -0
- data/lib/xeroizer/models/payroll/payment_method.rb +24 -0
- data/lib/xeroizer/models/payroll/paystub.rb +44 -0
- data/lib/xeroizer/models/payroll/reimbursement_line.rb +21 -0
- data/lib/xeroizer/models/payroll/reimbursement_type.rb +22 -0
- data/lib/xeroizer/models/payroll/salary_and_wage.rb +29 -0
- data/lib/xeroizer/models/payroll/super_line.rb +40 -0
- data/lib/xeroizer/models/payroll/tax_declaration.rb +50 -0
- data/lib/xeroizer/models/payroll/time_off_line.rb +20 -0
- data/lib/xeroizer/models/payroll/time_off_type.rb +32 -0
- data/lib/xeroizer/models/payroll/work_location.rb +25 -0
- data/lib/xeroizer/models/quote.rb +76 -0
- data/lib/xeroizer/models/tax_component.rb +1 -0
- data/lib/xeroizer/oauth.rb +12 -1
- data/lib/xeroizer/oauth2.rb +82 -0
- data/lib/xeroizer/oauth2_application.rb +49 -0
- data/lib/xeroizer/payroll_application.rb +8 -3
- data/lib/xeroizer/record/base_model.rb +1 -1
- data/lib/xeroizer/record/base_model_http_proxy.rb +1 -0
- data/lib/xeroizer/record/payroll_base.rb +4 -0
- data/lib/xeroizer/record/record_association_helper.rb +4 -4
- data/lib/xeroizer/record/validators/associated_validator.rb +1 -0
- data/lib/xeroizer/record/xml_helper.rb +16 -16
- data/lib/xeroizer/response.rb +22 -17
- data/lib/xeroizer/version.rb +1 -1
- data/lib/xeroizer.rb +31 -4
- data/test/acceptance/about_creating_bank_transactions_test.rb +80 -82
- data/test/acceptance/about_creating_prepayment_test.rb +25 -30
- data/test/acceptance/about_fetching_bank_transactions_test.rb +10 -10
- data/test/acceptance/about_online_invoice_test.rb +6 -10
- data/test/acceptance/acceptance_test.rb +28 -26
- data/test/acceptance/bank_transfer_test.rb +12 -17
- data/test/acceptance/bulk_operations_test.rb +18 -16
- data/test/acceptance/connections_test.rb +11 -0
- data/test/stub_responses/bad_request.json +6 -0
- data/test/stub_responses/connections.json +16 -0
- data/test/stub_responses/expired_oauth2_token.json +6 -0
- data/test/stub_responses/generic_response_error.json +6 -0
- data/test/stub_responses/invalid_oauth2_request_token.json +6 -0
- data/test/stub_responses/invalid_tenant_header.json +6 -0
- data/test/stub_responses/object_not_found.json +6 -0
- data/test/test_helper.rb +16 -11
- data/test/unit/generic_application_test.rb +21 -10
- data/test/unit/http_test.rb +281 -9
- data/test/unit/models/address_test.rb +2 -2
- data/test/unit/models/bank_transaction_model_parsing_test.rb +2 -2
- data/test/unit/models/bank_transaction_test.rb +1 -1
- data/test/unit/models/bank_transaction_validation_test.rb +1 -1
- data/test/unit/models/contact_test.rb +2 -2
- data/test/unit/models/credit_note_test.rb +8 -8
- data/test/unit/models/employee_test.rb +4 -4
- data/test/unit/models/invoice_test.rb +12 -12
- data/test/unit/models/journal_line_test.rb +6 -6
- data/test/unit/models/journal_test.rb +4 -4
- data/test/unit/models/line_item_sum_test.rb +1 -1
- data/test/unit/models/line_item_test.rb +19 -2
- data/test/unit/models/manual_journal_test.rb +3 -3
- data/test/unit/models/organisation_test.rb +2 -2
- data/test/unit/models/payment_service_test.rb +2 -2
- data/test/unit/models/phone_test.rb +7 -7
- data/test/unit/models/prepayment_test.rb +4 -4
- data/test/unit/models/repeating_invoice_test.rb +2 -2
- data/test/unit/models/tax_rate_test.rb +2 -2
- data/test/unit/oauth2_test.rb +171 -0
- data/test/unit/oauth_config_test.rb +1 -1
- data/test/unit/record/base_model_test.rb +13 -13
- data/test/unit/record/base_test.rb +5 -4
- data/test/unit/record/block_validator_test.rb +1 -1
- data/test/unit/record/connection_test.rb +60 -0
- data/test/unit/record/model_definition_test.rb +36 -36
- data/test/unit/record/parse_params_test.rb +2 -2
- data/test/unit/record/parse_where_hash_test.rb +13 -13
- data/test/unit/record/record_association_test.rb +14 -14
- data/test/unit/record/validators_test.rb +43 -43
- data/test/unit/record_definition_test.rb +7 -7
- data/test/unit/report_definition_test.rb +7 -7
- data/test/unit/report_test.rb +20 -20
- data/test/unit_test_helper.rb +16 -0
- metadata +106 -23
- data/lib/xeroizer/models/payroll/home_address.rb +0 -24
- data/lib/xeroizer/partner_application.rb +0 -51
- data/lib/xeroizer/private_application.rb +0 -25
- data/lib/xeroizer/public_application.rb +0 -21
- data/test/unit/http_tsl_12_upgrade_test.rb +0 -31
- data/test/unit/oauth_test.rb +0 -118
- data/test/unit/private_application_test.rb +0 -20
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 1dad6570c636b1273f3569cda927b6c997a1ca6e2cf2ad629cd88c730e57935a
         | 
| 4 | 
            +
              data.tar.gz: e4d31e8555ca3a986241f5395cc2f9bba6adaba088a48bf286c552cda7008f40
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 3f8bb7c9cbbfd1792285b4c4bd3d73a24100222dc41f86eba5344e5a84b7d1ffd75ef787fbf43a130a2c17d5f8ac16204b518949872bf6107d889d0e14cb2c4c
         | 
| 7 | 
            +
              data.tar.gz: bade0622cf61bdff9e444c9ae931c9e8c985c4185c9b0c69c88f285c3869b4df8c83f29294b2087e1b9b045bb2aaa9b171340ab5f63c23d7bae6ac860eb4a769
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,14 +1,21 @@ | |
| 1 | 
            -
            Xeroizer API Library | 
| 1 | 
            +
            Xeroizer API Library
         | 
| 2 2 | 
             
            ====================
         | 
| 3 3 |  | 
| 4 4 | 
             
            **Homepage**: 		[http://waynerobinson.github.com/xeroizer](http://waynerobinson.github.com/xeroizer)
         | 
| 5 | 
            +
             | 
| 5 6 | 
             
            **Git**: 					[git://github.com/waynerobinson/xeroizer.git](git://github.com/waynerobinson/xeroizer.git)
         | 
| 7 | 
            +
             | 
| 6 8 | 
             
            **Github**: 			[https://github.com/waynerobinson/xeroizer](https://github.com/waynerobinson/xeroizer)
         | 
| 9 | 
            +
             | 
| 7 10 | 
             
            **Author**: 			Wayne Robinson [http://www.wayne-robinson.com](http://www.wayne-robinson.com)
         | 
| 11 | 
            +
             | 
| 8 12 | 
             
            **Contributors**: See Contributors section below
         | 
| 13 | 
            +
             | 
| 9 14 | 
             
            **Copyright**:    2007-2013
         | 
| 15 | 
            +
             | 
| 10 16 | 
             
            **License**:      MIT License
         | 
| 11 17 |  | 
| 18 | 
            +
             | 
| 12 19 | 
             
            Introduction
         | 
| 13 20 | 
             
            ------------
         | 
| 14 21 |  | 
| @@ -29,7 +36,7 @@ require 'rubygems' | |
| 29 36 | 
             
            require 'xeroizer'
         | 
| 30 37 |  | 
| 31 38 | 
             
            # Create client (used to communicate with the API).
         | 
| 32 | 
            -
            client = Xeroizer:: | 
| 39 | 
            +
            client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID, YOUR_OAUTH2_CLIENT_SECRET)
         | 
| 33 40 |  | 
| 34 41 | 
             
            # Retrieve list of contacts (note: all communication must be made through the client).
         | 
| 35 42 | 
             
            contacts = client.Contact.all(:order => 'Name')
         | 
| @@ -38,55 +45,6 @@ contacts = client.Contact.all(:order => 'Name') | |
| 38 45 | 
             
            Authentication
         | 
| 39 46 | 
             
            --------------
         | 
| 40 47 |  | 
| 41 | 
            -
            Xero uses OAuth to authenticate API clients. The OAuth gem (with minor modification) by John Nunemaker ([http://github.com/jnunemaker/twitter](http://github.com/jnunemaker/twitter)) is used in this library. If you've used this before, things will all seem very familar.
         | 
| 42 | 
            -
             | 
| 43 | 
            -
            There are three methods of authentication detailed below:
         | 
| 44 | 
            -
             | 
| 45 | 
            -
            ### All: Consumer Key/Secret
         | 
| 46 | 
            -
             | 
| 47 | 
            -
            All methods of authentication require your OAuth consumer key and secret. This can be found for your application
         | 
| 48 | 
            -
            in the API management console at [http://api.xero.com](http://api.xero.com).
         | 
| 49 | 
            -
             | 
| 50 | 
            -
            ### Public Applications
         | 
| 51 | 
            -
             | 
| 52 | 
            -
            Public applications use a 3-legged authorisation process. A user will need to authorise your
         | 
| 53 | 
            -
            application against each organisation that you want access to. Your application can have access
         | 
| 54 | 
            -
            to many organisations at once by going through the authorisation process for each organisation.
         | 
| 55 | 
            -
             | 
| 56 | 
            -
            The access token received will expire after 30 minutes. If you want access for longer you will need
         | 
| 57 | 
            -
            the user to re-authorise your application.
         | 
| 58 | 
            -
             | 
| 59 | 
            -
            Authentication occurs in 3 steps:
         | 
| 60 | 
            -
             | 
| 61 | 
            -
            ```ruby
         | 
| 62 | 
            -
            client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY, YOUR_OAUTH_CONSUMER_SECRET)
         | 
| 63 | 
            -
             | 
| 64 | 
            -
            # 1. Get a RequestToken from Xero. :oauth_callback is the URL the user will be redirected to
         | 
| 65 | 
            -
            #    after they have authenticated your application.
         | 
| 66 | 
            -
            #
         | 
| 67 | 
            -
            #    Note: The callback URL's domain must match that listed for your application in http://api.xero.com
         | 
| 68 | 
            -
            #          otherwise the user will not be redirected and only be shown the authentication code.
         | 
| 69 | 
            -
            request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback')
         | 
| 70 | 
            -
             | 
| 71 | 
            -
            # 2. Redirect the user to the URL specified by the RequestToken.
         | 
| 72 | 
            -
            #
         | 
| 73 | 
            -
            #    Note: example uses redirect_to method defined in Rails controllers.
         | 
| 74 | 
            -
            redirect_to request_token.authorize_url
         | 
| 75 | 
            -
             | 
| 76 | 
            -
            # 3. Exchange RequestToken for AccessToken.
         | 
| 77 | 
            -
            #    This access token will be used for all subsequent requests but it is stored within the client
         | 
| 78 | 
            -
            #    application so you don't have to record it.
         | 
| 79 | 
            -
            #
         | 
| 80 | 
            -
            #    Note: This example assumes the callback URL is a Rails action.
         | 
| 81 | 
            -
            client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
         | 
| 82 | 
            -
            ```
         | 
| 83 | 
            -
             | 
| 84 | 
            -
            You can now use the client to access the Xero API methods, e.g.
         | 
| 85 | 
            -
             | 
| 86 | 
            -
            ```ruby
         | 
| 87 | 
            -
            contacts = client.Contact.all
         | 
| 88 | 
            -
            ```
         | 
| 89 | 
            -
             | 
| 90 48 | 
             
            #### Example Rails Controller
         | 
| 91 49 |  | 
| 92 50 | 
             
            ```ruby
         | 
| @@ -97,25 +55,32 @@ class XeroSessionController < ApplicationController | |
| 97 55 | 
             
            	public
         | 
| 98 56 |  | 
| 99 57 | 
             
            		def new
         | 
| 100 | 
            -
            			 | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 58 | 
            +
            			url = @xero_client.authorize_url(
         | 
| 59 | 
            +
            				# The URL's domain must match that listed for your application
         | 
| 60 | 
            +
            				# otherwise the user will see an invalid redirect_uri error
         | 
| 61 | 
            +
            				redirect_uri: YOUR_CALLBACK_URL,
         | 
| 62 | 
            +
            				# space separated, see all scopes at https://developer.xero.com/documentation/oauth2/scopes.
         | 
| 63 | 
            +
            				# note that `offline_access` is required to get a refresh token, otherwise the access only lasts for 30 mins and cannot be refreshed.
         | 
| 64 | 
            +
            				scope: "accounting.settings.read offline_access"
         | 
| 65 | 
            +
            			)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            			redirect_to url
         | 
| 105 68 | 
             
            		end
         | 
| 106 69 |  | 
| 107 70 | 
             
            		def create
         | 
| 108 | 
            -
            			@xero_client. | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 71 | 
            +
            			token = @xero_client.authorize_from_code(
         | 
| 72 | 
            +
            				params[:code],
         | 
| 73 | 
            +
            				redirect_uri: YOUR_CALLBACK_URL
         | 
| 74 | 
            +
            			)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            			connections = @xero_client.current_connections
         | 
| 112 77 |  | 
| 113 78 | 
             
            			session[:xero_auth] = {
         | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 79 | 
            +
            				:access_token => token[:access_token],
         | 
| 80 | 
            +
            				:refresh_token => token[:refresh_token],
         | 
| 81 | 
            +
            				:tenant_id => connections[1][:tenant_id]
         | 
| 82 | 
            +
            			}
         | 
| 116 83 |  | 
| 117 | 
            -
            			session.data.delete(:request_token)
         | 
| 118 | 
            -
            			session.data.delete(:request_secret)
         | 
| 119 84 | 
             
            		end
         | 
| 120 85 |  | 
| 121 86 | 
             
            		def destroy
         | 
| @@ -125,150 +90,130 @@ class XeroSessionController < ApplicationController | |
| 125 90 | 
             
            	private
         | 
| 126 91 |  | 
| 127 92 | 
             
            		def get_xero_client
         | 
| 128 | 
            -
            			@xero_client = Xeroizer:: | 
| 93 | 
            +
            			@xero_client = Xeroizer::OAuth2Application.new(
         | 
| 94 | 
            +
            				YOUR_OAUTH2_CLIENT_ID,
         | 
| 95 | 
            +
            				YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 96 | 
            +
            			)
         | 
| 129 97 |  | 
| 130 98 | 
             
            			# Add AccessToken if authorised previously.
         | 
| 131 99 | 
             
            			if session[:xero_auth]
         | 
| 132 | 
            -
            				@xero_client. | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 100 | 
            +
            				@xero_client.tenant_id = session[:xero_auth][:tenant_id]
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            				@xero_client.authorize_from_access(session[:xero_auth][:acesss_token])
         | 
| 135 103 | 
             
            			end
         | 
| 136 104 | 
             
            		end
         | 
| 137 105 | 
             
            end
         | 
| 138 106 | 
             
            ```
         | 
| 139 107 |  | 
| 140 | 
            -
             | 
| 141 | 
            -
             | 
| 142 | 
            -
            You can store the access token/secret pair so you can access the API again without user intervention. Currently these
         | 
| 143 | 
            -
            tokens are only valid for 30 minutes and will raise a `Xeroizer::OAuth::TokenExpired` exception if you try to access
         | 
| 144 | 
            -
            the API beyond the token's expiry time.
         | 
| 108 | 
            +
            ### OAuth2 Applications
         | 
| 145 109 |  | 
| 146 | 
            -
             | 
| 110 | 
            +
            For more details, checkout Xero's [documentation](https://developer.xero.com/documentation/oauth2/auth-flow)
         | 
| 147 111 |  | 
| 112 | 
            +
            1. Generate the authorization url and redirect the user to authenticate
         | 
| 148 113 | 
             
            ```ruby
         | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
             | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
             | 
| 160 | 
            -
             | 
| 161 | 
            -
            Private applications require a private RSA keypair which is used to sign each request to the API. You can
         | 
| 162 | 
            -
            generate this keypair on Mac OSX or Linux with OpenSSL. For example:
         | 
| 163 | 
            -
             | 
| 164 | 
            -
            	openssl genrsa -out privatekey.pem 1024
         | 
| 165 | 
            -
            	openssl req -newkey rsa:1024 -x509 -key privatekey.pem -out publickey.cer -days 365
         | 
| 166 | 
            -
            	openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer
         | 
| 167 | 
            -
             | 
| 168 | 
            -
            You need to upload this `public_privatekey.pfx` file to your private application in [http://api.xero.com](http://api.xero.com).
         | 
| 169 | 
            -
             | 
| 170 | 
            -
            Example usage:
         | 
| 114 | 
            +
            client = Xeroizer::OAuth2Application.new(
         | 
| 115 | 
            +
            	YOUR_OAUTH2_CLIENT_ID,
         | 
| 116 | 
            +
            	YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 117 | 
            +
            )
         | 
| 118 | 
            +
            url = client.authorize_url(
         | 
| 119 | 
            +
            	# The URL's domain must match that listed for your application
         | 
| 120 | 
            +
            	# otherwise the user will see an invalid redirect_uri error
         | 
| 121 | 
            +
            	redirect_uri: YOUR_CALLBACK_URL,
         | 
| 122 | 
            +
            	# space separated, see all scopes at https://developer.xero.com/documentation/oauth2/scopes.
         | 
| 123 | 
            +
            	# note that `offline_access` is required to get a refresh token, otherwise the access only lasts for 30 mins and cannot be refreshed.
         | 
| 124 | 
            +
            	scope: "accounting.settings.read offline_access"
         | 
| 125 | 
            +
            )
         | 
| 171 126 |  | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
            contacts = client.Contact.all
         | 
| 127 | 
            +
            # Rails as an example
         | 
| 128 | 
            +
            redirect_to url
         | 
| 175 129 | 
             
            ```
         | 
| 176 130 |  | 
| 177 | 
            -
             | 
| 131 | 
            +
            2. In the callback route, use the provided code to retrieve an access token.
         | 
| 178 132 |  | 
| 179 133 | 
             
            ```ruby
         | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 134 | 
            +
            token = client.authorize_from_code(
         | 
| 135 | 
            +
            	params[:code],
         | 
| 136 | 
            +
            	redirect_uri: YOUR_CALLBACK_URL
         | 
| 137 | 
            +
            )
         | 
| 138 | 
            +
            token.to_hash
         | 
| 139 | 
            +
            # {
         | 
| 140 | 
            +
            #   "token_type"=>"Bearer",
         | 
| 141 | 
            +
            #   "scope"=>"accounting.transactions.read accounting.settings.read",
         | 
| 142 | 
            +
            #   :access_token=>"...",
         | 
| 143 | 
            +
            #   :refresh_token=>nil,
         | 
| 144 | 
            +
            #   :expires_at=>1615220292
         | 
| 145 | 
            +
            # }
         | 
| 182 146 |  | 
| 183 | 
            -
            #  | 
| 184 | 
            -
            client = Xeroizer::PrivateApplication.new(key, secret, nil, private_key: Rails.application.credentials.xero_private_key)
         | 
| 147 | 
            +
            # Save the access_token, refresh_token...
         | 
| 185 148 | 
             
            ```
         | 
| 186 149 |  | 
| 187 | 
            -
             | 
| 188 | 
            -
             | 
| 189 | 
            -
            Partner applications use a combination of 3-legged authorisation and private key message signing.
         | 
| 190 | 
            -
             | 
| 191 | 
            -
            Visit the [https://developer.xero.com/partner/app-partner](Becoming an app partner) page to get permission to create a partner application.
         | 
| 192 | 
            -
             | 
| 193 | 
            -
            After you have followed the instructions provided by Xero for partner applications and uploaded your certificate you can
         | 
| 194 | 
            -
            access the partner application in a similar way to public applications.
         | 
| 195 | 
            -
             | 
| 196 | 
            -
            Authentication occcurs in 3 steps:
         | 
| 197 | 
            -
             | 
| 150 | 
            +
            3. Retrieve the tenant ids.
         | 
| 198 151 | 
             
            ```ruby
         | 
| 199 | 
            -
             | 
| 200 | 
            -
             | 
| 201 | 
            -
            					YOUR_OAUTH_CONSUMER_SECRET,
         | 
| 202 | 
            -
            					"/path/to/privatekey.pem"
         | 
| 203 | 
            -
            					)
         | 
| 152 | 
            +
            connections = client.current_connections
         | 
| 153 | 
            +
            # returns Xeroizer::Connection instances
         | 
| 204 154 |  | 
| 205 | 
            -
            #  | 
| 206 | 
            -
            #    after they have authenticated your application.
         | 
| 207 | 
            -
            #
         | 
| 208 | 
            -
            #    Note: The callback URL's domain must match that listed for your application in http://api.xero.com
         | 
| 209 | 
            -
            #          otherwise the user will not be redirected and only be shown the authentication code.
         | 
| 210 | 
            -
            request_token = client.request_token(:oauth_callback => 'http://yourapp.com/oauth/callback')
         | 
| 155 | 
            +
            # Save the tenant ids
         | 
| 211 156 |  | 
| 212 | 
            -
            # 2. Redirect the user to the URL specified by the RequestToken.
         | 
| 213 | 
            -
            #
         | 
| 214 | 
            -
            #    Note: example uses redirect_to method defined in Rails controllers.
         | 
| 215 | 
            -
            redirect_to request_token.authorize_url
         | 
| 216 | 
            -
             | 
| 217 | 
            -
            # 3. Exchange RequestToken for AccessToken.
         | 
| 218 | 
            -
            #    This access token will be used for all subsequent requests but it is stored within the client
         | 
| 219 | 
            -
            #    application so you don't have to record it.
         | 
| 220 | 
            -
            #
         | 
| 221 | 
            -
            #    Note: This example assumes the callback URL is a Rails action.
         | 
| 222 | 
            -
            client.authorize_from_request(request_token.token, request_token.secret, :oauth_verifier => params[:oauth_verifier])
         | 
| 223 157 | 
             
            ```
         | 
| 224 158 |  | 
| 225 | 
            -
             | 
| 226 | 
            -
            renew this token. To be able to renew this token, you need to save the following data from this organisation's
         | 
| 227 | 
            -
            AccessToken:
         | 
| 228 | 
            -
             | 
| 159 | 
            +
            4. Use access token and tenant ids to retrieve data.
         | 
| 229 160 | 
             
            ```ruby
         | 
| 230 | 
            -
             | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 233 | 
            -
             | 
| 234 | 
            -
             | 
| 235 | 
            -
             | 
| 161 | 
            +
            client = Xeroizer::OAuth2Application.new(
         | 
| 162 | 
            +
            	YOUR_OAUTH2_CLIENT_ID,
         | 
| 163 | 
            +
            	YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 164 | 
            +
            	access_token: access_token,
         | 
| 165 | 
            +
            	tenant_id: tenant_id
         | 
| 166 | 
            +
            )
         | 
| 167 | 
            +
            # OR
         | 
| 168 | 
            +
            client = Xeroizer::OAuth2Application.new(
         | 
| 169 | 
            +
            	YOUR_OAUTH2_CLIENT_ID,
         | 
| 170 | 
            +
            	YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 171 | 
            +
            	tenant_id: tenant_id
         | 
| 172 | 
            +
            ).authorize_from_access(access_token)
         | 
| 236 173 |  | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 174 | 
            +
            # use the client
         | 
| 175 | 
            +
            client.Organisation.first
         | 
| 176 | 
            +
            ```
         | 
| 239 177 |  | 
| 240 178 | 
             
            #### AccessToken Renewal
         | 
| 241 | 
            -
             | 
| 242 | 
            -
            Renewal of an access token requires knowledge of the previous access token generated for this organisation. To renew:
         | 
| 179 | 
            +
            Renewal of an access token requires the refresh token generated for this organisation. To renew:
         | 
| 243 180 |  | 
| 244 181 | 
             
            ```ruby
         | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 182 | 
            +
            client = Xeroizer::OAuth2Application.new(
         | 
| 183 | 
            +
            	YOUR_OAUTH2_CLIENT_ID,
         | 
| 184 | 
            +
            	YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 185 | 
            +
            	access_token: access_token,
         | 
| 186 | 
            +
            	refresh_token: refresh_token,
         | 
| 187 | 
            +
            	tenant_id: tenant_id
         | 
| 188 | 
            +
            )
         | 
| 247 189 |  | 
| 248 | 
            -
             | 
| 249 | 
            -
            client.renew_access_token(access_token, access_secret, session_handle)
         | 
| 190 | 
            +
            client.renew_access_token
         | 
| 250 191 | 
             
            ```
         | 
| 192 | 
            +
            If you lose these details at any stage you can always reauthorise by redirecting the user back to the Xero OAuth gateway.
         | 
| 251 193 |  | 
| 252 | 
            -
             | 
| 253 | 
            -
             | 
| 254 | 
            -
            session.
         | 
| 194 | 
            +
            #### Custom Connections
         | 
| 195 | 
            +
            Custom Connections are a paid-for option for private M2M applications. The generated token expires and needs recreating if expired. 
         | 
| 255 196 |  | 
| 256 | 
            -
             | 
| 197 | 
            +
            ```ruby
         | 
| 198 | 
            +
            client = Xeroizer::OAuth2Application.new(
         | 
| 199 | 
            +
            	YOUR_OAUTH2_CLIENT_ID,
         | 
| 200 | 
            +
            	YOUR_OAUTH2_CLIENT_SECRET
         | 
| 201 | 
            +
            )
         | 
| 257 202 |  | 
| 258 | 
            -
             | 
| 203 | 
            +
            token = client.authorize_from_client_credentials
         | 
| 204 | 
            +
            ```
         | 
| 205 | 
            +
            You can check the status of the token with the `expires?` and `expired?` methods.
         | 
| 259 206 |  | 
| 260 | 
            -
            Once you are approved as a Xero Partner you can request unofficial documentation to do with customizing Payment Services and Branding Themes using the API. There is more info on that [here](  https://github.com/waynerobinson/xeroizer/pull/439).
         | 
| 261 207 |  | 
| 262 208 | 
             
            Retrieving Data
         | 
| 263 209 | 
             
            ---------------
         | 
| 264 210 |  | 
| 265 211 | 
             
            Each of the below record types is implemented within this library. To allow for multiple access tokens to be used at the same
         | 
| 266 | 
            -
            time in a single application, the model classes are accessed from the instance of  | 
| 267 | 
            -
            or PartnerApplication. All class-level operations occur on this singleton. For example:
         | 
| 212 | 
            +
            time in a single application, the model classes are accessed from the instance of OAuth2Application. All class-level operations occur on this singleton. For example:
         | 
| 268 213 |  | 
| 269 214 | 
             
            ```ruby
         | 
| 270 | 
            -
            xero = Xeroizer:: | 
| 271 | 
            -
            xero.authorize_from_access(session[:xero_auth][:access_token] | 
| 215 | 
            +
            xero = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID, YOUR_OAUTH2_CLIENT_SECRET, tenant_id: tenant_id)
         | 
| 216 | 
            +
            xero.authorize_from_access(session[:xero_auth][:access_token])
         | 
| 272 217 |  | 
| 273 218 | 
             
            contacts = xero.Contact.all(:order => 'Name')
         | 
| 274 219 |  | 
| @@ -648,8 +593,8 @@ You can set this option when initializing an application: | |
| 648 593 |  | 
| 649 594 | 
             
            ```ruby
         | 
| 650 595 | 
             
            # Sleep for 2 seconds every time the rate limit is exceeded.
         | 
| 651 | 
            -
            client = Xeroizer:: | 
| 652 | 
            -
                                                      | 
| 596 | 
            +
            client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
         | 
| 597 | 
            +
                                                     YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 653 598 | 
             
                                                     :rate_limit_sleep => 2)
         | 
| 654 599 | 
             
            ```
         | 
| 655 600 |  | 
| @@ -666,8 +611,8 @@ You can set this option when initializing an application: | |
| 666 611 |  | 
| 667 612 | 
             
            ```ruby
         | 
| 668 613 | 
             
            # Sleep for 1 second and retry up to 3 times when Xero claims the nonce was used.
         | 
| 669 | 
            -
            client = Xeroizer:: | 
| 670 | 
            -
                                                      | 
| 614 | 
            +
            client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
         | 
| 615 | 
            +
                                                     YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 671 616 | 
             
                                                     :nonce_used_max_attempts => 3)
         | 
| 672 617 | 
             
            ```
         | 
| 673 618 |  | 
| @@ -679,8 +624,8 @@ You can add an optional parameter to the Xeroizer Application initialization, to | |
| 679 624 |  | 
| 680 625 | 
             
            ```ruby
         | 
| 681 626 | 
             
            XeroLogger = Logger.new('log/xero.log', 'weekly')
         | 
| 682 | 
            -
            client = Xeroizer:: | 
| 683 | 
            -
                                                      | 
| 627 | 
            +
            client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
         | 
| 628 | 
            +
                                                     YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 684 629 | 
             
                                                     :logger => XeroLogger)
         | 
| 685 630 | 
             
            ```
         | 
| 686 631 |  | 
| @@ -692,7 +637,7 @@ time Xeroizer makes an HTTP request, which is potentially useful for both | |
| 692 637 | 
             
            throttling and logging:
         | 
| 693 638 |  | 
| 694 639 | 
             
            ```ruby
         | 
| 695 | 
            -
            Xeroizer:: | 
| 640 | 
            +
            Xeroizer::OAuth2Application.new(
         | 
| 696 641 | 
             
              credentials[:key], credentials[:secret],
         | 
| 697 642 | 
             
              before_request: ->(request) { puts "Hitting this URL: #{request.url}" },
         | 
| 698 643 | 
             
              after_request: ->(request, response) { puts "Got this response: #{response.code}" },
         | 
| @@ -711,8 +656,8 @@ By default, the API accepts unit prices (UnitAmount) to two decimals places. If | |
| 711 656 |  | 
| 712 657 |  | 
| 713 658 | 
             
            ```ruby
         | 
| 714 | 
            -
            client = Xeroizer:: | 
| 715 | 
            -
                                                      | 
| 659 | 
            +
            client = Xeroizer::OAuth2Application.new(YOUR_OAUTH2_CLIENT_ID,
         | 
| 660 | 
            +
                                                     YOUR_OAUTH2_CLIENT_SECRET,
         | 
| 716 661 | 
             
                                                     :unitdp => 4)
         | 
| 717 662 | 
             
            ```
         | 
| 718 663 |  | 
| @@ -721,23 +666,19 @@ This option adds the unitdp=4 query string parameter to all requests for models | |
| 721 666 | 
             
            Tests
         | 
| 722 667 | 
             
            -----
         | 
| 723 668 |  | 
| 724 | 
            -
             | 
| 725 | 
            -
             | 
| 726 | 
            -
            Once you have created your Private App, set these environment variables:
         | 
| 727 | 
            -
            ```
         | 
| 728 | 
            -
            EXPORT CONSUMER_KEY="your private app's consumer key"
         | 
| 729 | 
            -
            EXPORT CONSUMER_SECRET="your private app's consumer secret"
         | 
| 730 | 
            -
            EXPORT PRIVATE_KEY_PATH="the path to your private app's private key"
         | 
| 731 | 
            -
            ```
         | 
| 669 | 
            +
            OAuth2 Tests
         | 
| 732 670 |  | 
| 733 | 
            -
             | 
| 671 | 
            +
            The tests within the repository can be run by setting up a [OAuth2 App](https://developer.xero.com/documentation/guides/oauth2/auth-flow/).  You can create a Private App in the [developer portal](https://developer.xero.com/myapps/), it's suggested that you create it against the [Demo Company (AU)](https://developer.xero.com/documentation/getting-started/development-accounts). Demo Company expires after 28 days, so you will need to reset it and re-connect to it if your Demo Company has expired. Make sure you create the Demo Company in Australia region.
         | 
| 734 672 |  | 
| 735 | 
            -
            Then run the tests
         | 
| 736 673 | 
             
            ```
         | 
| 674 | 
            +
            export XERO_CLIENT_ID="asd"
         | 
| 675 | 
            +
            export XERO_CLIENT_SECRET="asdfg"
         | 
| 676 | 
            +
            export XERO_ACCESS_TOKEN="sadfsdf"
         | 
| 677 | 
            +
            export XERO_TENANT_ID="asdfasdfasdfasd"
         | 
| 678 | 
            +
             | 
| 737 679 | 
             
            rake test
         | 
| 738 680 | 
             
            ```
         | 
| 739 681 |  | 
| 740 | 
            -
             | 
| 741 682 | 
             
            ### Contributors
         | 
| 742 683 | 
             
            Xeroizer was inspired by the https://github.com/tlconnor/xero_gateway gem created by Tim Connor
         | 
| 743 684 | 
             
            and Nik Wakelin and portions of the networking and authentication code are based completely off
         | 
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            module Xeroizer
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                attr_accessor :attributes
         | 
| 4 | 
            +
                attr_reader :parent
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                class << self
         | 
| 7 | 
            +
                  def current_connections(client)
         | 
| 8 | 
            +
                    response = do_request(client)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    if response.success?
         | 
| 11 | 
            +
                      JSON.parse(response.plain_body).map do |connection_json|
         | 
| 12 | 
            +
                        build(connection_json, client)
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                    else
         | 
| 15 | 
            +
                      raise Xeroizer::OAuth::TokenInvalid, response.plain_body
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def build(attributes, parent)
         | 
| 20 | 
            +
                    record = new(parent)
         | 
| 21 | 
            +
                    record.attributes = attributes
         | 
| 22 | 
            +
                    record
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  private
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def do_request(client)
         | 
| 28 | 
            +
                    client.get('https://api.xero.com/connections')
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def initialize(parent)
         | 
| 33 | 
            +
                  @parent = parent
         | 
| 34 | 
            +
                  @attributes = {}
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def method_missing(name, *_args)
         | 
| 38 | 
            +
                  @attributes.send(:[], name.to_s.camelcase(:lower))
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def delete
         | 
| 42 | 
            +
                  parent.delete("https://api.xero.com/connections/#{id}")
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def to_h
         | 
| 46 | 
            +
                  attributes.transform_keys(&:underscore)
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         | 
    
        data/lib/xeroizer/exceptions.rb
    CHANGED
    
    
| @@ -6,9 +6,11 @@ module Xeroizer | |
| 6 6 | 
             
                include Http
         | 
| 7 7 | 
             
                extend Record::ApplicationHelper
         | 
| 8 8 |  | 
| 9 | 
            -
                attr_reader :client, : | 
| 9 | 
            +
                attr_reader :client, :logger, :rate_limit_sleep, :rate_limit_max_attempts,
         | 
| 10 10 | 
             
                            :default_headers, :unitdp, :before_request, :after_request, :around_request, :nonce_used_max_attempts
         | 
| 11 11 |  | 
| 12 | 
            +
                attr_accessor :xero_url
         | 
| 13 | 
            +
             | 
| 12 14 | 
             
                extend Forwardable
         | 
| 13 15 | 
             
                def_delegators :client, :access_token
         | 
| 14 16 |  | 
| @@ -26,6 +28,7 @@ module Xeroizer | |
| 26 28 | 
             
                record :ExpenseClaim
         | 
| 27 29 | 
             
                record :Invoice
         | 
| 28 30 | 
             
                record :InvoiceReminder
         | 
| 31 | 
            +
                record :HistoryRecord
         | 
| 29 32 | 
             
                record :OnlineInvoice
         | 
| 30 33 | 
             
                record :Item
         | 
| 31 34 | 
             
                record :Journal
         | 
| @@ -37,6 +40,7 @@ module Xeroizer | |
| 37 40 | 
             
                record :Prepayment
         | 
| 38 41 | 
             
                record :Overpayment
         | 
| 39 42 | 
             
                record :PurchaseOrder
         | 
| 43 | 
            +
                record :Quote
         | 
| 40 44 | 
             
                record :Receipt
         | 
| 41 45 | 
             
                record :RepeatingInvoice
         | 
| 42 46 | 
             
                record :Schedule
         | 
| @@ -63,7 +67,8 @@ module Xeroizer | |
| 63 67 | 
             
                  # @see PublicApplication
         | 
| 64 68 | 
             
                  # @see PrivateApplication
         | 
| 65 69 | 
             
                  # @see PartnerApplication
         | 
| 66 | 
            -
                  def initialize( | 
| 70 | 
            +
                  def initialize(client, options = {})
         | 
| 71 | 
            +
                    raise Xeroizer::InvalidClientError.new unless [OAuth, OAuth2].member?(client.class)
         | 
| 67 72 | 
             
                    @xero_url = options[:xero_url] || "https://api.xero.com/api.xro/2.0"
         | 
| 68 73 | 
             
                    @rate_limit_sleep = options[:rate_limit_sleep] || false
         | 
| 69 74 | 
             
                    @rate_limit_max_attempts = options[:rate_limit_max_attempts] || 5
         | 
| @@ -72,7 +77,7 @@ module Xeroizer | |
| 72 77 | 
             
                    @before_request = options.delete(:before_request)
         | 
| 73 78 | 
             
                    @after_request = options.delete(:after_request)
         | 
| 74 79 | 
             
                    @around_request = options.delete(:around_request)
         | 
| 75 | 
            -
                    @client =  | 
| 80 | 
            +
                    @client = client
         | 
| 76 81 | 
             
                    @logger = options[:logger] || false
         | 
| 77 82 | 
             
                    @unitdp = options[:unitdp] || 2
         | 
| 78 83 | 
             
                  end
         |