antaeus-sdk 0.2.3

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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +119 -0
  4. data/bin/antaeus-cli +35 -0
  5. data/lib/antaeus-sdk.rb +58 -0
  6. data/lib/antaeus-sdk/api_client.rb +105 -0
  7. data/lib/antaeus-sdk/api_info.rb +47 -0
  8. data/lib/antaeus-sdk/config.rb +54 -0
  9. data/lib/antaeus-sdk/exception.rb +5 -0
  10. data/lib/antaeus-sdk/exceptions/approval_change_failed.rb +6 -0
  11. data/lib/antaeus-sdk/exceptions/authentication_failure.rb +6 -0
  12. data/lib/antaeus-sdk/exceptions/checkin_change_failed.rb +6 -0
  13. data/lib/antaeus-sdk/exceptions/checkout_change_failed.rb +6 -0
  14. data/lib/antaeus-sdk/exceptions/immutable_instance.rb +6 -0
  15. data/lib/antaeus-sdk/exceptions/immutable_modification.rb +6 -0
  16. data/lib/antaeus-sdk/exceptions/invalid_api_client.rb +6 -0
  17. data/lib/antaeus-sdk/exceptions/invalid_config_data.rb +6 -0
  18. data/lib/antaeus-sdk/exceptions/invalid_entity.rb +6 -0
  19. data/lib/antaeus-sdk/exceptions/invalid_input.rb +6 -0
  20. data/lib/antaeus-sdk/exceptions/invalid_options.rb +6 -0
  21. data/lib/antaeus-sdk/exceptions/invalid_property.rb +6 -0
  22. data/lib/antaeus-sdk/exceptions/invalid_where_query.rb +6 -0
  23. data/lib/antaeus-sdk/exceptions/login_required.rb +6 -0
  24. data/lib/antaeus-sdk/exceptions/missing_api_client.rb +6 -0
  25. data/lib/antaeus-sdk/exceptions/missing_entity.rb +6 -0
  26. data/lib/antaeus-sdk/exceptions/missing_path.rb +6 -0
  27. data/lib/antaeus-sdk/exceptions/new_instance_with_id.rb +6 -0
  28. data/lib/antaeus-sdk/guest_api_client.rb +63 -0
  29. data/lib/antaeus-sdk/helpers/string.rb +24 -0
  30. data/lib/antaeus-sdk/resource.rb +363 -0
  31. data/lib/antaeus-sdk/resource_collection.rb +198 -0
  32. data/lib/antaeus-sdk/resources/appointment.rb +166 -0
  33. data/lib/antaeus-sdk/resources/group.rb +27 -0
  34. data/lib/antaeus-sdk/resources/guest.rb +41 -0
  35. data/lib/antaeus-sdk/resources/hook.rb +17 -0
  36. data/lib/antaeus-sdk/resources/location.rb +50 -0
  37. data/lib/antaeus-sdk/resources/remote_application.rb +13 -0
  38. data/lib/antaeus-sdk/resources/user.rb +28 -0
  39. data/lib/antaeus-sdk/user_api_client.rb +63 -0
  40. data/lib/antaeus-sdk/version.rb +7 -0
  41. metadata +209 -0
@@ -0,0 +1,198 @@
1
+ module Antaeus
2
+ # The ResourceCollection class
3
+ # Should not allow or use mixed types
4
+ class ResourceCollection
5
+ include Enumerable
6
+ include Comparable
7
+
8
+ # @return [Class] this is a collection of this {Resource} subclass
9
+ attr_reader :type
10
+
11
+ def initialize(list, options = {})
12
+ raise Exceptions::InvalidOptions unless options.is_a?(Hash)
13
+ raise Exceptions::MissingAPIClient unless options[:client]
14
+ raise Exceptions::InvalidAPIClient unless options[:client].is_a?(APIClient)
15
+ raise Exceptions::InvalidInput if list.empty? and options[:type].nil?
16
+ @client = options[:client]
17
+ @list = list
18
+ if options[:type]
19
+ @type = options[:type]
20
+ else
21
+ @type = list.first.class
22
+ end
23
+ end
24
+
25
+ def each(&block)
26
+ @list.each(&block)
27
+ end
28
+
29
+ # Does the collection contain anything?
30
+ # @return [Boolean]
31
+ def empty?
32
+ @list.empty?
33
+ end
34
+
35
+ def first(n = nil)
36
+ if n
37
+ self.class.new(@list.first(n), type: @type, client: @client)
38
+ else
39
+ @list.first
40
+ end
41
+ end
42
+
43
+ def last(n = nil)
44
+ if n
45
+ self.class.new(@list.last(n), type: @type, client: @client)
46
+ else
47
+ @list.last
48
+ end
49
+ end
50
+
51
+ # Merge two collections
52
+ # @return [ResourceCollection]
53
+ def merge(other)
54
+ if other.is_a?(self.class)
55
+ new_list = @list.dup
56
+ self + (other - self)
57
+ else
58
+ fail Exceptions::InvalidInput
59
+ end
60
+ end
61
+
62
+ # Makes #model compatible with the server-side
63
+ def model
64
+ type
65
+ end
66
+
67
+ # Hacked together #or() method in the same spirit as #where().
68
+ # This method can be chained for multiple / more specific queries.
69
+ #
70
+ # @param attribute [Symbol] the attribute to query
71
+ # @param value [Object] the value to compare against
72
+ # @param comparison_method [String,Symbol] the method to use for comparison
73
+ # - allowed options are "'==', '!=', '>', '>=', '<', '<=', and 'match'"
74
+ # @raise [Exceptions::InvalidWhereQuery] if not the right kind of comparison
75
+ # @return [ResourceCollection]
76
+ def or(attribute, value, options = {})
77
+ options[:comparison] ||= '=='
78
+ if empty?
79
+ @type.where(attribute, value, comparison: options[:comparison], client: @client)
80
+ else
81
+ merge first.class.where(
82
+ attribute, value,
83
+ comparison: options[:comparison],
84
+ client: @client
85
+ )
86
+ end
87
+ end
88
+
89
+ # Pass pagination through to the Array (which passes to will_paginate)
90
+ def paginate(*args)
91
+ @list.paginate(*args)
92
+ end
93
+
94
+ # Returns the number of Resource instances in the collection
95
+ # @return [Fixnum]
96
+ def size
97
+ @list.size
98
+ end
99
+
100
+ # Allow complex sorting like an Array
101
+ # @return [ResourceCollection] sorted collection
102
+ def sort(&block)
103
+ self.class.new(super(&block), type: @type, client: @client)
104
+ end
105
+
106
+ # Horribly inefficient way to allow querying Resources by their attributes.
107
+ # This method can be chained for multiple / more specific queries.
108
+ #
109
+ # @param attribute [Symbol] the attribute to query
110
+ # @param value [Object] the value to compare against
111
+ # @param comparison_method [String,Symbol] the method to use for comparison
112
+ # - allowed options are "'==', '!=', '>', '>=', '<', '<=', and 'match'"
113
+ # @raise [Exceptions::InvalidWhereQuery] if not the right kind of comparison
114
+ # @return [ResourceCollection]
115
+ def where(attribute, value, options = {})
116
+ valid_comparisons = [:'==', :'!=', :>, :'>=', :<, :'<=', :match]
117
+ options[:comparison] ||= '=='
118
+ unless valid_comparisons.include?(options[:comparison].to_sym)
119
+ fail Exceptions::InvalidWhereQuery
120
+ end
121
+ self.class.new(
122
+ @list.collect do |item|
123
+ if item.send(attribute).nil?
124
+ nil
125
+ else
126
+ item if item.send(attribute).send(options[:comparison].to_sym, value)
127
+ end
128
+ end.compact,
129
+ type: @type,
130
+ client: @client
131
+ )
132
+ end
133
+
134
+ alias_method :and, :where
135
+
136
+ # Return the collection item at the specified index
137
+ # @return [Resource,ResourceCollection] the item at the requested index
138
+ def [](index)
139
+ if index.is_a?(Range)
140
+ self.class.new(@list[index], type: @type, client: @client)
141
+ else
142
+ @list[index]
143
+ end
144
+ end
145
+
146
+ # Return a collection after subtracting from the original
147
+ # @return [ResourceCollection]
148
+ def -(other)
149
+ new_list = @list.dup
150
+ if other.respond_to?(:to_a)
151
+ other.to_a.each do |item|
152
+ new_list.delete_if { |res| res.id == item.id }
153
+ end
154
+ elsif other.is_a?(Resource)
155
+ new_list.delete_if { |res| res.id == other.id }
156
+ else
157
+ raise Exceptions::InvalidInput
158
+ end
159
+ self.class.new(new_list, type: @type, client: @client)
160
+ end
161
+
162
+ # Return a collection after adding to the original
163
+ # Warning: this may cause duplicates or mixed type joins! For safety,
164
+ # use #merge
165
+ # @return [ResourceCollection]
166
+ def +(other)
167
+ if other.is_a?(self.class)
168
+ self.class.new(@list + other.to_a, type: @type, client: @client)
169
+ elsif other.is_a?(@type)
170
+ self.class.new(@list + [other], type: @type, client: @client)
171
+ else
172
+ fail Exceptions::InvalidInput
173
+ end
174
+ end
175
+
176
+ def <<(other)
177
+ if other.class == @type
178
+ @list << other
179
+ else
180
+ fail Exceptions::InvalidInput
181
+ end
182
+ end
183
+
184
+ def <=>(other)
185
+ collect(&:id).sort <=> other.collect(&:id).sort
186
+ end
187
+
188
+ # Allow comparison of collection
189
+ # @return [Boolean] do the collections contain the same resource ids?
190
+ def ==(other)
191
+ if other.is_a? self.class
192
+ collect(&:id).sort == other.collect(&:id).sort
193
+ else
194
+ false
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,166 @@
1
+ module Antaeus
2
+ module Resources
3
+ class Appointment < Resource
4
+ property :arrival, type: :time
5
+ property :comment
6
+ property :contact
7
+ property :departure, type: :time
8
+ property :location_id
9
+ property :guest_id
10
+ property :created_at, read_only: true, type: :time
11
+ property :created_by, read_only: true
12
+ property :arrived?, read_only: true
13
+ property :approved?, read_only: true
14
+ property :departed?, read_only: true
15
+ property :checkin_time, read_only: true, type: :time
16
+ property :checkout_time, read_only: true, type: :time
17
+
18
+ path :all, '/appointments'
19
+
20
+ # A collection of all upcoming appointments
21
+ def self.upcoming(options = {})
22
+ validate_options(options)
23
+ ResourceCollection.new(
24
+ options[:client].get("#{path_for(:all)}/upcoming")['appointments'].collect do |record|
25
+ self.new(
26
+ entity: record,
27
+ lazy: true,
28
+ tainted: false,
29
+ client: options[:client]
30
+ )
31
+ end,
32
+ type: self,
33
+ client: options[:client]
34
+ )
35
+ end
36
+
37
+ # Generate a report of appointments based on some criteria
38
+ def self.report(options = {})
39
+ validate_options(options)
40
+ ResourceCollection.new(
41
+ options[:client].post("/reports/generate", q: options[:criteria])['appointments'].collect do |record|
42
+ self.new(
43
+ entity: record,
44
+ lazy: false,
45
+ tainted: false,
46
+ client: options[:client]
47
+ )
48
+ end,
49
+ type: self,
50
+ client: options[:client]
51
+ )
52
+ end
53
+
54
+ # Approve an Appointment
55
+ def approve
56
+ if @client.patch("#{path_for(:all)}/#{id}/approve", approve: true)
57
+ true
58
+ else
59
+ fail Exceptions::ApprovalChangeFailed
60
+ end
61
+ reload
62
+ return true
63
+ end
64
+
65
+ # Checkin a Guest
66
+ def checkin
67
+ if @client.patch("#{path_for(:all)}/#{id}/checkin", email: guest.email)
68
+ true
69
+ else
70
+ raise Exceptions::CheckinChangeFailed
71
+ end
72
+ reload
73
+ return true
74
+ end
75
+
76
+ # Checkout a Guest
77
+ def checkout
78
+ if @client.patch("#{path_for(:all)}/#{id}/checkout", email: guest.email)
79
+ true
80
+ else
81
+ raise Exceptions::CheckoutChangeFailed
82
+ end
83
+ reload
84
+ return true
85
+ end
86
+
87
+ # Hidden property used to lookup related resource
88
+ def guest
89
+ Guest.get(@entity['guest_id'], client: @client)
90
+ end
91
+
92
+ # Set the guest associated with this appointment
93
+ def guest=(guest_or_guest_id)
94
+ @entity['guest_id'] = if guest_or_guest_id.is_a?(Guest)
95
+ guest_or_guest_id.id
96
+ else
97
+ guest_or_guest_id
98
+ end
99
+ @tainted = true
100
+ end
101
+
102
+ # Hidden property used to lookup related resource
103
+ def location
104
+ Location.get(@entity['location_id'], client: @client)
105
+ end
106
+
107
+ # Set the location associated with this appointment
108
+ def location=(location_or_location_id)
109
+ @entity['location_id'] = if location_or_location_id.is_a?(Location)
110
+ location_or_location_id.id
111
+ else
112
+ location_or_location_id
113
+ end
114
+ @tainted = true
115
+ end
116
+
117
+ # Unapprove an Appointment
118
+ def unapprove
119
+ if @client.patch("#{path_for(:all)}/#{id}/approve", approve: false)
120
+ true
121
+ else
122
+ fail Exceptions::ApprovalChangeFailed
123
+ end
124
+ reload
125
+ return true
126
+ end
127
+
128
+ # Checkin a Guest
129
+ def undo_checkin
130
+ if @client.delete("#{path_for(:all)}/#{id}/checkin")
131
+ true
132
+ else
133
+ raise Exceptions::CheckinChangeFailed
134
+ end
135
+ reload
136
+ return true
137
+ end
138
+
139
+ # Checkout a Guest
140
+ def undo_checkout
141
+ if @client.delete("#{path_for(:all)}/#{id}/checkout")
142
+ true
143
+ else
144
+ raise Exceptions::CheckoutChangeFailed
145
+ end
146
+ reload
147
+ return true
148
+ end
149
+
150
+ # User related to an appointment
151
+ def user
152
+ User.get(contact, client: @client)
153
+ end
154
+
155
+ # Set the user related to an appointment
156
+ def user=(username)
157
+ contact = if username.is_a?(User)
158
+ username.id
159
+ else
160
+ username
161
+ end
162
+ @tainted = true
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,27 @@
1
+ module Antaeus
2
+ module Resources
3
+ class Group < Resource
4
+ delayed_property { Antaeus.config.group_name_attribute || :cn }
5
+
6
+ path :all, '/groups'
7
+ immutable true
8
+
9
+ def members
10
+ ResourceCollection.new(
11
+ @client.get("#{path_for(:all)}/#{id}/members")['users'].collect do |record|
12
+ User.new(
13
+ entity: record,
14
+ lazy: true,
15
+ tainted: false,
16
+ client: @client
17
+ )
18
+ end,
19
+ type: Antaeus::Resources::User,
20
+ client: @client
21
+ )
22
+ end
23
+
24
+ alias_method :users, :members
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ module Antaeus
2
+ module Resources
3
+ class Guest < Resource
4
+ property :email
5
+ property :full_name
6
+ property :phone
7
+ property :citizenship
8
+ property :need_nda
9
+ property :signed_nda
10
+ property :need_tcpa
11
+ property :signed_tcpa
12
+ property :pin
13
+ property :created_at, read_only: true, type: :time
14
+
15
+ path :all, '/guests'
16
+
17
+ def appointments
18
+ ResourceCollection.new(
19
+ @client.get("#{path_for(:all)}/#{id}/appointments")['appointments'].collect do |record|
20
+ Appointment.new(
21
+ entity: record,
22
+ lazy: true,
23
+ tainted: false,
24
+ client: @client
25
+ )
26
+ end,
27
+ type: Antaeus::Resources::Appointment,
28
+ client: @client
29
+ )
30
+ end
31
+
32
+ def available_appointments(location)
33
+ upcoming_appointments.where(location: location)
34
+ end
35
+
36
+ def upcoming_appointments
37
+ Appointment.upcoming(client: client).where(:guest_id, id)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ module Antaeus
2
+ module Resources
3
+ class Hook < Resource
4
+ property :name
5
+ property :plugins
6
+ property :configurations
7
+ property :created_at, read_only: true, type: :time
8
+
9
+ path :all, '/hooks'
10
+
11
+ def self.all_available(options = {})
12
+ validate_options(options)
13
+ options[:client].get("#{path_for(:all)}/available")['available_hooks']
14
+ end
15
+ end
16
+ end
17
+ end