ka-ching-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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