supersaas-api-client 0.9.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.
@@ -0,0 +1,152 @@
1
+ module Supersaas
2
+ # REF: https://www.supersaas.com/info/dev/appointment_api
3
+ class Appointments < BaseApi
4
+ def agenda(schedule_id, user_id, from_time=nil)
5
+ path = "/agenda/#{validate_id(schedule_id)}"
6
+ params = {
7
+ user: validate_present(user_id),
8
+ from: from_time && validate_datetime(from_time)
9
+ }
10
+ res = client.get(path, params)
11
+ map_slots_or_bookings(res)
12
+ end
13
+
14
+ def agenda_slots(schedule_id, user_id, from_time=nil)
15
+ path = "/agenda/#{validate_id(schedule_id)}"
16
+ params = {
17
+ user: validate_present(user_id),
18
+ from: from_time && validate_datetime(from_time),
19
+ slot: true
20
+ }
21
+ res = client.get(path, params)
22
+ map_slots_or_bookings(res, true)
23
+ end
24
+
25
+ def available(schedule_id, from_time, length_minutes=nil, resource=nil, full=nil, limit=nil)
26
+ path = "/free/#{validate_id(schedule_id)}"
27
+ params = {
28
+ length: length_minutes && validate_number(length_minutes),
29
+ from: validate_datetime(from_time),
30
+ resource: resource,
31
+ full: full ? true : nil,
32
+ maxresults: limit && validate_number(limit)
33
+ }
34
+ res = client.get(path, params)
35
+ map_slots_or_bookings(res)
36
+ end
37
+
38
+ def list(schedule_id, form=nil, start_time=nil, limit=nil)
39
+ path = "/bookings"
40
+ params = {
41
+ schedule_id: validate_id(schedule_id),
42
+ form: form ? true : nil,
43
+ start: start_time ? validate_datetime(start_time) : nil,
44
+ limit: limit && validate_number(limit)
45
+ }
46
+ res = client.get(path, params)
47
+ map_slots_or_bookings(res)
48
+ end
49
+
50
+ def get(schedule_id, appointment_id)
51
+ params = {schedule_id: validate_id(schedule_id)}
52
+ path = "/bookings/#{validate_id(appointment_id)}"
53
+ res = client.get(path, params)
54
+ Supersaas::Appointment.new(res)
55
+ end
56
+
57
+ def create(schedule_id, user_id, attributes, form=nil, webhook=nil)
58
+ path = "/bookings"
59
+ params = {
60
+ schedule_id: schedule_id,
61
+ webhook: webhook,
62
+ user_id: validate_id(user_id),
63
+ form: form ? true : nil,
64
+ booking: {
65
+ start: attributes[:start],
66
+ finish: attributes[:finish],
67
+ name: attributes[:name],
68
+ email: attributes[:email],
69
+ full_name: attributes[:full_name],
70
+ address: attributes[:address],
71
+ mobile: attributes[:mobile],
72
+ phone: attributes[:phone],
73
+ country: attributes[:country],
74
+ field_1: attributes[:field_1],
75
+ field_2: attributes[:field_2],
76
+ field_1_r: attributes[:field_1_r],
77
+ field_2_r: attributes[:field_2_r],
78
+ super_field: attributes[:super_field],
79
+ resource_id: attributes[:resource_id],
80
+ slot_id: attributes[:slot_id]
81
+ }
82
+ }
83
+ client.post(path, params)
84
+ end
85
+
86
+ def update(schedule_id, appointment_id, attributes, form=nil, webhook=nil)
87
+ path = "/bookings/#{validate_id(appointment_id)}"
88
+ params = {
89
+ schedule_id: schedule_id,
90
+ webhook: webhook,
91
+ form: form,
92
+ booking: {
93
+ start: attributes[:start],
94
+ finish: attributes[:finish],
95
+ name: validate_present(attributes[:name]),
96
+ email: attributes[:email],
97
+ full_name: attributes[:full_name],
98
+ address: attributes[:address],
99
+ mobile: attributes[:mobile],
100
+ phone: attributes[:phone],
101
+ country: attributes[:country],
102
+ field_1: attributes[:field_1],
103
+ field_2: attributes[:field_2],
104
+ field_1_r: attributes[:field_1_r],
105
+ field_2_r: attributes[:field_2_r],
106
+ super_field: attributes[:super_field],
107
+ resource_id: attributes[:resource_id],
108
+ slot_id: attributes[:slot_id]
109
+ }
110
+ }
111
+ client.put(path, params)
112
+ end
113
+
114
+ def delete(appointment_id)
115
+ path = "/bookings/#{validate_id(appointment_id)}"
116
+ client.delete(path)
117
+ end
118
+
119
+ def changes(schedule_id, from_time)
120
+ path = "/changes/#{validate_id(schedule_id)}"
121
+ params = { from: validate_datetime(from_time) }
122
+ res = client.get(path, params)
123
+ map_slots_or_bookings(res)
124
+ end
125
+
126
+ def changes_slots(schedule_id, from_time)
127
+ path = "/changes/#{validate_id(schedule_id)}"
128
+ params = {
129
+ from: validate_datetime(from_time),
130
+ slot: true
131
+ }
132
+ res = client.get(path, params)
133
+ map_slots_or_bookings(res, true)
134
+ end
135
+
136
+ private
137
+
138
+ def map_slots_or_bookings(obj, slot=false)
139
+ if obj.is_a?(Array) && slot
140
+ obj.map { |attributes| Supersaas::Slot.new(attributes) }
141
+ elsif obj.is_a?(Array)
142
+ obj.map { |attributes| Supersaas::Appointment.new(attributes) }
143
+ elsif obj['slots']
144
+ obj['slots'].map { |attributes| Supersaas::Slot.new(attributes) }
145
+ elsif obj['bookings']
146
+ obj['bookings'].map { |attributes| Supersaas::Appointment.new(attributes) }
147
+ else
148
+ []
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,55 @@
1
+ module Supersaas
2
+ class BaseApi
3
+ attr_accessor :client
4
+
5
+ INTEGER_REGEX = /\A[0-9]+\Z/
6
+ DATETIME_REGEX = /\A\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\Z/
7
+
8
+ def initialize(client)
9
+ @client = client
10
+ end
11
+
12
+ protected
13
+
14
+ def validate_id(value)
15
+ if value.is_a?(Integer)
16
+ value
17
+ elsif value.is_a?(String) && value =~ INTEGER_REGEX
18
+ value.to_i
19
+ else
20
+ raise Supersaas::Exception.new("Invalid id parameter: #{value}. Provide a integer value.")
21
+ end
22
+ end
23
+ def validate_number(value); validate_id(value); end
24
+
25
+ def validate_present(value)
26
+ if value.is_a?(String) ? value.size : value
27
+ value
28
+ else
29
+ raise Supersaas::Exception.new("Required parameter is missing.")
30
+ end
31
+ end
32
+
33
+ def validate_datetime(value)
34
+ begin
35
+ if value.is_a?(String) && value =~ DATETIME_REGEX
36
+ value
37
+ elsif value.is_a?(Time) || value.is_a?(DateTime)
38
+ value.strftime("%Y-%m-%d %H:%M:%S")
39
+ else
40
+ raise ArgumentError
41
+ end
42
+ rescue ArgumentError
43
+ raise Supersaas::Exception.new("Invalid datetime parameter: #{value}. Provide a Time object or formatted 'YYYY-DD-MM HH:MM:SS' string.")
44
+ end
45
+ end
46
+
47
+ def validate_options(value, options)
48
+ if options.include?(value)
49
+ value
50
+ else
51
+ raise Supersaas::Exception.new("Invalid option parameter: #{value}. Must be one of #{options.join(', ')}.")
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ module Supersaas
2
+ # REF: https://www.supersaas.com/info/dev/form_api
3
+ class Forms < BaseApi
4
+ def list(template_form_id, from_time=nil)
5
+ path = "/forms"
6
+ params = {
7
+ form_id: validate_id(template_form_id),
8
+ from: from_time ? validate_datetime(from_time) : nil
9
+ }
10
+ res = client.get(path, params)
11
+ res.map { |attributes| Supersaas::Form.new(attributes) }
12
+ end
13
+
14
+ def get(form_id)
15
+ path = "/forms"
16
+ params = {id: validate_id(form_id)}
17
+ res = client.get(path, params)
18
+ Supersaas::Form.new(res)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ module Supersaas
2
+ class Schedules < BaseApi
3
+ def list
4
+ path = "/schedules"
5
+ res = client.get(path)
6
+ res.map { |attributes| Supersaas::Schedule.new(attributes) }
7
+ end
8
+
9
+ def resources(schedule_id)
10
+ path = "/resources"
11
+ query = {schedule_id: validate_id(schedule_id)}
12
+ res = client.get(path, query)
13
+ res.map { |attributes| Supersaas::Resource.new(attributes) }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,82 @@
1
+ module Supersaas
2
+ # REF: https://www.supersaas.com/info/dev/user_api
3
+ class Users < BaseApi
4
+ def list(form=nil, limit=nil, offset=nil)
5
+ path = user_path(nil)
6
+ params = {
7
+ form: form ? true : nil,
8
+ limit: limit && validate_number(limit),
9
+ offset: offset && validate_number(offset)
10
+ }
11
+ res = client.get(path, params)
12
+ res.map { |attributes| Supersaas::User.new(attributes) }
13
+ end
14
+
15
+ def get(user_id)
16
+ path = user_path(user_id)
17
+ res = client.get(path)
18
+ Supersaas::User.new(res)
19
+ end
20
+
21
+ def create(attributes, user_id=nil, webhook=nil)
22
+ path = user_path(user_id)
23
+ query = {webhook: webhook}
24
+ params = {
25
+ user: {
26
+ name: validate_present(attributes[:name]),
27
+ email: attributes[:email],
28
+ password: attributes[:password],
29
+ full_name: attributes[:full_name],
30
+ address: attributes[:address],
31
+ mobile: attributes[:mobile],
32
+ phone: attributes[:phone],
33
+ country: attributes[:country],
34
+ field_1: attributes[:field_1],
35
+ field_2: attributes[:field_2],
36
+ super_field: attributes[:super_field],
37
+ credit: attributes[:credit] && validate_number(attributes[:credit]),
38
+ role: attributes[:role] && validate_options(attributes[:role], User::ROLES)
39
+ }
40
+ }
41
+ client.post(path, params, query)
42
+ end
43
+
44
+ def update(user_id, attributes, webhook=nil)
45
+ path = user_path(validate_id(user_id))
46
+ query = {webhook: webhook}
47
+ params = {
48
+ user: {
49
+ name: attributes[:name],
50
+ email: attributes[:email],
51
+ password: attributes[:password],
52
+ full_name: attributes[:full_name],
53
+ address: attributes[:address],
54
+ mobile: attributes[:mobile],
55
+ phone: attributes[:phone],
56
+ country: attributes[:country],
57
+ field_1: attributes[:field_1],
58
+ field_2: attributes[:field_2],
59
+ super_field: attributes[:super_field],
60
+ credit: attributes[:credit] && validate_number(attributes[:credit]),
61
+ role: attributes[:role] && validate_options(attributes[:role], User::ROLES)
62
+ }
63
+ }
64
+ client.put(path, params, query)
65
+ end
66
+
67
+ def delete(user_id)
68
+ path = user_path(validate_id(user_id))
69
+ client.delete(path)
70
+ end
71
+
72
+ private
73
+
74
+ def user_path(user_id)
75
+ if user_id.nil? || user_id == ''
76
+ "/users"
77
+ else
78
+ "/users/#{user_id}"
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,159 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'json'
4
+
5
+ module Supersaas
6
+ class Client
7
+ class << self
8
+ attr_accessor :configuration
9
+
10
+ def configure
11
+ self.configuration ||= Configuration.new
12
+ yield(configuration)
13
+ end
14
+
15
+ def instance
16
+ Thread.current['SUPER_SAAS_CLIENT'] ||= new(configuration || Configuration.new)
17
+ end
18
+
19
+ def user_agent
20
+ "SSS/#{VERSION} Ruby/#{RUBY_VERSION} API/#{API_VERSION}"
21
+ end
22
+ end
23
+
24
+ attr_accessor :account_name, :password, :host, :dry_run, :verbose
25
+ attr_reader :last_request
26
+
27
+ def initialize(configuration=nil)
28
+ configuration = configuration || Configuration.new
29
+ @account_name = configuration.account_name
30
+ @password = configuration.password
31
+ @host = configuration.host
32
+ @dry_run = configuration.dry_run
33
+ @verbose = configuration.verbose
34
+ end
35
+
36
+ def appointments; @appointments ||= Appointments.new(self); end
37
+ def forms; @forms ||= Forms.new(self); end
38
+ def schedules; @schedules ||= Schedules.new(self); end
39
+ def users; @users ||= Users.new(self); end
40
+
41
+ def get(path, query={})
42
+ request(:get, path, {}, query)
43
+ end
44
+
45
+ def post(path, params={}, query={})
46
+ request(:post, path, params, query)
47
+ end
48
+
49
+ def put(path, params={}, query={})
50
+ request(:put, path, params, query)
51
+ end
52
+
53
+ def delete(path, params={}, query={})
54
+ request(:delete, path, params, query)
55
+ end
56
+
57
+ private
58
+
59
+ def request(http_method, path, params={}, query={})
60
+ raise Supersaas::Exception.new("Account name not configured. Call `Supersaas::Client.configure`.") unless account_name && account_name.size
61
+ raise Supersaas::Exception.new("Account password not configured. Call `Supersaas::Client.configure`.") unless password && password.size
62
+
63
+ uri = URI.parse(host)
64
+ http = Net::HTTP.new(uri.host, uri.port)
65
+ http.use_ssl = uri.scheme == 'https'
66
+
67
+ params = delete_blank_values params
68
+ query = delete_blank_values query
69
+
70
+ path = "/api#{path}.json"
71
+ path += "?#{URI.encode_www_form(query)}" if query.keys.size > 0
72
+
73
+ case http_method
74
+ when :get
75
+ req = Net::HTTP::Get.new(path)
76
+ when :post
77
+ req = Net::HTTP::Post.new(path)
78
+ req.body = params.to_json
79
+ when :put
80
+ req = Net::HTTP::Put.new(path)
81
+ req.body = params.to_json
82
+ when :delete
83
+ req = Net::HTTP::Delete.new(path)
84
+ req.body = params.to_json
85
+ else
86
+ raise Supersaas::Exception.new("Invalid HTTP Method: #{http_method}. Only `:get`, `:post`, `:put`, `:delete` supported.")
87
+ end
88
+
89
+ req.basic_auth account_name, password
90
+
91
+ req['Accept'] = 'application/json'
92
+ req['Content-Type'] = 'application/json'
93
+ req['User-Agent'] = self.class.user_agent
94
+
95
+ if verbose
96
+ puts "### SuperSaaS Client Request:"
97
+ puts "#{http_method} #{path}"
98
+ puts params.to_json
99
+ puts "------------------------------"
100
+ end
101
+
102
+ @last_request = req
103
+ return {} if dry_run
104
+
105
+ begin
106
+ res = http.request(req)
107
+ rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError,
108
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
109
+ raise Supersaas::Exception.new("HTTP Request Error (#{uri}#{path}): #{e.message}")
110
+ end
111
+
112
+ if verbose
113
+ puts "Response:"
114
+ puts res.body
115
+ puts "=============================="
116
+ end
117
+
118
+ code = res.code.to_i
119
+ case code
120
+ when 200, 201
121
+ json_body(res)
122
+ when 422, 400
123
+ json_body(res)
124
+ else
125
+ {}
126
+ end
127
+ end
128
+
129
+ def json_body(res)
130
+ begin
131
+ res.body && res.body.size ? JSON.parse(res.body) : {}
132
+ rescue JSON::ParserError
133
+ {}
134
+ end
135
+ end
136
+
137
+ def delete_blank_values(hash)
138
+ return hash unless hash
139
+ hash.delete_if do |k, v|
140
+ v = v.delete_if { |k2, v2| v2.nil? } if (v.is_a?(Hash))
141
+ v.nil? || v == ''
142
+ end
143
+ end
144
+
145
+ class Configuration
146
+ DEFAULT_HOST = 'https://www.supersaas.com'
147
+
148
+ attr_accessor :account_name, :host, :password, :dry_run, :verbose
149
+
150
+ def initialize
151
+ @account_name = ENV['SSS_API_ACCOUNT_NAME']
152
+ @password = ENV['SSS_API_PASSWORD']
153
+ @host = DEFAULT_HOST
154
+ @dry_run = false
155
+ @verbose = false
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,8 @@
1
+ module Supersaas
2
+ class Exception < StandardError
3
+ @field = nil
4
+ @code = nil
5
+
6
+ attr_accessor :field, :code
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ module Supersaas
2
+ class Appointment < BaseModel
3
+ attr_accessor :address, :country, :created_by, :created_on, :deleted, :description, :email, :field_1, :field_2, :field_1_r, :field_2_r, :finish, :form_id, :full_name, :id, :mobile, :name, :phone, :price, :res_name, :resource_id, :schedule_id, :schedule_name, :service_id, :service_name, :slot_id, :start, :status, :super_field, :updated_by, :updated_on, :user_id, :waitlisted
4
+ attr_reader :form, :slot
5
+
6
+ def form=(value)
7
+ if value.is_a?(Hash)
8
+ @form = Supersaas::Form.new(value)
9
+ else
10
+ @form = value
11
+ end
12
+ end
13
+
14
+ def slot=(value)
15
+ if value.is_a?(Hash)
16
+ @slot = Supersaas::Slot.new(value)
17
+ else
18
+ @slot = value
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Supersaas
2
+ class BaseModel
3
+ attr_accessor :errors
4
+ attr_reader :attributes
5
+
6
+ def initialize(attributes)
7
+ @attributes = attributes
8
+ assign_attributes(attributes)
9
+ end
10
+
11
+ private
12
+
13
+ def assign_attributes(attributes)
14
+ attributes.each do |key, value|
15
+ public_send("#{key}=", value) if respond_to?("#{key}=")
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ module Supersaas
2
+ class Form < BaseModel
3
+ attr_accessor :content, :created_on, :deleted, :id, :reservation_process_id, :super_form_id, :uniq, :updated_name, :updated_on, :user_id
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Supersaas
2
+ class Resource < BaseModel
3
+ attr_accessor :id, :name
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Supersaas
2
+ class Schedule < BaseModel
3
+ attr_accessor :id, :name
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ module Supersaas
2
+ class Slot < BaseModel
3
+ attr_accessor :count, :description, :finish, :id, :location, :name, :start, :title
4
+ attr_reader :bookings
5
+
6
+ def bookings=(value)
7
+ if value.is_a?(Array)
8
+ @bookings = value.map {|attributes| Supersaas::Appointment.new(attributes) }
9
+ else
10
+ @bookings = value
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module Supersaas
2
+ class User < BaseModel
3
+ ROLES = [3, 4, -1]
4
+
5
+ attr_accessor :address, :country, :created_on, :credit, :email, :field_1, :field_2, :fk, :full_name, :id, :mobile, :name, :phone, :role, :super_field
6
+ attr_reader :form
7
+
8
+ def form=(value)
9
+ if value.is_a?(Hash)
10
+ @form = Supersaas::Form.new(value)
11
+ else
12
+ @form = value
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ module Supersaas
2
+ API_VERSION = '1'
3
+ VERSION = '0.9.0'
4
+ end
@@ -0,0 +1 @@
1
+ require_relative './supersaas'
data/lib/supersaas.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "supersaas-api-client/version"
2
+ require "supersaas-api-client/exception"
3
+ require "supersaas-api-client/models/base_model"
4
+ require "supersaas-api-client/models/appointment"
5
+ require "supersaas-api-client/models/form"
6
+ require "supersaas-api-client/models/resource"
7
+ require "supersaas-api-client/models/schedule"
8
+ require "supersaas-api-client/models/slot"
9
+ require "supersaas-api-client/models/user"
10
+ require "supersaas-api-client/api/base_api"
11
+ require "supersaas-api-client/api/appointments"
12
+ require "supersaas-api-client/api/forms"
13
+ require "supersaas-api-client/api/schedules"
14
+ require "supersaas-api-client/api/users"
15
+ require "supersaas-api-client/client"
@@ -0,0 +1,27 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+
3
+ require "supersaas-api-client/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "supersaas-api-client"
7
+ spec.version = Supersaas::VERSION
8
+ spec.authors = ["Travis Dunn"]
9
+ spec.email = ["travis@supersaas.com"]
10
+
11
+ spec.summary = %q{Online bookings/appointments/calendars using the SuperSaaS scheduling platform.}
12
+ spec.description = %q{Online appointment scheduler for any type of business. Flexible and affordable booking software that can be integrated into any site. Free basic version.}
13
+ spec.homepage = "https://www.supersaas.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test)/})
18
+ end
19
+ spec.test_files = Dir['test/**/*']
20
+ spec.bindir = "bin"
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.16"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ end