aqumulate_api 0.2.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: 44bb14b6c3ff2fabfa1d7cebc37a371a971e994e
4
+ data.tar.gz: 74bc376608683f8f889a79148ed92c9361ae67bb
5
+ SHA512:
6
+ metadata.gz: a8e0758e8d98a2c5581081fb6d92d78303c7885ead57e9214cb381ff2ef26a0ae98f97e81c27c971e5550e688388bf86569dedf66a8f13533eef6ee6d89414d2
7
+ data.tar.gz: e51349a1f25da4a3822409b025dad880c8f54497021087dde2785a19b57eb474b8198f70f649840c08455e94a5cca72bab129a5ec5394e6b191fb620985ca765
@@ -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
+ .idea/
11
+ .ruby-*
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at kevin@kpheasey.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in aqumulate_api.gemspec
4
+ gemspec
@@ -0,0 +1,36 @@
1
+ # AqumulateAPI
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/aqumulate_api`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'aqumulate_api'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install aqumulate_api
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/aqumulate_api. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'aqumulate_api/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'aqumulate_api'
8
+ spec.version = AqumulateAPI::VERSION
9
+ spec.authors = ['Kevin Pheasey']
10
+ spec.email = ['kevin@kpheasey.com']
11
+
12
+ spec.summary = %q{Aqumulate API wrapper}
13
+ spec.description = %q{Aqumulate API wrapper with easy to use adapter classes.}
14
+ spec.homepage = 'https://github.com/JoelBeasley/autotask_api'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.13'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+
26
+ spec.add_dependency 'httparty', '~> 0.13'
27
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "aqumulate_api"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,24 @@
1
+ require 'net/http'
2
+ require 'httparty'
3
+
4
+ require 'aqumulate_api/agg_account'
5
+ require 'aqumulate_api/agg_advisor'
6
+ require 'aqumulate_api/configuration'
7
+ require 'aqumulate_api/errors'
8
+ require 'aqumulate_api/session'
9
+ require 'aqumulate_api/version'
10
+
11
+ require 'aqumulate_api/entity'
12
+ require 'aqumulate_api/entities/advisor'
13
+ require 'aqumulate_api/entities/account_balance'
14
+ require 'aqumulate_api/entities/position'
15
+ require 'aqumulate_api/entities/account'
16
+ require 'aqumulate_api/entities/fetch_parameter'
17
+ require 'aqumulate_api/entities/login_parameter'
18
+ require 'aqumulate_api/entities/account_data'
19
+ require 'aqumulate_api/entities/financial_institution'
20
+ require 'aqumulate_api/entities/transaction'
21
+
22
+ module AqumulateAPI
23
+
24
+ end
@@ -0,0 +1,25 @@
1
+ module AqumulateAPI
2
+ class AggAccount
3
+ RESOURCE = 'AggAccount'
4
+ end
5
+
6
+ AggAccount.singleton_class.class_eval do
7
+ methods = {
8
+ advisor_harvest_accounts: { method: 'AdvisorHarvestAccounts' },
9
+ advisor_harvest_account_status_check: { method: 'AdvisorHarvestAccountStatusCheck' },
10
+ advisor_get_aggregate_account: { method: 'AdvisorGetAggregateAccount' },
11
+ advisor_agg_get_account_for_fi: { method: 'AdvisorAggGetAccountForFI' },
12
+ link_account_to_client: { method: 'LinkAccountToClient' },
13
+ advisor_get_transactions_by_date: { method: 'AdvisorGetTransactionsByDate' },
14
+ advisor_search_financial_institution: { method: 'AdvisorSearchFinancialInstitution' },
15
+ advisor_get_financial_institution: { method: 'AdvisorGetFinancialInstitution' },
16
+ advisor_add_account: { method: 'AdvisorAddAccount' }
17
+ }
18
+
19
+ methods.each do |method, definition|
20
+ define_method(method) do |body = {}|
21
+ AqumulateAPI.session.api_request("#{self::RESOURCE}/#{definition[:method]}", body)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ module AqumulateAPI
2
+ class AggAdvisor
3
+ RESOURCE = 'AggAdvisor'
4
+ end
5
+
6
+ AggAdvisor.singleton_class.class_eval do
7
+ methods = {
8
+ get_advisors: { method: 'GetAdvisors' },
9
+ add_advisor: { method: 'AddAdvisor' },
10
+ advisor_sign_on: { method: 'AdvisorSignOn' },
11
+ get_advisor_by_id: { method: 'GetAdvisorById' },
12
+ update_advisor: { method: 'UpdateAdvisor' },
13
+ delete_advisor: { method: 'DeleteAdvisor' },
14
+ }
15
+
16
+ methods.each do |method, definition|
17
+ define_method(method) do |body = {}|
18
+ AqumulateAPI.session.api_request("#{self::RESOURCE}/#{definition[:method]}", body)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ module AqumulateAPI
2
+
3
+ class << self
4
+ attr_accessor :config
5
+ end
6
+
7
+ def self.config
8
+ @configuration ||= Configuration.new
9
+ end
10
+
11
+ def self.configure
12
+ yield config
13
+ end
14
+
15
+ class Configuration
16
+ attr_accessor :grant_type, :password, :url, :username, :debug, :timeout
17
+
18
+ def initialize
19
+ @grant_type = 'password'
20
+ @url = 'https://advagg.aqumulate.com'
21
+ @debug = false
22
+ @timeout = 300
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,64 @@
1
+ module AqumulateAPI
2
+ class Account < Entity
3
+
4
+ ATTR_MAP = {
5
+ id: 'AcctId',
6
+ login_account_id: 'FILoginAcctId',
7
+ added_by: 'AccountAddedBy',
8
+ financial_institution_id: 'FIId',
9
+ financial_institution_name: 'FIName',
10
+ account_type: 'AccountType',
11
+ account_type_extended: 'ExtendedAccountType',
12
+ nick_name: 'NickNameAtFI',
13
+ update_error_code: 'UpdateErrorCode',
14
+ retirement_status: 'RetirementStatus',
15
+ instrument: 'Instrument',
16
+ ownership: 'AccountOwnership',
17
+ adv_access: 'AdvAccess',
18
+ tracking_code: 'TrackingCode',
19
+ last_update_status_code: 'LastUpdateStatusCode',
20
+ last_update_status_msg: 'LastUpdateStatusMsg',
21
+ last_update_status_msg_fi: 'LastUpdateStatusMessageAtFI',
22
+ last_update_attempt: 'LastUpdateAttempt',
23
+ last_update: 'LastSuccessfulUpdate'
24
+ }
25
+
26
+ SOURCE_ASSOCIATIONS = [
27
+ { key: 'AccountBalances', class: AccountBalance, attr: :account_balances },
28
+ { key: 'Positions', class: Position, attr: :positions }
29
+ ]
30
+
31
+ attr_accessor :id, :login_account_id, :added_by, :financial_institution_id, :financial_institution_name,
32
+ :account_type, :account_type_extended, :nick_name, :update_error_code, :retirement_status,
33
+ :instrument, :ownership, :adv_access, :tracking_code, :last_update_status_code,
34
+ :last_update_status_msg, :last_update_status_msg_fi, :last_update_attempt, :last_update,
35
+ :account_balances, :positions
36
+
37
+ def self.fetch(advisor, fi_id = nil)
38
+ body = { 'SessionId' => advisor.session_id }
39
+
40
+ if fi_id.nil?
41
+ response = AggAccount.advisor_get_aggregate_account(body)
42
+ else
43
+ body['FIId'] = fi_id
44
+ response = AggAccount.advisor_agg_get_account_for_fi(body)
45
+ end
46
+ return [] unless response.has_key?('Accounts')
47
+
48
+ response['Accounts'].map { |source| from_source(source) }
49
+ end
50
+
51
+ def account_balances
52
+ @account_balances ||= []
53
+ end
54
+
55
+ def positions
56
+ @positions ||= []
57
+ end
58
+
59
+ def financial_institution
60
+ @financial_institution ||= FinancialInstitution.find(financial_institution_id)
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ module AqumulateAPI
2
+ class AccountBalance < Entity
3
+
4
+ ATTR_MAP = {
5
+ type: 'AcctBalType',
6
+ amount: 'AcctBalAmt',
7
+ currency: 'CurCode'
8
+ }
9
+
10
+ attr_accessor :type, :amount, :currency
11
+
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module AqumulateAPI
2
+ class AccountData < Entity
3
+
4
+ ATTR_MAP = {
5
+ id: 'AcctTypeId',
6
+ group: 'AcctGroup',
7
+ name: 'AcctName',
8
+ type: 'AcctType',
9
+ ext_type: 'ExtAcctName'
10
+ }
11
+
12
+ attr_accessor :id, :group, :name, :type, :ext_type
13
+
14
+ end
15
+ end
@@ -0,0 +1,94 @@
1
+ module AqumulateAPI
2
+ class Advisor < Entity
3
+
4
+ ATTR_MAP = {
5
+ user_id: 'UserId',
6
+ password: 'Password',
7
+ first_name: 'FirstName',
8
+ last_name: 'LastName',
9
+ email: 'Email',
10
+ phone: 'Phone',
11
+ ssn: 'SSN',
12
+ address_1: 'Address1',
13
+ address_2: 'Address2',
14
+ city: 'City',
15
+ state: 'StateProvidence',
16
+ zip: 'ZipCode',
17
+ country: 'Country'
18
+ }
19
+
20
+ attr_accessor :user_id, :password, :first_name, :last_name, :email, :phone, :ssn, :address_1, :address_2, :city,
21
+ :state, :zip, :country, :persisted
22
+
23
+ def self.find(user_id)
24
+ response = AggAdvisor.get_advisor_by_id({ 'UserId' => user_id })
25
+ AqumulateAPI.session.check_raise_request_error(response)
26
+
27
+ advisor = from_source(response)
28
+ advisor.user_id = user_id
29
+
30
+ return advisor
31
+ end
32
+
33
+ def self.all
34
+ response = AggAdvisor.get_advisors
35
+ return [] unless response.has_key?('AdvisorList')
36
+ response['AdvisorList'].map { |source| from_source(source) }
37
+ end
38
+
39
+ def session_id(password = self.password)
40
+ @session_id ||= AggAdvisor.advisor_sign_on('UserId' => user_id, 'Password' => password)['SessionId']
41
+ end
42
+
43
+ def save
44
+ if !persisted
45
+ create
46
+ else
47
+ update
48
+ end
49
+ end
50
+
51
+ def destroy
52
+ AggAdvisor.delete_advisor({ 'UserID' => user_id })
53
+ return true
54
+ end
55
+
56
+ def accounts(fi_id = nil)
57
+ Account.fetch(self, fi_id)
58
+ end
59
+
60
+ def add_account(fi_id, login_parameters, fetch_parameters)
61
+ AggAccount.advisor_add_account(
62
+ {
63
+ 'SessionId' => session_id,
64
+ 'FIId' => fi_id,
65
+ 'FIType' => 'Advisor',
66
+ 'FIFetchParamList' => fetch_parameters,
67
+ 'ParameterList' => login_parameters
68
+ }
69
+ )
70
+ end
71
+
72
+ private
73
+
74
+ def create
75
+ response = AggAdvisor.add_advisor(params)
76
+ AqumulateAPI.session.check_raise_request_error(response)
77
+
78
+ self.persisted = true
79
+
80
+ return self
81
+ end
82
+
83
+ def update
84
+ AggAdvisor.update_advisor(params)
85
+
86
+ return self
87
+ end
88
+
89
+ def params
90
+ ATTR_MAP.map { |k, v| [v, send(k)] }.to_h
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,11 @@
1
+ module AqumulateAPI
2
+ class FetchParameter < Entity
3
+ ATTR_MAP = {
4
+ id: 'ParamId',
5
+ caption: 'ParamCaption',
6
+ size: 'ParamSize'
7
+ }
8
+
9
+ attr_accessor :id, :caption, :size
10
+ end
11
+ end
@@ -0,0 +1,43 @@
1
+ module AqumulateAPI
2
+ class FinancialInstitution < Entity
3
+
4
+ SOURCE_ASSOCIATIONS = [
5
+ { key: 'FILoginParameters', class: LoginParameter, attr: :login_parameters },
6
+ { key: 'FiFetchParamList', class: FetchParameter, attr: :fetch_parameters },
7
+ { key: 'FiAccountDataList', class: AccountData, attr: :account_data },
8
+ ]
9
+
10
+ ATTR_MAP = {
11
+ id: 'FIId',
12
+ name: 'FIName',
13
+ url: 'Url',
14
+ }
15
+
16
+ attr_accessor :id, :name, :url, :login_parameters, :fetch_parameters, :account_data
17
+
18
+ def login_parameters
19
+ @login_parameters ||= []
20
+ end
21
+
22
+ def fetch_parameters
23
+ @fetch_parameters ||= []
24
+ end
25
+
26
+ def account_data
27
+ @account_data ||= []
28
+ end
29
+
30
+ def self.find(id)
31
+ response = AggAccount.advisor_get_financial_institution({ 'FIId' => id })
32
+ from_source(response['FinancialInstitution'])
33
+ end
34
+
35
+ def self.search(keyword)
36
+ response = AggAccount.advisor_search_financial_institution({ 'Keyword' => keyword })
37
+ return [] unless response.has_key?('FinancialInstitutions')
38
+
39
+ response['FinancialInstitutions'].map { |source| from_source(source) }
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ module AqumulateAPI
2
+ class LoginParameter < Entity
3
+
4
+ ATTR_MAP = {
5
+ id: 'ParameterId',
6
+ number: 'ParameterNumber',
7
+ type: 'ParameterType',
8
+ max_length: 'ParameterMaxLength',
9
+ size: 'ParameterSize',
10
+ caption: 'ParameterCaption',
11
+ variable_name: 'ParameterVariableName',
12
+ default_value: 'ParameterDefaultValue',
13
+ editable: 'ParameterEditable',
14
+ sensitivity_code: 'ParameterSensitivityCode'
15
+ }
16
+
17
+ attr_accessor :id, :number, :type, :max_length, :size, :caption, :variable_name, :default_value, :editable,
18
+ :sensitivity_code, :value
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module AqumulateAPI
2
+ class Position < Entity
3
+
4
+ ATTR_MAP = {
5
+ id: 'PosID',
6
+ date: 'Date',
7
+ ticker: 'Ticker',
8
+ description: 'Description',
9
+ asset_id: 'AssetID',
10
+ asset_type: 'AssetIDType',
11
+ price: 'Price',
12
+ quantity: 'Quantity',
13
+ market_value: 'MarketValue',
14
+ cost_basis: 'CostBasis'
15
+ }
16
+
17
+ attr_accessor :id, :date, :ticker, :description, :asset_id, :asset_type, :price, :quantity, :market_value,
18
+ :cost_basis
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ module AqumulateAPI
2
+ class Transaction < Entity
3
+
4
+ ATTR_MAP = {
5
+ id: 'TransactionId',
6
+ account_id: 'AccountId',
7
+ transaction_type: 'TransactionType',
8
+ posted_date: 'PostedDate',
9
+ origination_date: 'OriginationDate',
10
+ amount: 'Amount',
11
+ currency: 'CurCode',
12
+ description: 'Description',
13
+ code: 'TransactionCode',
14
+ action: 'TransactionAction',
15
+ date: 'TransactionDate',
16
+ commision: 'Commission',
17
+ ticker: 'Ticker',
18
+ position_description: 'PosDescription',
19
+ asset_id: 'AssetID',
20
+ asset_type: 'AssetIDType',
21
+ units: 'Units',
22
+ price: 'Price',
23
+ check_number: 'CheckNumber',
24
+ created_on: 'CreatedOn'
25
+ }
26
+
27
+ attr_accessor :id, :account_id, :transaction_type, :posted_date, :origination_date, :amount, :currency,
28
+ :description, :code, :action, :date, :commission, :ticker, :position_description, :asset_id,
29
+ :asset_type, :units, :price, :check_number, :created_on
30
+
31
+ # @param advisor [Advisor]
32
+ # @param account [Account]
33
+ # @param start_date [Date]
34
+ # @param end_date [Date]
35
+ # @return [Array(Transaction)]
36
+ def self.fetch(advisor, account, start_date, end_date)
37
+ body = {
38
+ 'SessionId' => advisor.session_id,
39
+ 'AccountId' => account.id,
40
+ 'AccountType' => account.account_type,
41
+ 'ExtendedAccountType' => account.account_type_extended,
42
+ 'StartDate' => start_date.strftime('%m/%d/%Y'),
43
+ 'EndDate' => end_date.strftime('%m/%d/%Y')
44
+ }
45
+
46
+ response = AggAccount.advisor_get_transactions_by_date(body)
47
+
48
+ return [] unless response.has_key?('Transactions')
49
+
50
+ response['Transactions'].map { |source| from_source(source) }
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ module AqumulateAPI
2
+ class Entity
3
+
4
+ ATTR_MAP = {}
5
+ SOURCE_ASSOCIATIONS = []
6
+
7
+ def initialize(attributes = {})
8
+ attributes.each { |k, v| instance_variable_set("@#{k}", v) }
9
+ end
10
+
11
+ def self.from_source(source)
12
+ entity = new({})
13
+
14
+ self::ATTR_MAP.invert.each do |k, v|
15
+ entity.send("#{v.to_s}=", source[k])
16
+ end
17
+
18
+ self::SOURCE_ASSOCIATIONS.each do |association|
19
+ next if source[association[:key]].nil? || source[association[:key]].empty?
20
+ association_set = entity.send(association[:attr])
21
+
22
+ source[association[:key]].each do |association_source|
23
+ association_set << association[:class].from_source(association_source)
24
+ end
25
+
26
+ entity.send("#{association[:attr].to_s}=", association_set)
27
+ end
28
+
29
+ entity
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module AqumulateAPI
2
+ class AuthenticationError < StandardError
3
+ attr_reader :request
4
+
5
+ def initialize(msg, request)
6
+ @request = request
7
+ super msg
8
+ end
9
+ end
10
+
11
+ class RequestError < StandardError
12
+ attr_reader :request
13
+
14
+ def initialize(msg, request)
15
+ @request = request
16
+ super msg
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,89 @@
1
+ module AqumulateAPI
2
+
3
+ class << self
4
+ attr_accessor :session
5
+ end
6
+
7
+ def self.session
8
+ @session ||= Session.new
9
+ end
10
+
11
+ def self.session=(session)
12
+ @session = session
13
+ end
14
+
15
+ class Session
16
+
17
+ attr_reader :config, :auth
18
+
19
+ def initialize
20
+ if block_given?
21
+ @config = AqumulateAPI::Configuration.new
22
+ yield(@config)
23
+ else
24
+ @config = AqumulateAPI.config
25
+ end
26
+
27
+ login
28
+ end
29
+
30
+ def login
31
+ response = HTTParty.post(
32
+ "#{config.url}/token",
33
+ headers: { 'Accept' => 'application/x-www-form-urlencoded' },
34
+ body: {
35
+ grant_type: config.grant_type,
36
+ 'userName' => config.username,
37
+ password: config.password
38
+ },
39
+ timeout: config.timeout
40
+ )
41
+
42
+ if config.debug
43
+ puts response.request.inspect
44
+ puts response.inspect
45
+ end
46
+
47
+ @auth = handle_response response
48
+ end
49
+
50
+ def handle_response(response)
51
+ if response.parsed_response.is_a?(Hash) && response.parsed_response.has_key?('error')
52
+ raise AuthenticationError.new(response.parsed_response['error_description'], response.request)
53
+
54
+ elsif response.code != 200
55
+ raise RequestError.new(response.response.msg, response.response)
56
+ end
57
+
58
+ response.parsed_response
59
+ end
60
+
61
+ def api_request(resource, body)
62
+ response = HTTParty.post(
63
+ "#{config.url}/api/#{resource}",
64
+ headers: {
65
+ 'Content-Type' => 'application/json',
66
+ 'Authorization' => "bearer #{auth['access_token']}"
67
+ },
68
+ body: body.to_json,
69
+ timeout: config.timeout
70
+ )
71
+
72
+ if config.debug
73
+ puts response.request.inspect
74
+ puts response.inspect
75
+ end
76
+
77
+ handle_response response
78
+ end
79
+
80
+ def check_raise_request_error(response)
81
+ raise RequestError.new(response['ErrorMessage'], response) if response_has_error?(response)
82
+ end
83
+
84
+ def response_has_error?(response)
85
+ response.has_key?('ErrorMessage') && !response['ErrorMessage'].to_s.empty?
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module AqumulateAPI
2
+ VERSION = '0.2.0'
3
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aqumulate_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Pheasey
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.13'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.13'
55
+ description: Aqumulate API wrapper with easy to use adapter classes.
56
+ email:
57
+ - kevin@kpheasey.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - CODE_OF_CONDUCT.md
64
+ - Gemfile
65
+ - README.md
66
+ - Rakefile
67
+ - aqumulate_api.gemspec
68
+ - bin/console
69
+ - bin/setup
70
+ - lib/aqumulate_api.rb
71
+ - lib/aqumulate_api/agg_account.rb
72
+ - lib/aqumulate_api/agg_advisor.rb
73
+ - lib/aqumulate_api/configuration.rb
74
+ - lib/aqumulate_api/entities/account.rb
75
+ - lib/aqumulate_api/entities/account_balance.rb
76
+ - lib/aqumulate_api/entities/account_data.rb
77
+ - lib/aqumulate_api/entities/advisor.rb
78
+ - lib/aqumulate_api/entities/fetch_parameter.rb
79
+ - lib/aqumulate_api/entities/financial_institution.rb
80
+ - lib/aqumulate_api/entities/login_parameter.rb
81
+ - lib/aqumulate_api/entities/position.rb
82
+ - lib/aqumulate_api/entities/transaction.rb
83
+ - lib/aqumulate_api/entity.rb
84
+ - lib/aqumulate_api/errors.rb
85
+ - lib/aqumulate_api/session.rb
86
+ - lib/aqumulate_api/version.rb
87
+ homepage: https://github.com/JoelBeasley/autotask_api
88
+ licenses: []
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.5.1
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Aqumulate API wrapper
110
+ test_files: []