ka-ching-client 0.1.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.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/test_*.rb']
10
+ end
11
+
12
+ task default: ['test']
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'httpx/adapters/faraday'
5
+
6
+ require_relative 'ka_ching/version'
7
+ require_relative 'ka_ching/api_client'
8
+ require_relative 'ka_ching/api_v1/audit_logs'
9
+ require_relative 'ka_ching/api_v1/bookings'
10
+ require_relative 'ka_ching/api_v1/lockings'
11
+ require_relative 'ka_ching/api_v1/saldo'
12
+ require_relative 'ka_ching/api_v1/admin'
13
+ require_relative 'ka_ching/api_v1/tenants'
14
+ require_relative 'ka_ching/api_v1/client'
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ #
5
+ # The base class for all API versions.
6
+ #
7
+ class ApiClient
8
+ attr_accessor :conn
9
+ attr_reader :api_version, :base_url, :v1
10
+
11
+ def initialize(api_version: :v1, base_url: 'http://localhost:9292')
12
+ @api_version = api_version
13
+ @base_url = base_url
14
+ end
15
+
16
+ def build_client!(faraday: nil)
17
+ @conn = faraday || default_faraday
18
+ case @api_version
19
+ when :v1, 'v1'
20
+ @v1 = KaChing::ApiV1::Client.new(conn: @conn, base_url: @base_url)
21
+ else
22
+ raise ArgumentError, "Unknown API version: #{@api_version}"
23
+ end
24
+ self
25
+ end
26
+
27
+ private
28
+
29
+ def default_faraday
30
+ Faraday.new do |builder|
31
+ builder.url_prefix = @base_url
32
+ builder.use Faraday::Request::UrlEncoded
33
+ builder.use Faraday::Response::RaiseError
34
+ builder.use Faraday::Response::Logger
35
+
36
+ builder.adapter :httpx
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ module ApiV1
5
+ #
6
+ # Admin Endpoint for the KaChing API V1
7
+ #
8
+ class Admin
9
+ extend Forwardable
10
+
11
+ def_delegators :@conn, :get, :post, :put, :patch, :delete
12
+
13
+ def initialize(conn:, api_url:)
14
+ @conn = conn
15
+ @api_url = api_url
16
+ end
17
+
18
+ #
19
+ # gets the details of a tenant database
20
+ #
21
+ # @param [String] full tenant_account_id with its database namespace, e.g. 'kaching_tenant_user_1'
22
+ #
23
+ # @yield [Faraday::Response] The response from the server
24
+ # @return [Hash] containing the details of the tenant database
25
+ #
26
+ def details(tenant_account_id:)
27
+ admin_tenant_url = build_url(tenant_account_id: tenant_account_id)
28
+ res = get(admin_tenant_url) do |req|
29
+ req.headers['Content-Type'] = 'application/json'
30
+ end
31
+ yield res if block_given?
32
+ JSON.parse(res.body)
33
+ end
34
+
35
+ #
36
+ # creates a new tenant database
37
+ #
38
+ # @param [String] tenant_account_id without its database namespace, e.g.
39
+ # 'user_1' instead of 'kaching_tenant_user_1'
40
+ #
41
+ # @yield [Faraday::Response] The response from the server
42
+ # @return [Hash]
43
+ #
44
+ def create!(tenant_account_id:)
45
+ res = post(build_url) do |req|
46
+ req.headers['Content-Type'] = 'application/json'
47
+ req.body = { tenant_account_id: tenant_account_id }.to_json
48
+ end
49
+ yield res if block_given?
50
+ JSON.parse(res.body)
51
+ end
52
+
53
+ #
54
+ # drops a tenant database
55
+ #
56
+ # @param [String] tenant_account_id without its database namespace,
57
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
58
+ #
59
+ # @yield [Faraday::Response] The response from the server
60
+ # @return [Hash]
61
+ #
62
+ def drop!(tenant_account_id:)
63
+ res = delete(build_url) do |req|
64
+ req.headers['Content-Type'] = 'application/json'
65
+ req.body = { tenant_account_id: tenant_account_id }.to_json
66
+ end
67
+ yield res if block_given?
68
+ JSON.parse(res.body)
69
+ end
70
+
71
+ private
72
+
73
+ def build_url(tenant_account_id: nil)
74
+ if tenant_account_id
75
+ "#{@api_url}/admin/#{tenant_account_id}"
76
+ else
77
+ "#{@api_url}/admin/"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ module ApiV1
5
+ #
6
+ # AuditLog Endpoint for the KaChing API V1
7
+ #
8
+ class AuditLogs
9
+ extend Forwardable
10
+
11
+ def_delegators :@conn, :get, :post, :put, :patch, :delete
12
+
13
+ def initialize(conn:, api_url:)
14
+ @conn = conn
15
+ @api_url = api_url
16
+ end
17
+
18
+ #
19
+ # Get all bookings for the current tenant of a specific year
20
+ #
21
+ # @param [String] tenant_account_id
22
+ # @param [Integer] year
23
+ #
24
+ # @yield [Faraday::Response] The response from the server
25
+ # @return [Hash] containing the bookings as array
26
+ #
27
+ def of_year(tenant_account_id:, year:)
28
+ res = get(build_url(tenant_account_id: tenant_account_id),
29
+ { year: year })
30
+
31
+ yield res if block_given?
32
+ parse_bookings(JSON.parse(res.body))['audit_logs']
33
+ end
34
+
35
+ #
36
+ # Get all bookings for the current tenant of a specific year, month
37
+ #
38
+ # @param [String] tenant_account_id
39
+ # @param [Integer] year
40
+ # @param [Integer] month
41
+ #
42
+ # @yield [Faraday::Response] The response from the server
43
+ # @return [Hash] containing the bookings as array
44
+ #
45
+ def of_year_month(tenant_account_id:, year:, month:)
46
+ res = get(build_url(tenant_account_id: tenant_account_id),
47
+ { year: year, month: month })
48
+ yield res if block_given?
49
+ parse_bookings(JSON.parse(res.body))['audit_logs']
50
+ end
51
+
52
+ #
53
+ # Get all bookings for the current tenant of a specific year, month, day
54
+ #
55
+ # @param [String] tenant_account_id
56
+ # @param [Integer] year
57
+ # @param [Integer] month
58
+ # @param [Integer] day
59
+ #
60
+ # @yield [Faraday::Response] The response from the server
61
+ # @return [Hash] containing the bookings as array
62
+ #
63
+ def of_year_month_day(tenant_account_id:, year:, month:, day:)
64
+ res = get(build_url(tenant_account_id: tenant_account_id),
65
+ { year: year, month: month, day: day })
66
+ yield res if block_given?
67
+ parse_bookings(JSON.parse(res.body))['audit_logs']
68
+ end
69
+
70
+ private
71
+
72
+ def parse_bookings(audit_log_json)
73
+ audit_log_json['audit_logs'].map! do |audit_log|
74
+ parse_booking(audit_log, 'environment_snapshot')
75
+ parse_booking(audit_log, 'log_entry')
76
+ audit_log
77
+ end
78
+ audit_log_json
79
+ end
80
+
81
+ def parse_booking(audit_log, bookings_key)
82
+ audit_log[bookings_key] = JSON.parse(audit_log[bookings_key])
83
+ audit_log[bookings_key]['context'] = JSON.parse(JSON.parse(audit_log[bookings_key]['context']))
84
+ audit_log[bookings_key]['bookings'] = JSON.parse(audit_log[bookings_key]['bookings_json'])
85
+ audit_log[bookings_key]['bookings'].map! do |b|
86
+ b['context'] = JSON.parse(b['context'])
87
+ b
88
+ end
89
+ audit_log
90
+ end
91
+
92
+ #
93
+ # build the url for the bookings endpoint
94
+ #
95
+ # @param [String] tenant_account_id
96
+ # @param [Integer,nil] year
97
+ # @param [Integer,nil] month
98
+ # @param [Integer,nil] day
99
+ #
100
+ # @return [<Type>] <description>
101
+ #
102
+ def build_url(tenant_account_id:)
103
+ "#{@api_url}/#{tenant_account_id}/auditlogs"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ module ApiV1
5
+ #
6
+ # Bookings Endpoint for the KaChing API V1
7
+ #
8
+ class Bookings
9
+ extend Forwardable
10
+
11
+ def_delegators :@conn, :get, :post, :put, :patch, :delete
12
+
13
+ def initialize(conn:, api_url:)
14
+ @conn = conn
15
+ @api_url = api_url
16
+ end
17
+
18
+ #
19
+ # Deposit money to the current tenant account
20
+ #
21
+ # @param [String] tenant_account_id without the tenant database namespace,
22
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
23
+ # @param [Integer] amount_cents the amount to deposit in cents
24
+ # @param [Integer] year the year of the booking
25
+ # @param [Integer] month the month of the booking
26
+ # @param [Integer] day the day of the booking
27
+ # @param [Hash] context additional context information
28
+ #
29
+ # @yield [Faraday::Response] The response from the server
30
+ # @return [Hash] containing the booking details
31
+ #
32
+ def deposit!(tenant_account_id:,
33
+ amount_cents:,
34
+ year: Date.today.year,
35
+ month: Date.today.month,
36
+ day: Date.today.day,
37
+ context: {})
38
+ res = create!(tenant_account_id: tenant_account_id,
39
+ booking: {
40
+ action: :deposit,
41
+ amount_cents: amount_cents,
42
+ year: year,
43
+ month: month,
44
+ day: day,
45
+ context: context
46
+ })
47
+ yield res if block_given?
48
+ JSON.parse(res.body)
49
+ end
50
+
51
+ #
52
+ # Withdraw money to the current tenant account
53
+ #
54
+ # @param [String] tenant_account_id without the tenant database namespace,
55
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
56
+ # @param [Integer] amount_cents the amount to deposit in cents
57
+ # @param [Integer] year the year of the booking
58
+ # @param [Integer] month the month of the booking
59
+ # @param [Integer] day the day of the booking
60
+ # @param [Hash] context additional context information
61
+ #
62
+ # @yield [Faraday::Response] The response from the server
63
+ # @return [Hash] containing the booking details
64
+ #
65
+ def withdraw!(tenant_account_id:,
66
+ amount_cents:,
67
+ year: Date.today.year,
68
+ month: Date.today.month,
69
+ day: Date.today.day,
70
+ context: {})
71
+ res = create!(tenant_account_id: tenant_account_id, booking: {
72
+ action: :withdraw,
73
+ amount_cents: amount_cents,
74
+ year: year,
75
+ month: month,
76
+ day: day,
77
+ context: context
78
+ })
79
+ yield res if block_given?
80
+ JSON.parse(res.body)
81
+ end
82
+
83
+ #
84
+ # drops/deletes a booking
85
+ #
86
+ # @param [String] tenant_account_id without the tenant database namespace,
87
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
88
+ # @param [String] booking_id uuid of the booking
89
+ #
90
+ # @yield [Faraday::Response] The response from the server
91
+ # @return [Hash] containing the booking details
92
+ #
93
+ def drop!(tenant_account_id:, booking_id:)
94
+ drop_url = "#{build_url(tenant_account_id: tenant_account_id)}/#{booking_id}"
95
+ res = delete(drop_url) do |req|
96
+ req.headers['Content-Type'] = 'application/json'
97
+ req.body = { id: booking_id }.to_json
98
+ end
99
+ yield res if block_given?
100
+ JSON.parse(res.body)
101
+ end
102
+
103
+ #
104
+ # returns all unlocked bookings for a tenant
105
+ #
106
+ # @param [String] tenant_account_id without the tenant database namespace,
107
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
108
+ #
109
+ # @yield [Faraday::Response] The response from the server
110
+ # @return [Hash] containing the booking details
111
+ #
112
+ def unlocked(tenant_account_id:)
113
+ unlocked_url = "#{build_url(tenant_account_id: tenant_account_id)}/unlocked"
114
+ res = get(unlocked_url) do |req|
115
+ req.headers['Content-Type'] = 'application/json'
116
+ end
117
+ yield res if block_given?
118
+ JSON.parse(res.body)
119
+ end
120
+
121
+ private
122
+
123
+ #
124
+ # post action to the current tenant account
125
+ #
126
+ # @param [String] tenant_account_id without the tenant database namespace,
127
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
128
+ # @param [Hash] booking the booking details
129
+ # @option booking [String] :action the action to perform, either :deposit or :withdraw
130
+ # @option booking [Integer] :amount_cents the amount to deposit in cents
131
+ # @option booking [Integer] :year the year of the booking
132
+ # @option booking [Integer] :month the month of the booking
133
+ # @option booking [Integer] :day the day of the booking
134
+ # @option booking [Hash] :context any additional context information
135
+ #
136
+ # @return [Faraday::Response] The response from the server
137
+ #
138
+ def create!(tenant_account_id:, booking:)
139
+ create_url = build_url(tenant_account_id: tenant_account_id)
140
+ post(create_url) do |req|
141
+ req.headers['Content-Type'] = 'application/json'
142
+ req.body = booking.to_json
143
+ end
144
+ end
145
+
146
+ def build_url(tenant_account_id:)
147
+ "#{@api_url}/#{tenant_account_id}/bookings"
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ module ApiV1
5
+ #
6
+ # The client class is the main entry point for the KaChing API V1.
7
+ #
8
+ class Client
9
+ def initialize(conn:, base_url:)
10
+ @conn = conn
11
+ @base_url = base_url
12
+ @api_url = '/ka-ching/api/v1'
13
+ end
14
+
15
+ #
16
+ # The admin endpoint interface for the KaChing API V1
17
+ #
18
+ # @return [KaChing::ApiV1::Admin] The admin endpoint interface
19
+ #
20
+ def admin
21
+ @_admin ||= KaChing::ApiV1::Admin.new(conn: @conn, api_url: @api_url)
22
+ end
23
+
24
+ #
25
+ # The tenants endpoint interface for the KaChing API V1
26
+ #
27
+ # @return [KaChing::ApiV1::Tenants] The tenants endpoint interface
28
+ #
29
+ def tenants
30
+ @_tenants ||= KaChing::ApiV1::Tenants.new(conn: @conn, api_url: @api_url)
31
+ end
32
+
33
+ #
34
+ # The saldo endpoint interface for the KaChing API V1
35
+ #
36
+ # @return [KaChing::ApiV1::Saldo] The saldo endpoint interface
37
+ #
38
+ def saldo
39
+ @_saldo ||= KaChing::ApiV1::Saldo.new(conn: @conn, api_url: @api_url)
40
+ end
41
+
42
+ #
43
+ # The booking endpoint interface for the KaChing API V1
44
+ #
45
+ # @return [KaChing::ApiV1::Bookings] The booking endpoint interface
46
+ #
47
+ def bookings
48
+ @_bookings ||= KaChing::ApiV1::Bookings.new(conn: @conn, api_url: @api_url)
49
+ end
50
+
51
+ #
52
+ # The lockings endpoint interface for the KaChing API V1
53
+ #
54
+ # @return [KaChing::ApiV1::Lockings] The lockings endpoint interface
55
+ #
56
+ def lockings
57
+ @_lockings ||= KaChing::ApiV1::Lockings.new(conn: @conn, api_url: @api_url)
58
+ end
59
+
60
+ #
61
+ # The audit_logs endpoint interface for the KaChing API V1
62
+ #
63
+ # @return [KaChing::ApiV1::AuditLogs] The audit_logs endpoint interface
64
+ #
65
+ def audit_logs
66
+ @_audit_logs ||= KaChing::ApiV1::AuditLogs.new(conn: @conn, api_url: @api_url)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ module ApiV1
5
+ #
6
+ # Lockings Endpoint for the KaChing API V1
7
+ #
8
+ class Lockings
9
+ extend Forwardable
10
+
11
+ def_delegators :@conn, :get, :post, :put, :patch, :delete
12
+
13
+ def initialize(conn:, api_url:)
14
+ @conn = conn
15
+ @api_url = api_url
16
+ end
17
+
18
+ #
19
+ # Locks the bookings for a specific day
20
+ #
21
+ # @param [String] action should be "lock"
22
+ # @param [Integer] amount_cents_saldo_user_counted what the user counted as saldo
23
+ # @param [Integer] year
24
+ # @param [Integer] month
25
+ # @param [Integer] day
26
+ # @param [Hash] context object with additional information stored with the locking
27
+ #
28
+ # @yield [Faraday::Response] The response from the server
29
+ # @return [Hash] the locking
30
+ #
31
+ def lock!(tenant_account_id:,
32
+ amount_cents_saldo_user_counted:,
33
+ year: Date.today.year,
34
+ month: Date.today.month,
35
+ day: Date.today.day,
36
+ context: {})
37
+ locking = {
38
+ action: :lock,
39
+ amount_cents_saldo_user_counted: amount_cents_saldo_user_counted,
40
+ year: Integer(year),
41
+ month: Integer(month),
42
+ day: Integer(day),
43
+ context: context
44
+ }
45
+ res = post(build_url(tenant_account_id: tenant_account_id)) do |req|
46
+ req.headers['Content-Type'] = 'application/json'
47
+ req.body = locking.to_json
48
+ end
49
+ yield res if block_given?
50
+ res = JSON.parse(res.body)
51
+ res['record']['bookings_json'] = JSON.parse(res['record']['bookings_json'])
52
+ res['context'] = JSON.parse(JSON.parse(res['context']))
53
+ res
54
+ end
55
+
56
+ #
57
+ # Unlocks last locking
58
+ #
59
+ # @yield [Faraday::Response] The response from the server
60
+ # @return [Hash]
61
+ #
62
+ def unlock!(tenant_account_id:)
63
+ res = delete(build_url(tenant_account_id: tenant_account_id)) do |req|
64
+ req.headers['Content-Type'] = 'application/json'
65
+ end
66
+ yield res if block_given?
67
+ res = JSON.parse(res.body)
68
+ res['bookings_json'] = JSON.parse(res['bookings_json'])
69
+ res['context'] = JSON.parse(JSON.parse(res['context']))
70
+ res
71
+ end
72
+
73
+ def all(tenant_account_id:, page: 1, per_page: 10)
74
+ res = get(build_url(tenant_account_id: tenant_account_id), { page: page, per_page: per_page }) do |req|
75
+ req.headers['Content-Type'] = 'application/json'
76
+ end
77
+ yield res if block_given?
78
+ JSON.parse(res.body)
79
+ end
80
+
81
+ #
82
+ # Get all lockings for a given year
83
+ #
84
+ # @yield [Faraday::Response] The response from the server
85
+ # @return [Hash]
86
+ #
87
+ def of_year(tenant_account_id:, year:, page: 1, per_page: 100)
88
+ res = get(build_url(tenant_account_id: tenant_account_id),
89
+ { year: year, page: page, per_page: per_page }) do |req|
90
+ req.headers['Content-Type'] = 'application/json'
91
+ req.body = { year: year }.to_json
92
+ end
93
+ yield res if block_given?
94
+ JSON.parse(res.body)
95
+ end
96
+
97
+ #
98
+ # Get all lockings for a given year in month
99
+ #
100
+ # @yield [Faraday::Response] The response from the server
101
+ # @return [Hash]
102
+ #
103
+ def of_year_month(tenant_account_id:, year:, month:, page: 1, per_page: 100)
104
+ res = get(build_url(tenant_account_id: tenant_account_id),
105
+ { year: year, month: month, page: page, per_page: per_page }) do |req|
106
+ req.headers['Content-Type'] = 'application/json'
107
+ req.body = { year: year, month: month }.to_json
108
+ end
109
+ yield res if block_given?
110
+ JSON.parse(res.body)
111
+ end
112
+
113
+ #
114
+ # Get all lockings for a given year in month at day
115
+ #
116
+ # @yield [Faraday::Response] The response from the server
117
+ # @return [Hash]
118
+ #
119
+ def of_year_month_day(tenant_account_id:, year:, month:, day:)
120
+ res = get(build_url(tenant_account_id: tenant_account_id).to_s,
121
+ { year: year, month: month, day: day, page: page, per_page: per_page }) do |req|
122
+ req.headers['Content-Type'] = 'application/json'
123
+ req.body = { year: year, month: month, day: day }.to_json
124
+ end
125
+ yield res if block_given?
126
+ JSON.parse(res.body)
127
+ end
128
+
129
+ private
130
+
131
+ def build_url(tenant_account_id:)
132
+ "#{@api_url}/#{tenant_account_id}/lockings"
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KaChing
4
+ module ApiV1
5
+ #
6
+ # Saldo Endpoint for the KaChing API V1
7
+ #
8
+ class Saldo
9
+ extend Forwardable
10
+
11
+ def_delegators :@conn, :get, :post, :put, :patch, :delete
12
+
13
+ def initialize(conn:, api_url:)
14
+ @conn = conn
15
+ @api_url = api_url
16
+ end
17
+
18
+ #
19
+ # Get the current saldo (in cents)
20
+ #
21
+ # @param [String] tenant_account_id without the tenant database namespace,
22
+ # e.g. 'user_1' instead of 'kaching_tenant_user_1'
23
+ #
24
+ # @yield [Faraday::Response] The response from the server
25
+ # @return [Hash] The current saldo
26
+ #
27
+ def current(tenant_account_id:)
28
+ res = get(build_url(tenant_account_id: tenant_account_id)) do |req|
29
+ req.headers['Content-Type'] = 'application/json'
30
+ end
31
+ yield res if block_given?
32
+ JSON.parse(res.body)
33
+ end
34
+
35
+ private
36
+
37
+ def build_url(tenant_account_id:)
38
+ "#{@api_url}/#{tenant_account_id}/saldo"
39
+ end
40
+ end
41
+ end
42
+ end