alula-ruby 0.50.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +14 -0
- data/.env.example +8 -0
- data/.github/workflows/gem-push.yml +45 -0
- data/.gitignore +23 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Dockerfile +6 -0
- data/Gemfile +12 -0
- data/Guardfile +42 -0
- data/README.md +423 -0
- data/Rakefile +6 -0
- data/VERSION.md +84 -0
- data/alula-docker-compose.yml +80 -0
- data/alula.gemspec +38 -0
- data/bin/console +15 -0
- data/bin/docparse +36 -0
- data/bin/genresource +79 -0
- data/bin/setup +8 -0
- data/bin/testauth +24 -0
- data/bin/testprep +9 -0
- data/data/docs/Alula_API_Documentation_2021-04-06.html +16240 -0
- data/docker-compose.yml +11 -0
- data/lib/alula/alula_response.rb +20 -0
- data/lib/alula/api_operations/delete.rb +52 -0
- data/lib/alula/api_operations/list.rb +45 -0
- data/lib/alula/api_operations/request.rb +44 -0
- data/lib/alula/api_operations/save.rb +81 -0
- data/lib/alula/api_resource.rb +196 -0
- data/lib/alula/client.rb +142 -0
- data/lib/alula/errors.rb +169 -0
- data/lib/alula/filter_builder.rb +271 -0
- data/lib/alula/helpers/device_attribute_translations.rb +68 -0
- data/lib/alula/list_object.rb +64 -0
- data/lib/alula/meta.rb +16 -0
- data/lib/alula/monkey_patches.rb +24 -0
- data/lib/alula/oauth.rb +118 -0
- data/lib/alula/pagination.rb +25 -0
- data/lib/alula/procedures/dealer_device_stats_proc.rb +16 -0
- data/lib/alula/procedures/dealer_restore_proc.rb +25 -0
- data/lib/alula/procedures/dealer_suspend_proc.rb +25 -0
- data/lib/alula/procedures/device_assign_proc.rb +23 -0
- data/lib/alula/procedures/device_cellular_history_proc.rb +33 -0
- data/lib/alula/procedures/device_rateplan_get_proc.rb +21 -0
- data/lib/alula/procedures/device_register_proc.rb +31 -0
- data/lib/alula/procedures/device_signal_add_proc.rb +42 -0
- data/lib/alula/procedures/device_signal_delivered_proc.rb +31 -0
- data/lib/alula/procedures/device_signal_update_proc.rb +32 -0
- data/lib/alula/procedures/device_unassign_proc.rb +16 -0
- data/lib/alula/procedures/device_unregister_proc.rb +21 -0
- data/lib/alula/procedures/upload_touchpad_branding_proc.rb +24 -0
- data/lib/alula/procedures/user_plansvideo_price_get.rb +21 -0
- data/lib/alula/procedures/user_transfer_accept.rb +19 -0
- data/lib/alula/procedures/user_transfer_authorize.rb +18 -0
- data/lib/alula/procedures/user_transfer_cancel.rb +18 -0
- data/lib/alula/procedures/user_transfer_deny.rb +19 -0
- data/lib/alula/procedures/user_transfer_reject.rb +19 -0
- data/lib/alula/procedures/user_transfer_request.rb +19 -0
- data/lib/alula/query_interface.rb +142 -0
- data/lib/alula/rate_limit.rb +11 -0
- data/lib/alula/relationship_attributes.rb +107 -0
- data/lib/alula/resource_attributes.rb +206 -0
- data/lib/alula/resources/admin_user.rb +207 -0
- data/lib/alula/resources/billing_program.rb +41 -0
- data/lib/alula/resources/dealer.rb +218 -0
- data/lib/alula/resources/dealer_account_transfer.rb +172 -0
- data/lib/alula/resources/dealer_address.rb +89 -0
- data/lib/alula/resources/dealer_branding.rb +226 -0
- data/lib/alula/resources/dealer_program.rb +75 -0
- data/lib/alula/resources/dealer_suspension_log.rb +49 -0
- data/lib/alula/resources/device.rb +716 -0
- data/lib/alula/resources/device_cellular_status.rb +134 -0
- data/lib/alula/resources/device_charge.rb +70 -0
- data/lib/alula/resources/device_event_log.rb +167 -0
- data/lib/alula/resources/device_program.rb +54 -0
- data/lib/alula/resources/event_trigger.rb +75 -0
- data/lib/alula/resources/event_webhook.rb +47 -0
- data/lib/alula/resources/feature_bysubject.rb +74 -0
- data/lib/alula/resources/feature_plan.rb +57 -0
- data/lib/alula/resources/feature_planvideo.rb +54 -0
- data/lib/alula/resources/feature_price.rb +46 -0
- data/lib/alula/resources/receiver_connection.rb +95 -0
- data/lib/alula/resources/receiver_group.rb +74 -0
- data/lib/alula/resources/revision.rb +91 -0
- data/lib/alula/resources/self.rb +61 -0
- data/lib/alula/resources/station.rb +130 -0
- data/lib/alula/resources/token_exchange.rb +34 -0
- data/lib/alula/resources/user.rb +229 -0
- data/lib/alula/resources/user_address.rb +121 -0
- data/lib/alula/resources/user_phone.rb +116 -0
- data/lib/alula/resources/user_preferences.rb +57 -0
- data/lib/alula/resources/user_pushtoken.rb +75 -0
- data/lib/alula/resources/user_videoprofile.rb +38 -0
- data/lib/alula/rest_resource.rb +17 -0
- data/lib/alula/rpc_resource.rb +40 -0
- data/lib/alula/rpc_response.rb +14 -0
- data/lib/alula/singleton_rest_resource.rb +26 -0
- data/lib/alula/util.rb +107 -0
- data/lib/alula/version.rb +5 -0
- data/lib/alula.rb +135 -0
- data/lib/parser.rb +199 -0
- metadata +282 -0
data/docker-compose.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Alula
|
2
|
+
class AlulaResponse
|
3
|
+
attr_accessor :data, :http_body, :http_headers, :http_status, :raw, :rate_limit
|
4
|
+
|
5
|
+
def ok?
|
6
|
+
(200..299).cover?(self.http_status)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_httparty_response(response)
|
10
|
+
resp = AlulaResponse.new
|
11
|
+
resp.data = response.parsed_response
|
12
|
+
resp.http_body = response.body
|
13
|
+
resp.http_headers = response.headers
|
14
|
+
resp.rate_limit = Alula::RateLimit.new(response.headers)
|
15
|
+
resp.http_status = response.code
|
16
|
+
resp.raw = response
|
17
|
+
resp
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Alula
|
2
|
+
module ApiOperations
|
3
|
+
module Delete
|
4
|
+
def self.extended(base)
|
5
|
+
base.include(InstanceMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
def delete
|
10
|
+
# payload = {
|
11
|
+
# data: {
|
12
|
+
# id: id,
|
13
|
+
# attributes: as_patchable_json
|
14
|
+
# }
|
15
|
+
# }
|
16
|
+
|
17
|
+
response = Alula::Client.request(:delete, resource_url)
|
18
|
+
|
19
|
+
return true if response.ok?
|
20
|
+
|
21
|
+
handle_errors(response)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def handle_errors(response)
|
27
|
+
# Should never see a 300-range response code
|
28
|
+
if (300..399).cover?(response.http_status)
|
29
|
+
raise Alula::UnknownError, "The Alula-Ruby gem encountered a response code of #{response.code}, that should not happen"
|
30
|
+
|
31
|
+
# Server errors, malformed crap, etc
|
32
|
+
elsif (500..599).cover?(response.http_status)
|
33
|
+
return AlulaError.for_response(response)
|
34
|
+
|
35
|
+
# Validation errors usually
|
36
|
+
elsif (400..499).cover?(response.http_status)
|
37
|
+
model_errors = Util.model_errors_from_response(response)
|
38
|
+
|
39
|
+
if model_errors
|
40
|
+
annotate_errors(model_errors)
|
41
|
+
return false
|
42
|
+
else
|
43
|
+
return AlulaError.for_response(response)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
raise Alula::UnknownError, "Unknown HTTP response code, aborting. Code: #{response.http_status}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Alula
|
2
|
+
module ApiOperations
|
3
|
+
module List
|
4
|
+
def self.extended(base)
|
5
|
+
base.include(InstanceMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(method, *args, &block)
|
9
|
+
QueryInterface.new(self).send(method, *args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def list(filters = {}, opts = {})
|
13
|
+
response = Alula::Client.request(:get, resource_url, filters, opts)
|
14
|
+
if response.ok?
|
15
|
+
list = ListObject.construct_from_response(self, response, opts)
|
16
|
+
list = build_and_merge_list_relationships(list, response.data['included']) if response.data['included']
|
17
|
+
list
|
18
|
+
else
|
19
|
+
error_class = AlulaError.for_response(response)
|
20
|
+
raise error_class
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_and_merge_list_relationships(collection, relations)
|
25
|
+
relations.each do |relation|
|
26
|
+
model = Alula.class_from_resource_type(relation['type'])
|
27
|
+
model = model.new.construct_from(relation)
|
28
|
+
key = { type: model.get_type, id: model.id }.freeze
|
29
|
+
|
30
|
+
#
|
31
|
+
# TODO: Remove this line. This makes us ignore unknown relationships
|
32
|
+
unless get_relationship(model.get_type).nil?
|
33
|
+
collection.select{ |m| m.link_matchers.include?(key) }
|
34
|
+
.each { |m| m.add_model_to_relationship(model) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
collection
|
39
|
+
end
|
40
|
+
|
41
|
+
module InstanceMethods
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Alula
|
2
|
+
module ApiOperations
|
3
|
+
module Request
|
4
|
+
def self.extended(base)
|
5
|
+
base.include(InstanceMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Load a single model by ID
|
9
|
+
def retrieve(id, built_filters = {})
|
10
|
+
response = Alula::Client.request(:get, self.resource_url(id), built_filters, {})
|
11
|
+
if response.ok?
|
12
|
+
item = self.new.construct_from(response.data['data'])
|
13
|
+
item = build_and_merge_item_relationships(item, response.data['included']) if response.data['included']
|
14
|
+
item.rate_limit = response.rate_limit
|
15
|
+
item
|
16
|
+
else
|
17
|
+
error_class = AlulaError.for_response(response)
|
18
|
+
raise error_class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_and_merge_item_relationships(item, relations)
|
23
|
+
relations.each do |relation|
|
24
|
+
model = Alula.class_from_resource_type(relation['type'])
|
25
|
+
model = model.new.construct_from(relation)
|
26
|
+
|
27
|
+
#
|
28
|
+
# TODO: Remove this line. This makes us ignore unknown relationships
|
29
|
+
unless get_relationship(model.get_type).nil?
|
30
|
+
item.add_model_to_relationship(model)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
item
|
35
|
+
end
|
36
|
+
|
37
|
+
module InstanceMethods
|
38
|
+
def request(method, url, params, opts)
|
39
|
+
Alula::Client.request(method, url, params, opts)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Alula
|
2
|
+
module ApiOperations
|
3
|
+
module Save
|
4
|
+
def self.extended(base)
|
5
|
+
base.include(InstanceMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module InstanceMethods
|
9
|
+
def save
|
10
|
+
payload = {
|
11
|
+
data: {
|
12
|
+
id: id,
|
13
|
+
attributes: as_patchable_json
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
response = Alula::Client.request(:patch, resource_url, payload, {})
|
18
|
+
|
19
|
+
if response.ok?
|
20
|
+
construct_from(response.data['data'])
|
21
|
+
return true
|
22
|
+
end
|
23
|
+
|
24
|
+
handle_errors(response)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
data = {
|
29
|
+
attributes: as_patchable_json
|
30
|
+
}
|
31
|
+
#
|
32
|
+
# Most creations _won't_ have an ID, but the Alula API utlizes a shared GUID strategy for a few resources
|
33
|
+
# We need to conditionally include this ID in the data array even though the record 'doesnt exist'
|
34
|
+
#
|
35
|
+
# - Dealer Branding shares the same primary ID as the Dealer record
|
36
|
+
# - Dealer Contact info shares the same primary ID as the Dealer record
|
37
|
+
data[:id] = id if id
|
38
|
+
|
39
|
+
payload = {
|
40
|
+
data: data
|
41
|
+
}
|
42
|
+
|
43
|
+
response = Alula::Client.request(:post, self.class.resource_url, payload, {})
|
44
|
+
|
45
|
+
if response.ok?
|
46
|
+
construct_from(response.data['data'])
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
|
50
|
+
handle_errors(response)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def handle_errors(response)
|
56
|
+
# Should never see a 300-range response code
|
57
|
+
if (300..399).cover?(response.http_status)
|
58
|
+
raise Alula::UnknownError, "The Alula-Ruby gem encountered a response code of #{response.code}, that should not happen"
|
59
|
+
|
60
|
+
# Server errors, malformed crap, etc
|
61
|
+
elsif (500..599).cover?(response.http_status)
|
62
|
+
return AlulaError.for_response(response)
|
63
|
+
|
64
|
+
# Validation errors usually
|
65
|
+
elsif (400..499).cover?(response.http_status)
|
66
|
+
model_errors = Util.model_errors_from_response(response)
|
67
|
+
|
68
|
+
if model_errors
|
69
|
+
annotate_errors(model_errors)
|
70
|
+
return false
|
71
|
+
else
|
72
|
+
return AlulaError.for_response(response)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
raise Alula::UnknownError, "Unknown HTTP response code, aborting. Code: #{response.http_status}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module Alula
|
2
|
+
# Parent class for all API objects
|
3
|
+
class ApiResource
|
4
|
+
attr_accessor :id, :raw_data, :values, :dirty_attributes, :errors, :links, :link_matchers, :rate_limit
|
5
|
+
|
6
|
+
def self.class_name
|
7
|
+
name.split("::")[-1]
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(id = nil, attributes = {})
|
11
|
+
@raw_data = {}
|
12
|
+
@values = {}
|
13
|
+
@dirty_attributes = Set.new
|
14
|
+
@errors = ModelErrors.new(self.class)
|
15
|
+
|
16
|
+
construct_from(
|
17
|
+
'id' => id,
|
18
|
+
'attributes' => attributes
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Construct a new resource, ready to receive attributes, with
|
24
|
+
# empty values for all attrs.
|
25
|
+
# Useful for making New resources
|
26
|
+
# TODO: This will need testing and probably more work when we
|
27
|
+
# actually use it, at the moment we're only retrieving and
|
28
|
+
# modifying existing models.
|
29
|
+
def self.build
|
30
|
+
fields = self.get_fields.keys.map{ |k| Util.camelize(k) }
|
31
|
+
empty_shell = fields.each_with_object({}) { |f, obj| obj[f] = nil }
|
32
|
+
self.new(nil, empty_shell)
|
33
|
+
end
|
34
|
+
|
35
|
+
def construct_from(json_object)
|
36
|
+
@raw_data = json_object.dup
|
37
|
+
@values = json_object['attributes']
|
38
|
+
|
39
|
+
self.id = json_object['id'] unless [nil, ''].include?(json_object['id'])
|
40
|
+
|
41
|
+
@dirty_attributes = Set.new
|
42
|
+
@errors = ModelErrors.new(self.class)
|
43
|
+
|
44
|
+
@related_models = {}
|
45
|
+
cache_links(json_object['relationships'] || {})
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Take a hash of attributes and apply them to the model
|
52
|
+
def apply_attributes(attributes)
|
53
|
+
attributes.each do |key, value|
|
54
|
+
self.send("#{key}=", value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def reconstruct_from(json_object)
|
59
|
+
construct_from(json_object)
|
60
|
+
end
|
61
|
+
|
62
|
+
def dirty?(attribute_name = nil)
|
63
|
+
return @dirty_attributes.any? if attribute_name.nil?
|
64
|
+
|
65
|
+
@dirty_attributes.include? attribute_name.to_sym
|
66
|
+
end
|
67
|
+
|
68
|
+
def errors?
|
69
|
+
@errors.any?
|
70
|
+
end
|
71
|
+
|
72
|
+
def refresh
|
73
|
+
response = Alula::Client.request(:get, resource_url, {}, {})
|
74
|
+
if response.ok?
|
75
|
+
model = construct_from(response.data['data'])
|
76
|
+
model.rate_limit = response.rate_limit
|
77
|
+
model
|
78
|
+
else
|
79
|
+
error_class = AlulaError.for_response(response)
|
80
|
+
raise error_class
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Fetch known attributes out of the object, collected into a Hash in camelCase format
|
86
|
+
# Intended for eventually making its way back up to the API
|
87
|
+
def as_json
|
88
|
+
self.field_names.each_with_object({}) do |ruby_key, obj|
|
89
|
+
key = Util.camelize(ruby_key)
|
90
|
+
val = self.send(ruby_key)
|
91
|
+
|
92
|
+
if self.date_fields.include?(ruby_key) && ![nil, ''].include?(val)
|
93
|
+
if val.respond_to? :strftime
|
94
|
+
obj[key] = val.strftime('%Y-%m-%dT%H:%M:%S.%L%z')
|
95
|
+
else
|
96
|
+
obj[key] = val.to_s
|
97
|
+
end
|
98
|
+
else
|
99
|
+
obj[key] = val
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Reduce as_json to a set that can be updated, removing any fields
|
106
|
+
# that are not patchable by the user
|
107
|
+
def as_patchable_json
|
108
|
+
values = as_json.each_pair.each_with_object({}) do |(key, value), collector|
|
109
|
+
ruby_key = Util.underscore(key).to_sym
|
110
|
+
collector[key] = value if !read_only_attributes.include?(ruby_key) && dirty?(ruby_key)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Remove blank values if creating a new record
|
114
|
+
values = values.select{ |k, v| !v.nil? } unless persisted?
|
115
|
+
values
|
116
|
+
end
|
117
|
+
|
118
|
+
def annotate_errors(model_errors)
|
119
|
+
@errors = ModelErrors.new(self.class)
|
120
|
+
model_errors.each_pair do |field_name, error|
|
121
|
+
errors.add(field_name, error)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Links are hashes that identify any included models, they are used to
|
127
|
+
# distribute included models when also including relationships
|
128
|
+
# See list.rb#build_and_merge_relationships for details on usage
|
129
|
+
def cache_links(links)
|
130
|
+
@links = links
|
131
|
+
|
132
|
+
@link_matchers = links.each_pair.each_with_object([]) do |(type, link), collection|
|
133
|
+
data = link['data']
|
134
|
+
next if data.nil?
|
135
|
+
|
136
|
+
if data.is_a?(Array)
|
137
|
+
data.each do |nested_link|
|
138
|
+
collection << { type: nested_link['type'], id: nested_link['id'] }.freeze
|
139
|
+
end
|
140
|
+
else
|
141
|
+
collection << { type: data['type'], id: data['id'] }.freeze
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Return an instance of QueryEngine annotated with the correct model attributes
|
148
|
+
def filter_builder
|
149
|
+
Alula::FilterBuilder.new(self.class)
|
150
|
+
end
|
151
|
+
alias fb filter_builder
|
152
|
+
|
153
|
+
def model_name
|
154
|
+
self.class
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
class ModelErrors
|
160
|
+
include Enumerable
|
161
|
+
|
162
|
+
def initialize(model_class)
|
163
|
+
@model_class = model_class
|
164
|
+
@details = {}
|
165
|
+
end
|
166
|
+
|
167
|
+
def each
|
168
|
+
@details.each do |field, error|
|
169
|
+
yield({ field => error })
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def any?
|
174
|
+
@details.any?
|
175
|
+
end
|
176
|
+
|
177
|
+
def add(field_name, error_message)
|
178
|
+
@details[field_name] = error_message
|
179
|
+
end
|
180
|
+
|
181
|
+
def [](field_name)
|
182
|
+
@details[field_name.to_sym]
|
183
|
+
end
|
184
|
+
|
185
|
+
def full_messages
|
186
|
+
@details.map { |field, error| "#{field}: #{error}" }
|
187
|
+
end
|
188
|
+
|
189
|
+
def full_messages_for(attribute_name)
|
190
|
+
return nil unless @details[attribute_name.to_sym].present?
|
191
|
+
|
192
|
+
"#{attribute_name}: #{@details[attribute_name.to_sym]}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
data/lib/alula/client.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'alula/monkey_patches'
|
3
|
+
|
4
|
+
module Alula
|
5
|
+
class Client
|
6
|
+
include HTTParty
|
7
|
+
class << self
|
8
|
+
# Has pseudo accessors :api_key, :api_url, :user_agent, :debug
|
9
|
+
DEFAULT_CUSTOM_OPTIONS = {
|
10
|
+
omitRelationships: true
|
11
|
+
}
|
12
|
+
|
13
|
+
def request(http_method, resource_path, filters = {}, opts = {})
|
14
|
+
ensure_api_key_set
|
15
|
+
ensure_role_set if %i[patch post put].include? http_method
|
16
|
+
ensure_method_allowable(http_method)
|
17
|
+
|
18
|
+
raise Alula::NotConfiguredError, 'did you forget to set the Alula::Client.api_url config option?' unless api_url
|
19
|
+
|
20
|
+
request_opts = {
|
21
|
+
headers: {
|
22
|
+
'Authorization': "Bearer #{api_key}",
|
23
|
+
'User-Agent': "#{user_agent || 'No Agent Set'}/alula-ruby v#{Alula::VERSION}"
|
24
|
+
}
|
25
|
+
}.merge(opts)
|
26
|
+
|
27
|
+
request_opts[:headers]['Content-Type'] = 'application/json' unless opts[:multipart]
|
28
|
+
case http_method
|
29
|
+
when :patch,
|
30
|
+
:post
|
31
|
+
request_opts[:body] = opts[:multipart] ? filters : JSON.generate(filters)
|
32
|
+
when :get
|
33
|
+
if resource_path.match(%r{^/rest})
|
34
|
+
request_opts = request_opts.merge build_rest_options(filters)
|
35
|
+
elsif resource_path.match(%r{^/rpc})
|
36
|
+
request_opts = request_opts.merge build_rest_options(filters)
|
37
|
+
end
|
38
|
+
when :delete
|
39
|
+
# Nothing special
|
40
|
+
end
|
41
|
+
|
42
|
+
if debug == true
|
43
|
+
request_opts[:debug_output] = Alula.logger
|
44
|
+
logger Alula.logger, :info
|
45
|
+
end
|
46
|
+
|
47
|
+
# TODO: Handle network failures
|
48
|
+
response = make_request(http_method, api_url + resource_path, request_opts)
|
49
|
+
|
50
|
+
begin
|
51
|
+
resp = AlulaResponse.from_httparty_response(response)
|
52
|
+
rescue JSON::ParserError
|
53
|
+
# TODO: Should be able to send better info up with this
|
54
|
+
raise 'Unable to decode JSON'
|
55
|
+
end
|
56
|
+
|
57
|
+
resp
|
58
|
+
end
|
59
|
+
|
60
|
+
def make_request(http_method, full_url, request_opts)
|
61
|
+
send(http_method, full_url, request_opts)
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Using RequestStore so we're thread safe even with our non-thread-safe architecture :(
|
66
|
+
%i[api_key api_url user_agent debug].each do |prop|
|
67
|
+
define_method(prop) do
|
68
|
+
RequestStore.store["alula_#{prop}"]
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method("#{prop}=") do |value|
|
72
|
+
RequestStore.store["alula_#{prop}"] = value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Stash the user type as a role for us to infer from later
|
78
|
+
# You can set a symbolized role via role = :user, or provide the string userType
|
79
|
+
# We cast to a symbolized role for ease of use in consumers.
|
80
|
+
def role=(user_type)
|
81
|
+
unless user_type.is_a?(Symbol) || user_type.is_a?(String)
|
82
|
+
raise Alula::InvalidRoleError, 'Role must be symbol or string'
|
83
|
+
end
|
84
|
+
|
85
|
+
RequestStore.store['alula_role'] = Util.underscore(user_type).to_sym
|
86
|
+
end
|
87
|
+
|
88
|
+
def role
|
89
|
+
RequestStore.store['alula_role']
|
90
|
+
end
|
91
|
+
|
92
|
+
def ensure_api_key_set
|
93
|
+
if api_key.nil?
|
94
|
+
raise Alula::NotConfiguredError,
|
95
|
+
'Set your API access token before making requests with Alula::Client.api_key = {access_token}'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def ensure_method_allowable(http_method)
|
100
|
+
unless %i[get post put patch delete].include?(http_method)
|
101
|
+
raise "Unable to send a request with #{http_method} http method in #{self::OBJECT_NAME} class"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def ensure_role_set
|
106
|
+
if role.nil?
|
107
|
+
message = 'User role not configured! You must set '\
|
108
|
+
'Alula::Client.role '\
|
109
|
+
'before attempting to save any resources'
|
110
|
+
|
111
|
+
raise Alula::NotConfiguredError, message
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def build_rest_options(filters)
|
118
|
+
request_opts = {}
|
119
|
+
|
120
|
+
custom_options = filters.delete(:customOptions)
|
121
|
+
pagination = filters.delete(:page)
|
122
|
+
relationships = filters.delete(:include)
|
123
|
+
sort = filters.delete(:sort)
|
124
|
+
|
125
|
+
request_opts[:query] = {
|
126
|
+
filter: filters
|
127
|
+
}
|
128
|
+
|
129
|
+
request_opts[:query][:customOptions] = Util.deep_merge(DEFAULT_CUSTOM_OPTIONS, custom_options)
|
130
|
+
request_opts[:query][:page] = pagination if pagination
|
131
|
+
request_opts[:query][:include] = relationships if relationships
|
132
|
+
request_opts[:query][:sort] = sort if sort
|
133
|
+
|
134
|
+
request_opts
|
135
|
+
end
|
136
|
+
|
137
|
+
def build_rpc_options(filters)
|
138
|
+
filters
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|