supersaas-api-client 2.0.4 → 2.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +129 -43
  3. data/examples/appointments.rb +16 -22
  4. data/examples/forms.rb +9 -5
  5. data/examples/groups.rb +2 -1
  6. data/examples/promotions.rb +5 -4
  7. data/examples/schedules.rb +7 -7
  8. data/examples/slots.rb +42 -0
  9. data/examples/users.rb +1 -1
  10. data/lib/supersaas-api-client/api/appointments.rb +38 -43
  11. data/lib/supersaas-api-client/api/base_api.rb +2 -0
  12. data/lib/supersaas-api-client/api/forms.rb +3 -1
  13. data/lib/supersaas-api-client/api/groups.rb +1 -1
  14. data/lib/supersaas-api-client/api/hooks.rb +25 -0
  15. data/lib/supersaas-api-client/api/promotions.rb +1 -1
  16. data/lib/supersaas-api-client/api/schedules.rb +9 -1
  17. data/lib/supersaas-api-client/api/slots.rb +63 -0
  18. data/lib/supersaas-api-client/api/users.rb +39 -41
  19. data/lib/supersaas-api-client/client.rb +48 -32
  20. data/lib/supersaas-api-client/exception.rb +8 -0
  21. data/lib/supersaas-api-client/models/appointment.rb +5 -4
  22. data/lib/supersaas-api-client/models/base_model.rb +18 -2
  23. data/lib/supersaas-api-client/models/field_list.rb +11 -2
  24. data/lib/supersaas-api-client/models/form.rb +1 -2
  25. data/lib/supersaas-api-client/models/slot.rb +1 -1
  26. data/lib/supersaas-api-client/models/user.rb +2 -0
  27. data/lib/supersaas-api-client/version.rb +1 -1
  28. data/lib/supersaas.rb +2 -0
  29. metadata +11 -17
  30. data/.github/workflows/actions.yaml +0 -21
  31. data/.gitignore +0 -13
  32. data/.rubocop.yml +0 -1
  33. data/.rubocop_todo.yml +0 -296
  34. data/Gemfile +0 -5
  35. data/Gemfile.lock +0 -49
  36. data/Rakefile +0 -12
  37. data/bin/console +0 -15
  38. data/bin/setup +0 -8
  39. data/supersaas-api-client.gemspec +0 -28
@@ -3,10 +3,10 @@
3
3
  module Supersaas
4
4
  # REF: https://www.supersaas.com/info/dev/appointment_api
5
5
  class Appointments < BaseApi
6
- def agenda(schedule_id, user_id, from_time = nil, slot = false)
6
+ def agenda(schedule_id, user, from_time = nil, slot = false)
7
7
  path = "/agenda/#{validate_id(schedule_id)}"
8
8
  params = {
9
- user: validate_present(user_id),
9
+ user: validate_present(user),
10
10
  from: from_time && validate_datetime(from_time)
11
11
  }
12
12
  params.merge!(slot: true) if slot
@@ -14,6 +14,7 @@ module Supersaas
14
14
  map_slots_or_bookings(res)
15
15
  end
16
16
 
17
+ # LEGACY METHOD WILL BE REMOVED USE AGENDA
17
18
  def agenda_slots(schedule_id, user_id, from_time = nil)
18
19
  path = "/agenda/#{validate_id(schedule_id)}"
19
20
  params = {
@@ -38,14 +39,16 @@ module Supersaas
38
39
  map_slots_or_bookings(res)
39
40
  end
40
41
 
41
- def list(schedule_id, form = nil, start_time = nil, limit = nil)
42
+ def list(schedule_id, form = nil, start_time = nil, limit = nil, finish = nil, offset = nil)
42
43
  path = '/bookings'
43
44
  params = {
44
45
  schedule_id: validate_id(schedule_id),
45
46
  form: form ? true : nil,
46
- start: start_time ? validate_datetime(start_time) : nil
47
+ start: start_time ? validate_datetime(start_time) : nil,
48
+ finish: finish ? validate_datetime(finish) : nil
47
49
  }
48
50
  params.merge!(limit: validate_number(limit)) if limit
51
+ params.merge!(offset: validate_number(offset)) if offset
49
52
  res = client.get(path, params)
50
53
  map_slots_or_bookings(res)
51
54
  end
@@ -64,24 +67,7 @@ module Supersaas
64
67
  webhook: webhook,
65
68
  user_id: validate_id(user_id),
66
69
  form: form ? true : nil,
67
- booking: {
68
- start: attributes[:start],
69
- finish: attributes[:finish],
70
- name: attributes[:name],
71
- email: attributes[:email],
72
- full_name: attributes[:full_name],
73
- address: attributes[:address],
74
- mobile: attributes[:mobile],
75
- phone: attributes[:phone],
76
- country: attributes[:country],
77
- field_1: attributes[:field_1],
78
- field_2: attributes[:field_2],
79
- field_1_r: attributes[:field_1_r],
80
- field_2_r: attributes[:field_2_r],
81
- super_field: attributes[:super_field],
82
- resource_id: attributes[:resource_id],
83
- slot_id: attributes[:slot_id]
84
- }
70
+ booking: booking_params(attributes)
85
71
  }
86
72
  params[:booking].compact!
87
73
  client.post(path, params)
@@ -91,24 +77,7 @@ module Supersaas
91
77
  path = "/bookings/#{validate_id(appointment_id)}"
92
78
  params = {
93
79
  schedule_id: schedule_id,
94
- booking: {
95
- start: attributes[:start],
96
- finish: attributes[:finish],
97
- name: attributes[:name],
98
- email: attributes[:email],
99
- full_name: attributes[:full_name],
100
- address: attributes[:address],
101
- mobile: attributes[:mobile],
102
- phone: attributes[:phone],
103
- country: attributes[:country],
104
- field_1: attributes[:field_1],
105
- field_2: attributes[:field_2],
106
- field_1_r: attributes[:field_1_r],
107
- field_2_r: attributes[:field_2_r],
108
- super_field: attributes[:super_field],
109
- resource_id: attributes[:resource_id],
110
- slot_id: attributes[:slot_id]
111
- }
80
+ booking: booking_params(attributes)
112
81
  }
113
82
 
114
83
  params.merge!(form: form) if form
@@ -117,9 +86,10 @@ module Supersaas
117
86
  client.put(path, params)
118
87
  end
119
88
 
120
- def delete(schedule_id, appointment_id)
89
+ def delete(schedule_id, appointment_id, webhook = nil)
121
90
  path = "/bookings/#{validate_id(appointment_id)}"
122
91
  params = { schedule_id: validate_id(schedule_id) }
92
+ params.merge!(webhook: webhook) if webhook
123
93
  client.delete(path, nil, params)
124
94
  end
125
95
 
@@ -130,8 +100,8 @@ module Supersaas
130
100
  map_slots_or_bookings(res)
131
101
  end
132
102
 
133
- def range(schedule_id, today = false, from_time = nil, to = nil, slot = false, user = nil, resource_id = nil, service_id = nil,
134
- limit = nil, offset = nil)
103
+ def range(schedule_id, today = false, from_time = nil, to = nil, slot = false, user = nil, resource_id = nil,
104
+ service_id = nil, limit = nil, offset = nil)
135
105
  path = "/range/#{validate_id(schedule_id)}"
136
106
  params = {}
137
107
  params = build_param(params, from_time, to, slot, user, limit, offset, resource_id, service_id)
@@ -142,6 +112,31 @@ module Supersaas
142
112
 
143
113
  private
144
114
 
115
+ # Input attributes only. description is for resource schedules, quantity (number of places) for capacity schedules, form_id attaches an existing form
116
+ def booking_params(attributes)
117
+ {
118
+ start: attributes[:start],
119
+ finish: attributes[:finish],
120
+ email: attributes[:email],
121
+ res_name: attributes[:name],
122
+ full_name: attributes[:full_name],
123
+ address: attributes[:address],
124
+ mobile: attributes[:mobile],
125
+ phone: attributes[:phone],
126
+ country: attributes[:country],
127
+ field_1: attributes[:field_1],
128
+ field_2: attributes[:field_2],
129
+ field_1_r: attributes[:field_1_r],
130
+ field_2_r: attributes[:field_2_r],
131
+ super_field: attributes[:super_field],
132
+ resource_id: attributes[:resource_id],
133
+ slot_id: attributes[:slot_id],
134
+ description: attributes[:description],
135
+ quantity: attributes[:quantity],
136
+ form_id: attributes[:form_id]
137
+ }
138
+ end
139
+
145
140
  def map_slots_or_bookings(obj, slot = false)
146
141
  if obj.is_a?(Array) && slot
147
142
  obj.map { |attributes| Supersaas::Slot.new(attributes) }
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'date'
4
+
3
5
  module Supersaas
4
6
  class BaseApi
5
7
  attr_accessor :client
@@ -3,11 +3,13 @@
3
3
  module Supersaas
4
4
  # REF: https://www.supersaas.com/info/dev/form_api
5
5
  class Forms < BaseApi
6
- def list(template_form_id, from_time = nil, user = nil)
6
+ def list(template_form_id, from_time = nil, user = nil, limit = nil, offset = nil)
7
7
  path = '/forms'
8
8
  params = { form_id: validate_id(template_form_id) }
9
9
  params.merge!(from: validate_datetime(from_time)) if from_time
10
10
  params.merge!(user: validate_user(user)) if user
11
+ params.merge!(limit: limit) if limit
12
+ params.merge!(offset: offset) if offset
11
13
  res = client.get(path, params)
12
14
  res.map { |attributes| Supersaas::Form.new(attributes) }
13
15
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Supersaas
4
+ # REF: https://www.supersaas.com/info/dev/information_api#groups
4
5
  class Groups < BaseApi
5
- # REF: https://www.supersaas.com/info/dev/information_api#groups
6
6
  def list
7
7
  path = '/groups'
8
8
  res = client.get(path)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Supersaas
4
+ # REF: https://www.supersaas.com/info/dev/webhooks
5
+ class Hooks < BaseApi
6
+ # event: the trigger, e.g. 'U' (user created), 'M' (user changed), 'N' (new appointment), 'C' (changed appointment)
7
+ # parent_id: the schedule id for appointment events, the super form id for form events, else the account id
8
+ # Returns { 'id' => ..., 'parent_id' => ... }
9
+ def create(target_url, event, parent_id)
10
+ path = '/hooks'
11
+ params = {
12
+ target_url: validate_present(target_url),
13
+ event: validate_present(event),
14
+ parent_id: validate_id(parent_id)
15
+ }
16
+ client.post(path, params)
17
+ end
18
+
19
+ def delete(hook_id, parent_id)
20
+ path = '/hooks'
21
+ query = { id: validate_id(hook_id), parent_id: validate_id(parent_id) }
22
+ client.delete(path, nil, query)
23
+ end
24
+ end
25
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Supersaas
4
+ # REF: https://www.supersaas.com/info/dev/promotion_api
4
5
  class Promotions < BaseApi
5
- # REF: https://www.supersaas.com/info/dev/promotion_api
6
6
  def list(limit = nil, offset = nil)
7
7
  path = '/promotions'
8
8
  params = {}
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Supersaas
4
+ # REF: https://www.supersaas.com/info/dev/information_api
4
5
  class Schedules < BaseApi
5
- # REF: https://www.supersaas.com/info/dev/information_api
6
6
  def list
7
7
  path = '/schedules'
8
8
  res = client.get(path)
@@ -22,5 +22,13 @@ module Supersaas
22
22
  res = client.get(path, query)
23
23
  res.map { |attributes| Supersaas::FieldList.new(attributes) }
24
24
  end
25
+
26
+ # The fields that can be set when creating a booking
27
+ def input_field_list(schedule_id)
28
+ path = '/input_field_list'
29
+ query = { schedule_id: validate_id(schedule_id) }
30
+ res = client.get(path, query)
31
+ res.map { |attributes| Supersaas::FieldList.new(attributes) }
32
+ end
25
33
  end
26
34
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Supersaas
4
+ # REF: https://www.supersaas.com/info/dev/appointment_api#slots_api
5
+ # Only available for capacity type schedules.
6
+ class Slots < BaseApi
7
+ def list(schedule_id, start_time = nil, finish = nil, limit = nil, offset = nil)
8
+ path = '/slots'
9
+ params = {
10
+ schedule_id: validate_id(schedule_id),
11
+ start: start_time ? validate_datetime(start_time) : nil,
12
+ finish: finish ? validate_datetime(finish) : nil
13
+ }
14
+ params.merge!(limit: validate_number(limit)) if limit
15
+ params.merge!(offset: validate_number(offset)) if offset
16
+ res = client.get(path, params)
17
+ res.map { |attributes| Supersaas::Slot.new(attributes) }
18
+ end
19
+
20
+ def get(schedule_id, slot_id)
21
+ params = { schedule_id: validate_id(schedule_id) }
22
+ path = "/slots/#{validate_id(slot_id)}"
23
+ res = client.get(path, params)
24
+ Supersaas::Slot.new(res)
25
+ end
26
+
27
+ def create(schedule_id, attributes)
28
+ path = '/slots'
29
+ query = { schedule_id: validate_id(schedule_id) }
30
+ client.post(path, { slot: slot_params(attributes) }, query)
31
+ end
32
+
33
+ def update(schedule_id, slot_id, attributes)
34
+ path = "/slots/#{validate_id(slot_id)}"
35
+ query = { schedule_id: validate_id(schedule_id) }
36
+ client.put(path, { slot: slot_params(attributes) }, query)
37
+ end
38
+
39
+ def delete(schedule_id, slot_id)
40
+ path = "/slots/#{validate_id(slot_id)}"
41
+ params = { schedule_id: validate_id(schedule_id) }
42
+ client.delete(path, nil, params)
43
+ end
44
+
45
+ private
46
+
47
+ # capacity: an Integer, or '-' for unlimited, '0' for no places
48
+ def slot_params(attributes)
49
+ params = {
50
+ start: attributes[:start] && validate_datetime(attributes[:start]),
51
+ finish: attributes[:finish] && validate_datetime(attributes[:finish]),
52
+ title: attributes[:title],
53
+ description: attributes[:description],
54
+ location: attributes[:location],
55
+ capacity: attributes[:capacity],
56
+ price: attributes[:price],
57
+ color: attributes[:color]
58
+ }
59
+ params.compact!
60
+ params
61
+ end
62
+ end
63
+ end
@@ -10,13 +10,17 @@ module Supersaas
10
10
  limit: limit && validate_number(limit),
11
11
  offset: offset && validate_number(offset)
12
12
  }
13
+ params.compact!
13
14
  res = client.get(path, params)
14
15
  res.map { |attributes| Supersaas::User.new(attributes) }
15
16
  end
16
17
 
17
- def get(user_id)
18
+ def get(user_id, form = nil)
18
19
  path = user_path(user_id)
19
- res = client.get(path)
20
+ params = {
21
+ form: form ? true : nil
22
+ }
23
+ res = client.get(path, params)
20
24
  Supersaas::User.new(res)
21
25
  end
22
26
 
@@ -24,24 +28,7 @@ module Supersaas
24
28
  path = user_path(user_id)
25
29
  query = { webhook: webhook }
26
30
  query.merge!(duplicate: validate_duplicate(duplicate)) if duplicate
27
- params = {
28
- user: {
29
- name: validate_name(attributes[:name]),
30
- email: attributes[:email],
31
- password: attributes[:password],
32
- full_name: attributes[:full_name],
33
- address: attributes[:address],
34
- mobile: attributes[:mobile],
35
- phone: attributes[:phone],
36
- country: attributes[:country],
37
- timezone: attributes[:timezone],
38
- field_1: attributes[:field_1],
39
- field_2: attributes[:field_2],
40
- super_field: attributes[:super_field],
41
- credit: attributes[:credit] && validate_number(attributes[:credit]),
42
- role: attributes[:role] && validate_options(attributes[:role], User::ROLES)
43
- }
44
- }
31
+ params = { user: user_params(attributes) }
45
32
  client.post(path, params, query)
46
33
  end
47
34
 
@@ -49,31 +36,14 @@ module Supersaas
49
36
  path = user_path(user_id)
50
37
  query = { webhook: webhook }
51
38
  query.merge!(notfound: validate_notfound(notfound)) if notfound
52
- params = {
53
- user: {
54
- name: validate_name(attributes[:name]),
55
- email: attributes[:email],
56
- password: attributes[:password],
57
- full_name: attributes[:full_name],
58
- address: attributes[:address],
59
- mobile: attributes[:mobile],
60
- phone: attributes[:phone],
61
- country: attributes[:country],
62
- timezone: attributes[:timezone],
63
- field_1: attributes[:field_1],
64
- field_2: attributes[:field_2],
65
- super_field: attributes[:super_field],
66
- credit: attributes[:credit] && validate_number(attributes[:credit]),
67
- role: attributes[:role] && validate_options(attributes[:role], User::ROLES)
68
- }
69
- }
70
- params[:user].compact!
39
+ params = { user: user_params(attributes) }
71
40
  client.put(path, params, query)
72
41
  end
73
42
 
74
- def delete(user_id)
43
+ def delete(user_id, webhook = nil)
75
44
  path = user_path(user_id)
76
- client.delete(path)
45
+ params = { webhook: webhook }
46
+ client.delete(path, nil, params)
77
47
  end
78
48
 
79
49
  def field_list
@@ -82,6 +52,13 @@ module Supersaas
82
52
  res.map { |attributes| Supersaas::FieldList.new(attributes) }
83
53
  end
84
54
 
55
+ # The fields that can be set when creating a user
56
+ def input_field_list
57
+ path = '/input_field_list'
58
+ res = client.get(path)
59
+ res.map { |attributes| Supersaas::FieldList.new(attributes) }
60
+ end
61
+
85
62
  private
86
63
 
87
64
  def user_path(user_id)
@@ -91,5 +68,26 @@ module Supersaas
91
68
  "/users/#{validate_user(user_id)}"
92
69
  end
93
70
  end
71
+
72
+ # The user attributes the API accepts, leaving out the ones that are nil
73
+ def user_params(attributes)
74
+ {
75
+ name: validate_name(attributes[:name]),
76
+ email: attributes[:email],
77
+ password: attributes[:password],
78
+ full_name: attributes[:full_name],
79
+ address: attributes[:address],
80
+ mobile: attributes[:mobile],
81
+ phone: attributes[:phone],
82
+ country: attributes[:country],
83
+ timezone: attributes[:timezone],
84
+ field_1: attributes[:field_1],
85
+ field_2: attributes[:field_2],
86
+ super_field: attributes[:super_field],
87
+ credit: attributes[:credit] && validate_number(attributes[:credit]),
88
+ role: attributes[:role] && validate_options(attributes[:role], User::ROLES),
89
+ group: attributes[:group] && validate_number(attributes[:group])
90
+ }.compact
91
+ end
94
92
  end
95
93
  end
@@ -47,6 +47,10 @@ module Supersaas
47
47
  @schedules ||= Schedules.new(self)
48
48
  end
49
49
 
50
+ def slots
51
+ @slots ||= Slots.new(self)
52
+ end
53
+
50
54
  def users
51
55
  @users ||= Users.new(self)
52
56
  end
@@ -59,6 +63,16 @@ module Supersaas
59
63
  @groups ||= Groups.new(self)
60
64
  end
61
65
 
66
+ def hooks
67
+ @hooks ||= Hooks.new(self)
68
+ end
69
+
70
+ # Checks the credentials, returns true or raises Supersaas::Exception
71
+ def ping
72
+ get('/ping')
73
+ true
74
+ end
75
+
62
76
  def get(path, query = {})
63
77
  request(:get, path, {}, query)
64
78
  end
@@ -77,12 +91,14 @@ module Supersaas
77
91
 
78
92
  private
79
93
 
80
- # The rate limiter allows a maximum of 4 requests within the specified time window
94
+ # The rate limiter allows at most MAX_REQUESTS requests per WINDOW_SIZE seconds
81
95
  WINDOW_SIZE = 1 # seconds
82
- MAX_PER_WINDOW = 4
96
+ MAX_REQUESTS = 4
97
+
83
98
  def throttle
84
99
  # A queue to store timestamps of requests made within the rate limiting window
85
- @queue ||= Array.new(MAX_PER_WINDOW)
100
+ @queue ||= Array.new(MAX_REQUESTS)
101
+
86
102
  # Represents the timestamp of the oldest request within the time window
87
103
  oldest_request = @queue.push(Time.now).shift
88
104
  # This ensures that the client does not make requests faster than the defined rate limit
@@ -136,7 +152,7 @@ module Supersaas
136
152
  if verbose
137
153
  puts '### SuperSaaS Client Request:'
138
154
  puts "#{http_method} #{path}"
139
- puts params.to_json
155
+ puts filter_secrets(params).to_json
140
156
  puts '------------------------------'
141
157
  end
142
158
 
@@ -159,6 +175,8 @@ module Supersaas
159
175
 
160
176
  code = res.code.to_i
161
177
  case code
178
+ when 204
179
+ {}
162
180
  when 200, 201
163
181
  if http_method == :post && res['location'] =~ /www.supersaas.com/
164
182
  res['location']
@@ -171,34 +189,24 @@ module Supersaas
171
189
  end
172
190
 
173
191
  def handle_errors(code, res)
174
- print_errors(res)
175
- case code
176
- when 422
177
- raise Supersaas::Exception, 'HTTP Request Error: Unprocessable Content'
178
- when 400
179
- raise Supersaas::Exception, 'HTTP Request Error: Bad Request'
180
- when 401
181
- raise Supersaas::Exception, 'HTTP Request Error: Unauthorised'
182
- when 404
183
- raise Supersaas::Exception, 'HTTP Request Error: Not Found'
184
- when 501
185
- raise Supersaas::Exception, 'Not yet implemented for service type schedule'
186
- when 403
187
- raise Supersaas::Exception, 'Unauthorized'
188
- when 405
189
- raise Supersaas::Exception, 'Not available for capacity type schedule'
190
- else
191
- raise Supersaas::Exception, "HTTP Request Error: #{code}"
192
- end
193
- end
194
-
195
- def print_errors(res)
196
- json_body = json_body(res)
197
- return unless json_body[:errors]
198
-
199
- errors each do |error|
200
- puts "Error code: #{error['code']}, #{error['title']}"
201
- end
192
+ message = case code
193
+ when 422 then 'HTTP Request Error: Unprocessable Content'
194
+ when 400 then 'HTTP Request Error: Bad Request'
195
+ when 401 then 'HTTP Request Error: Unauthorised'
196
+ when 404 then 'HTTP Request Error: Not Found'
197
+ when 501 then 'Not yet implemented for service type schedule'
198
+ when 403 then 'Unauthorized'
199
+ when 405 then 'Not available for this schedule type'
200
+ else "HTTP Request Error: #{code}"
201
+ end
202
+ raise Supersaas::Exception.new(message, status: code, errors: parsed_errors(res))
203
+ end
204
+
205
+ # The errors array of a JSON error body like {"errors":[{"status":"404","title":"Not found"}]}
206
+ def parsed_errors(res)
207
+ body = json_body(res)
208
+ errors = body['errors'] if body.is_a?(Hash)
209
+ errors.is_a?(Array) ? errors : []
202
210
  end
203
211
 
204
212
  def json_body(res)
@@ -216,6 +224,14 @@ module Supersaas
216
224
  end
217
225
  end
218
226
 
227
+ # A copy of the params with password values replaced, for verbose output only
228
+ def filter_secrets(value)
229
+ return value.map { |v| filter_secrets(v) } if value.is_a?(Array)
230
+ return value unless value.is_a?(Hash)
231
+
232
+ value.each_with_object({}) { |(k, v), h| h[k] = k.to_s == 'password' ? '[FILTERED]' : filter_secrets(v) }
233
+ end
234
+
219
235
  class Configuration
220
236
  DEFAULT_HOST = 'https://www.supersaas.com'
221
237
 
@@ -6,5 +6,13 @@ module Supersaas
6
6
  @code = nil
7
7
 
8
8
  attr_accessor :field, :code
9
+ # HTTP status code and the server's parsed errors, e.g. [{"status"=>"404", "title"=>"Not found"}]
10
+ attr_reader :status, :errors
11
+
12
+ def initialize(message = nil, status: nil, errors: [])
13
+ super(message)
14
+ @status = status
15
+ @errors = errors
16
+ end
9
17
  end
10
18
  end
@@ -2,10 +2,11 @@
2
2
 
3
3
  module Supersaas
4
4
  class Appointment < BaseModel
5
- attr_accessor :address, :country, :created_by, :created_on, :deleted, :description, :email, :field_1, :field_2,
6
- :field_1_r, :field_2_r, :finish, :form_id, :full_name, :id, :mobile, :name, :phone, :price, :res_name,
7
- :resource_id, :schedule_id, :schedule_name, :service_id, :service_name, :slot_id, :start, :status,
8
- :super_field, :updated_by, :updated_on, :user_id, :waitlisted
5
+ attr_accessor :field_1, :field_2, :field_2_r, :field_1_r, :country, :address, :mobile, :phone, :email, :price_cents,
6
+ :finish, :start, :service_name, :res_name, :id, :quantity, :status_message, :deleted,
7
+ :created_on, :slot_id, :created_by, :price, :status, :full_name, :super_field, :updated_by, :form_id,
8
+ :updated_on, :user_id, :waitlisted, :resource_id, :schedule_name, :schedule_id, :service_id
9
+
9
10
  attr_reader :form, :slot
10
11
 
11
12
  def form=(value)
@@ -10,12 +10,28 @@ module Supersaas
10
10
  assign_attributes(attributes)
11
11
  end
12
12
 
13
+ # Hash-style access to the raw attribute value, with a String or Symbol key.
14
+ # Keeps code working that read nested data as a Hash, e.g. appointment.form['content'].
15
+ def [](key)
16
+ return attributes[key] if attributes.key?(key)
17
+
18
+ case key
19
+ when Symbol then attributes[key.to_s]
20
+ when String then attributes[key.to_sym]
21
+ end
22
+ end
23
+
24
+ def to_h
25
+ attributes.to_h
26
+ end
27
+
13
28
  private
14
29
 
15
30
  def assign_attributes(attributes)
16
31
  attributes.each do |key, value|
17
- self.class.module_eval { attr_accessor key }
18
- public_send("#{key}=", value) if respond_to?("#{key}=")
32
+ # Keep custom writers such as Appointment#form=, which wrap nested data in models
33
+ self.class.module_eval { attr_accessor key } unless self.class.method_defined?("#{key}=")
34
+ public_send("#{key}=", value)
19
35
  end
20
36
  end
21
37
  end
@@ -2,11 +2,20 @@
2
2
 
3
3
  module Supersaas
4
4
  class FieldList < BaseModel
5
- attr_accessor :name, :type, :label, :advanced
5
+ attr_accessor :name, :type, :label, :advanced, :required
6
6
  attr_reader :spec
7
7
 
8
8
  def spec=(value)
9
- @spec = value.is_a?(Array) ? value.map { |attributes| JSON.parse(attributes) } : value
9
+ @spec = value.is_a?(Array) ? value.map { |item| parse_spec_item(item) } : value
10
+ end
11
+
12
+ private
13
+
14
+ # Items arrive as JSON strings; anything else (or invalid JSON) is kept as is
15
+ def parse_spec_item(item)
16
+ item.is_a?(String) ? JSON.parse(item) : item
17
+ rescue JSON::ParserError
18
+ item
10
19
  end
11
20
  end
12
21
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Supersaas
4
4
  class Form < BaseModel
5
- attr_accessor :content, :created_on, :deleted, :id, :reservation_process_id, :super_form_id, :uniq, :updated_name,
6
- :updated_on, :user_id
5
+ attr_accessor :content, :created_on, :deleted, :id, :reservation_process_id, :super_form_id, :updated_on, :user_id
7
6
  end
8
7
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Supersaas
4
4
  class Slot < BaseModel
5
- attr_accessor :count, :description, :finish, :id, :location, :name, :start, :title
5
+ attr_accessor :capacity, :color, :count, :description, :finish, :id, :location, :name, :price, :start, :title
6
6
  attr_reader :bookings
7
7
 
8
8
  def bookings=(value)
@@ -5,6 +5,8 @@ module Supersaas
5
5
  ROLES = [3, 4, -1].freeze
6
6
 
7
7
  attr_reader :form
8
+ attr_accessor :name, :email, :password, :full_name, :address, :mobile, :phone, :country, :timezone, :field_1,
9
+ :field_2, :super_field, :credit, :role, :group, :webhook, :id, :fk, :created_on, :updated_on
8
10
 
9
11
  def form=(value)
10
12
  @form = value.is_a?(Hash) ? Supersaas::Form.new(value) : value