async-firefly 1.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 +7 -0
- data/lib/async/firefly/about.rb +79 -0
- data/lib/async/firefly/accounts.rb +129 -0
- data/lib/async/firefly/attachments.rb +53 -0
- data/lib/async/firefly/autocomplete.rb +435 -0
- data/lib/async/firefly/available_budgets.rb +59 -0
- data/lib/async/firefly/bills.rb +107 -0
- data/lib/async/firefly/budgets.rb +165 -0
- data/lib/async/firefly/categories.rb +63 -0
- data/lib/async/firefly/charts.rb +73 -0
- data/lib/async/firefly/client.rb +89 -0
- data/lib/async/firefly/configurations.rb +62 -0
- data/lib/async/firefly/currencies.rb +71 -0
- data/lib/async/firefly/data.rb +37 -0
- data/lib/async/firefly/error.rb +57 -0
- data/lib/async/firefly/escape.rb +9 -0
- data/lib/async/firefly/exchange_rates.rb +77 -0
- data/lib/async/firefly/insight.rb +185 -0
- data/lib/async/firefly/links.rb +69 -0
- data/lib/async/firefly/nested.rb +75 -0
- data/lib/async/firefly/object_groups.rb +33 -0
- data/lib/async/firefly/page.rb +139 -0
- data/lib/async/firefly/piggy_banks.rb +153 -0
- data/lib/async/firefly/preferences.rb +58 -0
- data/lib/async/firefly/recurrences.rb +169 -0
- data/lib/async/firefly/redacted.rb +32 -0
- data/lib/async/firefly/representation.rb +99 -0
- data/lib/async/firefly/rule_groups.rb +55 -0
- data/lib/async/firefly/rules.rb +105 -0
- data/lib/async/firefly/search.rb +40 -0
- data/lib/async/firefly/submit.rb +139 -0
- data/lib/async/firefly/summary.rb +45 -0
- data/lib/async/firefly/sums.rb +17 -0
- data/lib/async/firefly/tags.rb +57 -0
- data/lib/async/firefly/transactions.rb +231 -0
- data/lib/async/firefly/user_groups.rb +49 -0
- data/lib/async/firefly/users.rb +35 -0
- data/lib/async/firefly/version.rb +5 -0
- data/lib/async/firefly/webhooks.rb +101 -0
- data/lib/async/firefly/wrapper.rb +65 -0
- data/lib/async/firefly.rb +2 -0
- data/license.md +21 -0
- data/readme.md +498 -0
- metadata +110 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative "page"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
module Data
|
|
6
|
+
def bulk_update_transactions(query)
|
|
7
|
+
self.discard(self.with(path: "v1/data/bulk/transactions", parameters: {query: query}), nil, method: :post)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def destroy!(objects, unused: nil)
|
|
11
|
+
self.discard(self.with(path: "v1/data/destroy", parameters: {objects: objects, unused: unused}.compact))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def purge! = self.discard(self.with(path: "v1/data/purge"))
|
|
15
|
+
|
|
16
|
+
def export_accounts(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/accounts", parameters: parameters))
|
|
17
|
+
|
|
18
|
+
def export_bills(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/bills", parameters: parameters))
|
|
19
|
+
|
|
20
|
+
def export_subscriptions(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/subscriptions", parameters: parameters))
|
|
21
|
+
|
|
22
|
+
def export_budgets(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/budgets", parameters: parameters))
|
|
23
|
+
|
|
24
|
+
def export_categories(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/categories", parameters: parameters))
|
|
25
|
+
|
|
26
|
+
def export_piggy_banks(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/piggy-banks", parameters: parameters))
|
|
27
|
+
|
|
28
|
+
def export_recurring(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/recurring", parameters: parameters))
|
|
29
|
+
|
|
30
|
+
def export_rules(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/rules", parameters: parameters))
|
|
31
|
+
|
|
32
|
+
def export_tags(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/tags", parameters: parameters))
|
|
33
|
+
|
|
34
|
+
def export_transactions(**parameters) = self.fetch(Download, self.with(path: "v1/data/export/transactions", parameters: parameters))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require "async/rest/error"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class Failure < Async::REST::ResponseError
|
|
9
|
+
def self.for(response)
|
|
10
|
+
self.new(response, self.problem_for(self.payload_of(response)))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.payload_of(response)
|
|
14
|
+
response.read
|
|
15
|
+
rescue StandardError => error
|
|
16
|
+
error.message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.problem_for(payload)
|
|
20
|
+
case payload
|
|
21
|
+
in Hash => problem
|
|
22
|
+
problem
|
|
23
|
+
else
|
|
24
|
+
{message: payload.to_s}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.summary_of(problem)
|
|
29
|
+
problem[:message] || problem[:exception] || problem.inspect
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(response, problem = {})
|
|
33
|
+
super(response)
|
|
34
|
+
|
|
35
|
+
@problem = problem
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attr :problem
|
|
39
|
+
|
|
40
|
+
def message = self.status.to_s + ": " + self.class.summary_of(@problem)
|
|
41
|
+
|
|
42
|
+
def to_s = self.message
|
|
43
|
+
|
|
44
|
+
def status = @response.status
|
|
45
|
+
|
|
46
|
+
def retry_after = @response.headers["retry-after"]
|
|
47
|
+
|
|
48
|
+
def detail = @problem[:message]
|
|
49
|
+
|
|
50
|
+
def exception_class = @problem[:exception]
|
|
51
|
+
|
|
52
|
+
def errors = @problem.fetch(:errors, {})
|
|
53
|
+
|
|
54
|
+
def invalid? = !self.errors.empty?
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require_relative "page"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
class CurrencyExchangeRate < Representation
|
|
6
|
+
def created_at = self.attributes[:created_at]
|
|
7
|
+
|
|
8
|
+
def updated_at = self.attributes[:updated_at]
|
|
9
|
+
|
|
10
|
+
def from_currency_id = self.attributes[:from_currency_id]
|
|
11
|
+
|
|
12
|
+
def from_currency_name = self.attributes[:from_currency_name]
|
|
13
|
+
|
|
14
|
+
def from_currency_code = self.attributes[:from_currency_code]
|
|
15
|
+
|
|
16
|
+
def from_currency_symbol = self.attributes[:from_currency_symbol]
|
|
17
|
+
|
|
18
|
+
def from_currency_decimal_places = self.attributes[:from_currency_decimal_places]
|
|
19
|
+
|
|
20
|
+
def to_currency_id = self.attributes[:to_currency_id]
|
|
21
|
+
|
|
22
|
+
def to_currency_name = self.attributes[:to_currency_name]
|
|
23
|
+
|
|
24
|
+
def to_currency_code = self.attributes[:to_currency_code]
|
|
25
|
+
|
|
26
|
+
def to_currency_symbol = self.attributes[:to_currency_symbol]
|
|
27
|
+
|
|
28
|
+
def to_currency_decimal_places = self.attributes[:to_currency_decimal_places]
|
|
29
|
+
|
|
30
|
+
def rate = self.attributes[:rate]
|
|
31
|
+
|
|
32
|
+
def date = self.attributes[:date]
|
|
33
|
+
|
|
34
|
+
def update(payload) = self.submit(CurrencyExchangeRate, @resource, payload, method: :put)
|
|
35
|
+
|
|
36
|
+
def delete = self.discard(@resource)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class CurrencyExchangeRatePage < Page
|
|
40
|
+
MEMBER = CurrencyExchangeRate
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class ExchangeRatePairPage < Page
|
|
44
|
+
MEMBER = CurrencyExchangeRate
|
|
45
|
+
|
|
46
|
+
def member_path(record) = "../../../exchange-rates/" + self.identifier(record, :id)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class ExchangeRateDatedPage < Page
|
|
50
|
+
MEMBER = CurrencyExchangeRate
|
|
51
|
+
|
|
52
|
+
def member_path(record) = "../../../../exchange-rates/" + self.identifier(record, :id)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module ExchangeRates
|
|
56
|
+
def exchange_rates(**parameters) = Collection.for(self, "v1/exchange-rates", CurrencyExchangeRatePage, **parameters)
|
|
57
|
+
|
|
58
|
+
def exchange_rate(id) = CurrencyExchangeRate.new(self.with(path: "v1/exchange-rates/" + Firefly.escape(id)))
|
|
59
|
+
|
|
60
|
+
def create_exchange_rate(payload) = self.submit(CurrencyExchangeRate, self.with(path: "v1/exchange-rates"), payload, identify: true)
|
|
61
|
+
|
|
62
|
+
def exchange_rates_between(from, to, **parameters) = Collection.for(self, "v1/exchange-rates/" + Firefly.escape(from) + "/" + Firefly.escape(to), ExchangeRatePairPage, **parameters)
|
|
63
|
+
|
|
64
|
+
def delete_exchange_rates_between(from, to) = self.discard(self.with(path: "v1/exchange-rates/" + Firefly.escape(from) + "/" + Firefly.escape(to)))
|
|
65
|
+
|
|
66
|
+
def exchange_rates_on(from, to, date, **parameters) = Collection.for(self, "v1/exchange-rates/" + Firefly.escape(from) + "/" + Firefly.escape(to) + "/" + Firefly.escape(date), ExchangeRateDatedPage, **parameters)
|
|
67
|
+
|
|
68
|
+
def update_exchange_rate_on(from, to, date, payload) = self.submit(CurrencyExchangeRate, self.with(path: "v1/exchange-rates/" + Firefly.escape(from) + "/" + Firefly.escape(to) + "/" + Firefly.escape(date)), payload, method: :put)
|
|
69
|
+
|
|
70
|
+
def delete_exchange_rate_on(from, to, date) = self.discard(self.with(path: "v1/exchange-rates/" + Firefly.escape(from) + "/" + Firefly.escape(to) + "/" + Firefly.escape(date)))
|
|
71
|
+
|
|
72
|
+
def create_exchange_rates_by_date(date, payload) = self.submit(CurrencyExchangeRatePage, self.with(path: "v1/exchange-rates/by-date/" + Firefly.escape(date)), payload, identify: nil)
|
|
73
|
+
|
|
74
|
+
def create_exchange_rates_by_pair(from, to, payload) = self.submit(CurrencyExchangeRatePage, self.with(path: "v1/exchange-rates/by-currencies/" + Firefly.escape(from) + "/" + Firefly.escape(to)), payload, identify: nil)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
require_relative "page"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
class InsightGroupEntry < Representation
|
|
6
|
+
def id = self.value[:id]
|
|
7
|
+
|
|
8
|
+
def name = self.value[:name]
|
|
9
|
+
|
|
10
|
+
def difference = self.value[:difference]
|
|
11
|
+
|
|
12
|
+
def difference_float = self.value[:difference_float]
|
|
13
|
+
|
|
14
|
+
def currency_id = self.value[:currency_id]
|
|
15
|
+
|
|
16
|
+
def currency_code = self.value[:currency_code]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class InsightTotalEntry < Representation
|
|
20
|
+
def difference = self.value[:difference]
|
|
21
|
+
|
|
22
|
+
def difference_float = self.value[:difference_float]
|
|
23
|
+
|
|
24
|
+
def currency_id = self.value[:currency_id]
|
|
25
|
+
|
|
26
|
+
def currency_code = self.value[:currency_code]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class InsightTransferEntry < Representation
|
|
30
|
+
def id = self.value[:id]
|
|
31
|
+
|
|
32
|
+
def name = self.value[:name]
|
|
33
|
+
|
|
34
|
+
def difference = self.value[:difference]
|
|
35
|
+
|
|
36
|
+
def difference_float = self.value[:difference_float]
|
|
37
|
+
|
|
38
|
+
def in = self.value[:in]
|
|
39
|
+
|
|
40
|
+
def in_float = self.value[:in_float]
|
|
41
|
+
|
|
42
|
+
def out = self.value[:out]
|
|
43
|
+
|
|
44
|
+
def out_float = self.value[:out_float]
|
|
45
|
+
|
|
46
|
+
def currency_id = self.value[:currency_id]
|
|
47
|
+
|
|
48
|
+
def currency_code = self.value[:currency_code]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class InsightGroup < Representation
|
|
52
|
+
include Enumerable
|
|
53
|
+
|
|
54
|
+
def each(&block)
|
|
55
|
+
if block
|
|
56
|
+
self.deconstruct.each{|record| block.call(InsightGroupEntry.new(@resource, value: record, metadata: self.metadata))}
|
|
57
|
+
else
|
|
58
|
+
self.to_enum(:each)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class InsightTotal < Representation
|
|
64
|
+
include Enumerable
|
|
65
|
+
|
|
66
|
+
def each(&block)
|
|
67
|
+
if block
|
|
68
|
+
self.deconstruct.each{|record| block.call(InsightTotalEntry.new(@resource, value: record, metadata: self.metadata))}
|
|
69
|
+
else
|
|
70
|
+
self.to_enum(:each)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class InsightTransfer < Representation
|
|
76
|
+
include Enumerable
|
|
77
|
+
|
|
78
|
+
def each(&block)
|
|
79
|
+
if block
|
|
80
|
+
self.deconstruct.each{|record| block.call(InsightTransferEntry.new(@resource, value: record, metadata: self.metadata))}
|
|
81
|
+
else
|
|
82
|
+
self.to_enum(:each)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
module Insight
|
|
88
|
+
def expense_by_expense_account(start:, end:, accounts: nil)
|
|
89
|
+
InsightGroup.new(self.with(path: "v1/insight/expense/expense", parameters: {start:, end:, accounts:}.compact))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def expense_by_asset_account(start:, end:, accounts: nil)
|
|
93
|
+
InsightGroup.new(self.with(path: "v1/insight/expense/asset", parameters: {start:, end:, accounts:}.compact))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def income_by_revenue_account(start:, end:, accounts: nil)
|
|
97
|
+
InsightGroup.new(self.with(path: "v1/insight/income/revenue", parameters: {start:, end:, accounts:}.compact))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def income_by_asset_account(start:, end:, accounts: nil)
|
|
101
|
+
InsightGroup.new(self.with(path: "v1/insight/income/asset", parameters: {start:, end:, accounts:}.compact))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def transfer_by_asset_account(start:, end:, accounts: nil)
|
|
105
|
+
InsightTransfer.new(self.with(path: "v1/insight/transfer/asset", parameters: {start:, end:, accounts:}.compact))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def expense_by_bill(start:, end:, bills: nil, accounts: nil)
|
|
109
|
+
InsightGroup.new(self.with(path: "v1/insight/expense/bill", parameters: {start:, end:, bills:, accounts:}.compact))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def expense_without_bill(start:, end:, accounts: nil)
|
|
113
|
+
InsightTotal.new(self.with(path: "v1/insight/expense/no-bill", parameters: {start:, end:, accounts:}.compact))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def expense_by_budget(start:, end:, budgets: nil, accounts: nil)
|
|
117
|
+
InsightGroup.new(self.with(path: "v1/insight/expense/budget", parameters: {start:, end:, budgets:, accounts:}.compact))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def expense_without_budget(start:, end:, accounts: nil)
|
|
121
|
+
InsightTotal.new(self.with(path: "v1/insight/expense/no-budget", parameters: {start:, end:, accounts:}.compact))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def expense_by_category(start:, end:, categories: nil, accounts: nil)
|
|
125
|
+
InsightGroup.new(self.with(path: "v1/insight/expense/category", parameters: {start:, end:, categories:, accounts:}.compact))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def expense_without_category(start:, end:, accounts: nil)
|
|
129
|
+
InsightTotal.new(self.with(path: "v1/insight/expense/no-category", parameters: {start:, end:, accounts:}.compact))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def income_by_category(start:, end:, categories: nil, accounts: nil)
|
|
133
|
+
InsightGroup.new(self.with(path: "v1/insight/income/category", parameters: {start:, end:, categories:, accounts:}.compact))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def income_without_category(start:, end:, accounts: nil)
|
|
137
|
+
InsightTotal.new(self.with(path: "v1/insight/income/no-category", parameters: {start:, end:, accounts:}.compact))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def transfer_by_category(start:, end:, categories: nil, accounts: nil)
|
|
141
|
+
InsightGroup.new(self.with(path: "v1/insight/transfer/category", parameters: {start:, end:, categories:, accounts:}.compact))
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def transfer_without_category(start:, end:, accounts: nil)
|
|
145
|
+
InsightTotal.new(self.with(path: "v1/insight/transfer/no-category", parameters: {start:, end:, accounts:}.compact))
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def expense_by_tag(start:, end:, tags: nil, accounts: nil)
|
|
149
|
+
InsightGroup.new(self.with(path: "v1/insight/expense/tag", parameters: {start:, end:, tags:, accounts:}.compact))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def expense_without_tag(start:, end:, accounts: nil)
|
|
153
|
+
InsightTotal.new(self.with(path: "v1/insight/expense/no-tag", parameters: {start:, end:, accounts:}.compact))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def income_by_tag(start:, end:, tags: nil, accounts: nil)
|
|
157
|
+
InsightGroup.new(self.with(path: "v1/insight/income/tag", parameters: {start:, end:, tags:, accounts:}.compact))
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def income_without_tag(start:, end:, accounts: nil)
|
|
161
|
+
InsightTotal.new(self.with(path: "v1/insight/income/no-tag", parameters: {start:, end:, accounts:}.compact))
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def transfer_by_tag(start:, end:, tags: nil, accounts: nil)
|
|
165
|
+
InsightGroup.new(self.with(path: "v1/insight/transfer/tag", parameters: {start:, end:, tags:, accounts:}.compact))
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def transfer_without_tag(start:, end:, accounts: nil)
|
|
169
|
+
InsightTotal.new(self.with(path: "v1/insight/transfer/no-tag", parameters: {start:, end:, accounts:}.compact))
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def expense_total(start:, end:, accounts: nil)
|
|
173
|
+
InsightTotal.new(self.with(path: "v1/insight/expense/total", parameters: {start:, end:, accounts:}.compact))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def income_total(start:, end:, accounts: nil)
|
|
177
|
+
InsightTotal.new(self.with(path: "v1/insight/income/total", parameters: {start:, end:, accounts:}.compact))
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def transfer_total(start:, end:, accounts: nil)
|
|
181
|
+
InsightTotal.new(self.with(path: "v1/insight/transfer/total", parameters: {start:, end:, accounts:}.compact))
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require_relative "page"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
class LinkType < Representation
|
|
6
|
+
def name = self.attributes[:name]
|
|
7
|
+
|
|
8
|
+
def inward = self.attributes[:inward]
|
|
9
|
+
|
|
10
|
+
def outward = self.attributes[:outward]
|
|
11
|
+
|
|
12
|
+
def editable = self.attributes[:editable]
|
|
13
|
+
|
|
14
|
+
def transactions(**parameters) = Collection.for(@resource, "transactions", LinkTypeTransactionPage, **parameters)
|
|
15
|
+
|
|
16
|
+
def update(payload) = self.submit(LinkType, @resource, payload, method: :put)
|
|
17
|
+
|
|
18
|
+
def delete = self.discard(@resource)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class TransactionLink < Representation
|
|
22
|
+
def created_at = self.attributes[:created_at]
|
|
23
|
+
|
|
24
|
+
def updated_at = self.attributes[:updated_at]
|
|
25
|
+
|
|
26
|
+
def link_type_id = self.attributes[:link_type_id]
|
|
27
|
+
|
|
28
|
+
def link_type_name = self.attributes[:link_type_name]
|
|
29
|
+
|
|
30
|
+
def inward_id = self.attributes[:inward_id]
|
|
31
|
+
|
|
32
|
+
def outward_id = self.attributes[:outward_id]
|
|
33
|
+
|
|
34
|
+
def notes = self.attributes[:notes]
|
|
35
|
+
|
|
36
|
+
def update(payload) = self.submit(TransactionLink, @resource, payload, method: :put)
|
|
37
|
+
|
|
38
|
+
def delete = self.discard(@resource)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class LinkTypePage < Page
|
|
42
|
+
MEMBER = LinkType
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class TransactionLinkPage < Page
|
|
46
|
+
MEMBER = TransactionLink
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class LinkTypeTransactionPage < Page
|
|
50
|
+
def member_class = Transaction
|
|
51
|
+
|
|
52
|
+
def member_path(record) = "../../../transactions/" + self.identifier(record, :id)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module Links
|
|
56
|
+
def link_types(**parameters) = Collection.for(self, "v1/link-types", LinkTypePage, **parameters)
|
|
57
|
+
|
|
58
|
+
def link_type(id) = LinkType.new(self.with(path: "v1/link-types/" + Firefly.escape(id)))
|
|
59
|
+
|
|
60
|
+
def create_link_type(payload) = self.submit(LinkType, self.with(path: "v1/link-types"), payload, identify: true)
|
|
61
|
+
|
|
62
|
+
def transaction_links(**parameters) = Collection.for(self, "v1/transaction-links", TransactionLinkPage, **parameters)
|
|
63
|
+
|
|
64
|
+
def transaction_link(id) = TransactionLink.new(self.with(path: "v1/transaction-links/" + Firefly.escape(id)))
|
|
65
|
+
|
|
66
|
+
def create_transaction_link(payload) = self.submit(TransactionLink, self.with(path: "v1/transaction-links"), payload, identify: true)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require_relative "page"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
class NestedPage < Page
|
|
6
|
+
def collection = raise NotImplementedError
|
|
7
|
+
|
|
8
|
+
def member_path(record) = "../../../" + self.collection + "/" + self.identifier(record, :id)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class NestedAccountPage < NestedPage
|
|
12
|
+
def member_class = Account
|
|
13
|
+
|
|
14
|
+
def collection = "accounts"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class NestedTransactionPage < NestedPage
|
|
18
|
+
def member_class = Transaction
|
|
19
|
+
|
|
20
|
+
def collection = "transactions"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class NestedAttachmentPage < NestedPage
|
|
24
|
+
def member_class = Attachment
|
|
25
|
+
|
|
26
|
+
def collection = "attachments"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class NestedPiggyBankPage < NestedPage
|
|
30
|
+
def member_class = PiggyBank
|
|
31
|
+
|
|
32
|
+
def collection = "piggy-banks"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class NestedBillPage < NestedPage
|
|
36
|
+
def member_class = Bill
|
|
37
|
+
|
|
38
|
+
def collection = "bills"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class NestedRulePage < NestedPage
|
|
42
|
+
def member_class = Rule
|
|
43
|
+
|
|
44
|
+
def collection = "rules"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class NestedRecurrencePage < NestedPage
|
|
48
|
+
def member_class = Recurrence
|
|
49
|
+
|
|
50
|
+
def collection = "recurrences"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class NestedAvailableBudgetPage < NestedPage
|
|
54
|
+
def member_class = AvailableBudget
|
|
55
|
+
|
|
56
|
+
def collection = "available-budgets"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class NestedExchangeRatePage < NestedPage
|
|
60
|
+
def member_class = CurrencyExchangeRate
|
|
61
|
+
|
|
62
|
+
def collection = "exchange-rates"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class NestedBudgetLimitPage < NestedPage
|
|
66
|
+
def member_class = BudgetLimit
|
|
67
|
+
|
|
68
|
+
def collection = "budget-limits"
|
|
69
|
+
|
|
70
|
+
def member_path(record)
|
|
71
|
+
"../../../budgets/" + self.identifier(record, :attributes, :budget_id) + "/limits/" + self.identifier(record, :id)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require_relative "page"
|
|
2
|
+
|
|
3
|
+
module Async
|
|
4
|
+
module Firefly
|
|
5
|
+
class ObjectGroup < Representation
|
|
6
|
+
def created_at = self.attributes[:created_at]
|
|
7
|
+
|
|
8
|
+
def updated_at = self.attributes[:updated_at]
|
|
9
|
+
|
|
10
|
+
def title = self.attributes[:title]
|
|
11
|
+
|
|
12
|
+
def order = self.attributes[:order]
|
|
13
|
+
|
|
14
|
+
def piggy_banks(**parameters) = Collection.for(@resource, "piggy-banks", NestedPiggyBankPage, **parameters)
|
|
15
|
+
|
|
16
|
+
def bills(**parameters) = Collection.for(@resource, "bills", NestedBillPage, **parameters)
|
|
17
|
+
|
|
18
|
+
def update(payload) = self.submit(ObjectGroup, @resource, payload, method: :put)
|
|
19
|
+
|
|
20
|
+
def delete = self.discard(@resource)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class ObjectGroupPage < Page
|
|
24
|
+
MEMBER = ObjectGroup
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module ObjectGroups
|
|
28
|
+
def object_groups(**parameters) = Collection.for(self, "v1/object-groups", ObjectGroupPage, **parameters)
|
|
29
|
+
|
|
30
|
+
def object_group(id) = ObjectGroup.new(self.with(path: "v1/object-groups/" + Firefly.escape(id)))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require_relative "error"
|
|
2
|
+
require_relative "representation"
|
|
3
|
+
|
|
4
|
+
module Async
|
|
5
|
+
module Firefly
|
|
6
|
+
class Page < Representation
|
|
7
|
+
include Enumerable
|
|
8
|
+
|
|
9
|
+
MEMBER = Representation
|
|
10
|
+
|
|
11
|
+
def records
|
|
12
|
+
case self.value
|
|
13
|
+
in {data: Array => records}
|
|
14
|
+
records
|
|
15
|
+
else
|
|
16
|
+
[]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pagination
|
|
21
|
+
case self.value
|
|
22
|
+
in {meta: {pagination: Hash => pagination}}
|
|
23
|
+
pagination
|
|
24
|
+
else
|
|
25
|
+
{}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def total = self.pagination[:total].to_i
|
|
30
|
+
|
|
31
|
+
def per_page = self.pagination[:per_page].to_i
|
|
32
|
+
|
|
33
|
+
def current_page = self.pagination[:current_page].to_i
|
|
34
|
+
|
|
35
|
+
def total_pages = self.pagination[:total_pages].to_i
|
|
36
|
+
|
|
37
|
+
def last? = self.current_page >= [self.total_pages, 1].max
|
|
38
|
+
|
|
39
|
+
def empty? = self.records.empty?
|
|
40
|
+
|
|
41
|
+
def each(&block)
|
|
42
|
+
if block
|
|
43
|
+
self.records.each do |record|
|
|
44
|
+
block.call(self.member_for(record))
|
|
45
|
+
end
|
|
46
|
+
else
|
|
47
|
+
self.to_enum(:each)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def member_class = self.class::MEMBER
|
|
52
|
+
|
|
53
|
+
def member_path(record) = self.identifier(record, :id)
|
|
54
|
+
|
|
55
|
+
def identifier(record, *keys)
|
|
56
|
+
value = record.dig(*keys)
|
|
57
|
+
|
|
58
|
+
if value.nil? || value.to_s.empty?
|
|
59
|
+
raise Error, self.class.to_s + " cannot address a member: the response carried no " + keys.join(".")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
Firefly.escape(value)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def member_resource(record)
|
|
66
|
+
@resource.with(path: self.member_path(record), parameters: nil, merge: false)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def member_for(record)
|
|
70
|
+
self.member_class.new(self.member_resource(record), value: {data: record}, metadata: self.metadata)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class Collection
|
|
75
|
+
include Enumerable
|
|
76
|
+
|
|
77
|
+
DEFAULT_LIMIT = 50
|
|
78
|
+
|
|
79
|
+
def self.for(parent, path, page_class, **parameters)
|
|
80
|
+
self.new(parent.with(path: path, parameters: parameters), page_class)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def initialize(resource, page_class)
|
|
84
|
+
@resource = resource
|
|
85
|
+
@page_class = page_class
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attr :resource
|
|
89
|
+
|
|
90
|
+
attr :page_class
|
|
91
|
+
|
|
92
|
+
def each_page(limit: DEFAULT_LIMIT, &block)
|
|
93
|
+
if block
|
|
94
|
+
self.walk(limit, &block)
|
|
95
|
+
else
|
|
96
|
+
self.to_enum(:each_page, limit: limit)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def each(**options, &block)
|
|
101
|
+
if block
|
|
102
|
+
self.each_page(**options) do |page|
|
|
103
|
+
page.each(&block)
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
self.to_enum(:each, **options)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def page(number, limit: DEFAULT_LIMIT)
|
|
111
|
+
@page_class.new(@resource.with(parameters: {page: number, limit: limit}))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def total
|
|
115
|
+
self.page(1, limit: 1).total
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def walk(limit, &block)
|
|
119
|
+
number = 1
|
|
120
|
+
|
|
121
|
+
while true
|
|
122
|
+
page = self.page(number, limit: limit)
|
|
123
|
+
|
|
124
|
+
if page.empty?
|
|
125
|
+
break
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
block.call(page)
|
|
129
|
+
|
|
130
|
+
if page.last?
|
|
131
|
+
break
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
number += 1
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|