lockstep_rails 0.3.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +216 -0
- data/Rakefile +31 -0
- data/app/assets/config/lockstep_rails_manifest.js +0 -0
- data/app/concepts/lockstep/active_records/association.rb +78 -0
- data/app/concepts/lockstep/api_record.rb +1118 -0
- data/app/concepts/lockstep/api_records/scopes.rb +20 -0
- data/app/concepts/lockstep/client.rb +162 -0
- data/app/concepts/lockstep/error.rb +50 -0
- data/app/concepts/lockstep/exceptions.rb +4 -0
- data/app/concepts/lockstep/query.rb +409 -0
- data/app/concepts/lockstep/query_methods.rb +75 -0
- data/app/concepts/lockstep/relation_array.rb +100 -0
- data/app/helpers/types.rb +3 -0
- data/app/models/lockstep/account.rb +15 -0
- data/app/models/lockstep/connection.rb +19 -0
- data/app/models/lockstep/contact.rb +9 -0
- data/app/models/lockstep/customer_summary.rb +6 -0
- data/app/models/lockstep/invoice.rb +7 -0
- data/app/models/lockstep/invoice_summary.rb +6 -0
- data/app/models/lockstep/invoices/address.rb +24 -0
- data/app/models/lockstep/note.rb +7 -0
- data/app/models/lockstep/payment.rb +7 -0
- data/app/models/lockstep/payment_summary.rb +6 -0
- data/app/models/lockstep/user.rb +10 -0
- data/app/platform_api/model_template.rb.erb +27 -0
- data/app/platform_api/schema/action_result.rb +15 -0
- data/app/platform_api/schema/activity.rb +141 -0
- data/app/platform_api/schema/activity_fetch_result.rb +26 -0
- data/app/platform_api/schema/activity_stream_item.rb +58 -0
- data/app/platform_api/schema/activity_x_ref.rb +37 -0
- data/app/platform_api/schema/aging.rb +24 -0
- data/app/platform_api/schema/api_key.rb +71 -0
- data/app/platform_api/schema/api_key_fetch_result.rb +26 -0
- data/app/platform_api/schema/app_enrollment.rb +88 -0
- data/app/platform_api/schema/app_enrollment_custom_field.rb +67 -0
- data/app/platform_api/schema/app_enrollment_custom_field_fetch_result.rb +26 -0
- data/app/platform_api/schema/app_enrollment_fetch_result.rb +26 -0
- data/app/platform_api/schema/application.rb +89 -0
- data/app/platform_api/schema/application_fetch_result.rb +26 -0
- data/app/platform_api/schema/ar_aging_header_info.rb +47 -0
- data/app/platform_api/schema/ar_header_info.rb +118 -0
- data/app/platform_api/schema/assembly.rb +68 -0
- data/app/platform_api/schema/at_risk_invoice_summary.rb +90 -0
- data/app/platform_api/schema/at_risk_invoice_summary_fetch_result.rb +26 -0
- data/app/platform_api/schema/attachment.rb +92 -0
- data/app/platform_api/schema/attachment_fetch_result.rb +26 -0
- data/app/platform_api/schema/attachment_header_info.rb +41 -0
- data/app/platform_api/schema/batch_sync.rb +18 -0
- data/app/platform_api/schema/bulk_currency_conversion.rb +19 -0
- data/app/platform_api/schema/cashflow_report.rb +35 -0
- data/app/platform_api/schema/code_definition.rb +58 -0
- data/app/platform_api/schema/code_definition_fetch_result.rb +26 -0
- data/app/platform_api/schema/company.rb +243 -0
- data/app/platform_api/schema/company_fetch_result.rb +26 -0
- data/app/platform_api/schema/company_sync.rb +142 -0
- data/app/platform_api/schema/connector_info.rb +27 -0
- data/app/platform_api/schema/constructor_info.rb +128 -0
- data/app/platform_api/schema/contact.rb +148 -0
- data/app/platform_api/schema/contact_fetch_result.rb +26 -0
- data/app/platform_api/schema/contact_sync.rb +109 -0
- data/app/platform_api/schema/country.rb +62 -0
- data/app/platform_api/schema/country_fetch_result.rb +26 -0
- data/app/platform_api/schema/credit_memo_applied.rb +104 -0
- data/app/platform_api/schema/credit_memo_applied_fetch_result.rb +26 -0
- data/app/platform_api/schema/credit_memo_applied_sync.rb +67 -0
- data/app/platform_api/schema/credit_memo_invoice.rb +77 -0
- data/app/platform_api/schema/currency.rb +31 -0
- data/app/platform_api/schema/currency_fetch_result.rb +26 -0
- data/app/platform_api/schema/currency_rate.rb +28 -0
- data/app/platform_api/schema/custom_attribute_data.rb +18 -0
- data/app/platform_api/schema/custom_attribute_named_argument.rb +24 -0
- data/app/platform_api/schema/custom_attribute_typed_argument.rb +16 -0
- data/app/platform_api/schema/custom_field_definition.rb +76 -0
- data/app/platform_api/schema/custom_field_definition_fetch_result.rb +26 -0
- data/app/platform_api/schema/custom_field_sync.rb +71 -0
- data/app/platform_api/schema/custom_field_value.rb +75 -0
- data/app/platform_api/schema/custom_field_value_fetch_result.rb +26 -0
- data/app/platform_api/schema/customer_details.rb +98 -0
- data/app/platform_api/schema/customer_details_payment.rb +60 -0
- data/app/platform_api/schema/customer_summary.rb +88 -0
- data/app/platform_api/schema/customer_summary_fetch_result.rb +26 -0
- data/app/platform_api/schema/daily_sales_outstanding_report.rb +25 -0
- data/app/platform_api/schema/developer_account_submit.rb +23 -0
- data/app/platform_api/schema/email.rb +160 -0
- data/app/platform_api/schema/email_fetch_result.rb +26 -0
- data/app/platform_api/schema/erp.rb +23 -0
- data/app/platform_api/schema/erp_fetch_result.rb +26 -0
- data/app/platform_api/schema/erp_info.rb +18 -0
- data/app/platform_api/schema/erp_info_data.rb +23 -0
- data/app/platform_api/schema/event_info.rb +59 -0
- data/app/platform_api/schema/exception.rb +41 -0
- data/app/platform_api/schema/field_info.rb +105 -0
- data/app/platform_api/schema/financial_account.rb +90 -0
- data/app/platform_api/schema/financial_account_balance_history.rb +90 -0
- data/app/platform_api/schema/financial_account_balance_history_fetch_result.rb +26 -0
- data/app/platform_api/schema/financial_account_fetch_result.rb +26 -0
- data/app/platform_api/schema/financial_year_setting.rb +74 -0
- data/app/platform_api/schema/financial_year_setting_fetch_result.rb +26 -0
- data/app/platform_api/schema/invite.rb +25 -0
- data/app/platform_api/schema/invite_data.rb +18 -0
- data/app/platform_api/schema/invite_submit.rb +15 -0
- data/app/platform_api/schema/invoice.rb +226 -0
- data/app/platform_api/schema/invoice_address.rb +97 -0
- data/app/platform_api/schema/invoice_fetch_result.rb +29 -0
- data/app/platform_api/schema/invoice_history.rb +188 -0
- data/app/platform_api/schema/invoice_history_fetch_result.rb +26 -0
- data/app/platform_api/schema/invoice_line.rb +142 -0
- data/app/platform_api/schema/invoice_line_sync.rb +208 -0
- data/app/platform_api/schema/invoice_payment_detail.rb +65 -0
- data/app/platform_api/schema/invoice_summary.rb +85 -0
- data/app/platform_api/schema/invoice_summary_fetch_result.rb +26 -0
- data/app/platform_api/schema/invoice_sync.rb +280 -0
- data/app/platform_api/schema/lead.rb +32 -0
- data/app/platform_api/schema/member_info.rb +36 -0
- data/app/platform_api/schema/method_base.rb +128 -0
- data/app/platform_api/schema/method_info.rb +137 -0
- data/app/platform_api/schema/module.rb +44 -0
- data/app/platform_api/schema/module_handle.rb +15 -0
- data/app/platform_api/schema/note.rb +72 -0
- data/app/platform_api/schema/note_fetch_result.rb +26 -0
- data/app/platform_api/schema/parameter_info.rb +64 -0
- data/app/platform_api/schema/payment.rb +147 -0
- data/app/platform_api/schema/payment_applied.rb +95 -0
- data/app/platform_api/schema/payment_applied_fetch_result.rb +26 -0
- data/app/platform_api/schema/payment_applied_sync.rb +74 -0
- data/app/platform_api/schema/payment_detail.rb +110 -0
- data/app/platform_api/schema/payment_detail_fetch_result.rb +26 -0
- data/app/platform_api/schema/payment_detail_header.rb +43 -0
- data/app/platform_api/schema/payment_fetch_result.rb +26 -0
- data/app/platform_api/schema/payment_summary.rb +79 -0
- data/app/platform_api/schema/payment_summary_fetch_result.rb +26 -0
- data/app/platform_api/schema/payment_sync.rb +112 -0
- data/app/platform_api/schema/problem_details.rb +31 -0
- data/app/platform_api/schema/property_info.rb +60 -0
- data/app/platform_api/schema/provisioning.rb +28 -0
- data/app/platform_api/schema/provisioning_finalize_request.rb +28 -0
- data/app/platform_api/schema/provisioning_response.rb +42 -0
- data/app/platform_api/schema/risk_rate.rb +57 -0
- data/app/platform_api/schema/runtime_field_handle.rb +13 -0
- data/app/platform_api/schema/runtime_method_handle.rb +13 -0
- data/app/platform_api/schema/runtime_type_handle.rb +13 -0
- data/app/platform_api/schema/state.rb +22 -0
- data/app/platform_api/schema/state_fetch_result.rb +26 -0
- data/app/platform_api/schema/status.rb +72 -0
- data/app/platform_api/schema/struct_layout_attribute.rb +16 -0
- data/app/platform_api/schema/sync_entity_result.rb +34 -0
- data/app/platform_api/schema/sync_request.rb +71 -0
- data/app/platform_api/schema/sync_request_fetch_result.rb +26 -0
- data/app/platform_api/schema/sync_submit.rb +15 -0
- data/app/platform_api/schema/test_argument_exception.rb +41 -0
- data/app/platform_api/schema/test_timeout_exception.rb +41 -0
- data/app/platform_api/schema/transfer_owner.rb +16 -0
- data/app/platform_api/schema/transfer_owner_submit.rb +15 -0
- data/app/platform_api/schema/type.rb +278 -0
- data/app/platform_api/schema/type_info.rb +287 -0
- data/app/platform_api/schema/uri.rb +15 -0
- data/app/platform_api/schema/user_account.rb +152 -0
- data/app/platform_api/schema/user_account_fetch_result.rb +26 -0
- data/app/platform_api/schema/user_role.rb +50 -0
- data/app/platform_api/schema/user_role_fetch_result.rb +26 -0
- data/app/platform_api/schema/webhook.rb +96 -0
- data/app/platform_api/schema/webhook_fetch_result.rb +26 -0
- data/app/platform_api/schema/webhook_history_table_storage.rb +64 -0
- data/app/platform_api/schema/webhook_history_table_storage_fetch_result.rb +26 -0
- data/app/platform_api/swagger.json +22056 -0
- data/config/routes.rb +2 -0
- data/lib/lockstep_rails/engine.rb +4 -0
- data/lib/lockstep_rails/version.rb +3 -0
- data/lib/lockstep_rails.rb +5 -0
- data/lib/tasks/lockstep_rails_tasks.rake +4 -0
- data/lib/tasks/update_api_schema.rake +159 -0
- metadata +230 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Lockstep::ApiRecords::Scopes
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class_methods do
|
5
|
+
def scopes
|
6
|
+
@scopes ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_scope(&block)
|
10
|
+
scopes[:default_scope] = block if block_given?
|
11
|
+
scopes[:default_scope]
|
12
|
+
end
|
13
|
+
|
14
|
+
def scope(name, block)
|
15
|
+
scopes[name] = block
|
16
|
+
define_singleton_method name, &block
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'openssl'
|
3
|
+
require 'uri'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
module Lockstep
|
7
|
+
class Client
|
8
|
+
|
9
|
+
|
10
|
+
def self.set_bearer_token(token)
|
11
|
+
RequestStore.store[:lockstep_bearer_token] = token
|
12
|
+
RequestStore.store[:lockstep_api_key] = nil
|
13
|
+
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.bearer_token
|
18
|
+
RequestStore.store[:lockstep_bearer_token]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.set_api_key(key)
|
22
|
+
RequestStore.store[:lockstep_api_key] = key
|
23
|
+
RequestStore.store[:lockstep_bearer_token] = nil
|
24
|
+
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.api_key
|
29
|
+
RequestStore.store[:lockstep_api_key]
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
##
|
34
|
+
# Construct a new Lockstep API client targeting the specified server.
|
35
|
+
#
|
36
|
+
# @param env [string] Either "sbx", "prd", or the URI of the server, ending in a slash (/)
|
37
|
+
def initialize(env)
|
38
|
+
@version = "2022.4.32.0"
|
39
|
+
@env = case env
|
40
|
+
when "sbx"
|
41
|
+
"https://api.sbx.lockstep.io/"
|
42
|
+
when "prd"
|
43
|
+
"https://api.lockstep.io/"
|
44
|
+
else
|
45
|
+
env
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Configure this API client to use API key authentication
|
51
|
+
#
|
52
|
+
# @param api_key [string] The [Lockstep Platform API key](https://developer.lockstep.io/docs/api-keys) to use for authentication
|
53
|
+
def with_api_key(api_key)
|
54
|
+
self.class.set_api_key(api_key)
|
55
|
+
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Configure this API client to use JWT Bearer Token authentication
|
61
|
+
#
|
62
|
+
# @param bearer_token [string] The [JWT Bearer Token](https://developer.lockstep.io/docs/jwt-bearer-tokens) to use for authentication
|
63
|
+
def with_bearer_token(token)
|
64
|
+
self.class.set_bearer_token(token)
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def bearer_token
|
70
|
+
self.class.bearer_token
|
71
|
+
end
|
72
|
+
|
73
|
+
def api_key
|
74
|
+
self.class.api_key
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Configure this API to use an application name
|
79
|
+
#
|
80
|
+
# @param app_name [string] The name of the application
|
81
|
+
def with_app_name(app_name)
|
82
|
+
@app_name = app_name
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Send a request to the API and return the results
|
87
|
+
#
|
88
|
+
# Sends a request to the
|
89
|
+
def request(method, path, body, params)
|
90
|
+
|
91
|
+
url = URI(@env + path)
|
92
|
+
if !params.nil?
|
93
|
+
url.query = URI.encode_www_form(params)
|
94
|
+
end
|
95
|
+
|
96
|
+
http = Net::HTTP.new(url.host, url.port)
|
97
|
+
http.use_ssl = true
|
98
|
+
|
99
|
+
request = case method
|
100
|
+
when :get
|
101
|
+
Net::HTTP::Get.new(url)
|
102
|
+
when :post
|
103
|
+
Net::HTTP::Post.new(url)
|
104
|
+
when :patch
|
105
|
+
Net::HTTP::Patch.new(url)
|
106
|
+
when :put
|
107
|
+
Net::HTTP::Put.new(url)
|
108
|
+
when :delete
|
109
|
+
Net::HTTP::Delete.new(url)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Set headers and body for request
|
113
|
+
request["Accept"] = 'application/json'
|
114
|
+
request["Content-Type"] = 'application/*+json'
|
115
|
+
request["SdkType"] = 'Ruby'
|
116
|
+
request["SdkVersion"] = '2022.4.32.0'
|
117
|
+
request["MachineName"] = Socket.gethostname
|
118
|
+
body = body.to_json unless body.is_a?(String)
|
119
|
+
request.body = body
|
120
|
+
|
121
|
+
# If there is an application name
|
122
|
+
if @app_name != nil
|
123
|
+
request["ApplicationName"] = @app_name
|
124
|
+
end
|
125
|
+
|
126
|
+
# Which authentication are we using?
|
127
|
+
if api_key != nil
|
128
|
+
request["Api-Key"] = api_key
|
129
|
+
end
|
130
|
+
if bearer_token != nil
|
131
|
+
request["Authorization"] = 'Bearer ' + bearer_token
|
132
|
+
end
|
133
|
+
|
134
|
+
# Send the request
|
135
|
+
http.request(request)
|
136
|
+
end
|
137
|
+
|
138
|
+
def get(path, params: {})
|
139
|
+
request(:get, path, nil, params)
|
140
|
+
end
|
141
|
+
|
142
|
+
def post(path, body: {}, params: {})
|
143
|
+
request(:post, path, body, params)
|
144
|
+
end
|
145
|
+
|
146
|
+
def patch(path, body: {}, params: {})
|
147
|
+
request(:patch, path, body, params)
|
148
|
+
end
|
149
|
+
|
150
|
+
def put(path, body: {}, params: {})
|
151
|
+
request(:put, path, body, params)
|
152
|
+
end
|
153
|
+
|
154
|
+
def delete(path)
|
155
|
+
request(:delete, path, {}, {})
|
156
|
+
end
|
157
|
+
|
158
|
+
def with_logger(&block)
|
159
|
+
block.call
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Lockstep::Error
|
2
|
+
# ParseError actually represents both HTTP & parse.com error codes. If the
|
3
|
+
# HTTP response is 400, one can inspect the first element of the error
|
4
|
+
# converted to_array for the HTTP error code and the 2nd element for the
|
5
|
+
# parse error response.
|
6
|
+
attr_accessor :msg, :code, :error
|
7
|
+
|
8
|
+
# @param [String] an error code, e.g. "400"
|
9
|
+
# @param [Object] an optional error mesg/object.
|
10
|
+
def initialize(code, msg="")
|
11
|
+
@msg = msg
|
12
|
+
@code = code
|
13
|
+
case code.to_s
|
14
|
+
when "111"
|
15
|
+
@error = "Invalid type."
|
16
|
+
when "135"
|
17
|
+
@error = "Unknown device type."
|
18
|
+
when "202"
|
19
|
+
@error = "Username already taken."
|
20
|
+
when "203"
|
21
|
+
@error = "Email already taken."
|
22
|
+
when "400"
|
23
|
+
@error = "Bad Request: The request cannot be fulfilled due to bad syntax."
|
24
|
+
when "401"
|
25
|
+
@error = "Unauthorized: Check your App ID & Master Key."
|
26
|
+
when "403"
|
27
|
+
@error = "Forbidden: You do not have permission to access or modify this."
|
28
|
+
when "408"
|
29
|
+
@error = "Request Timeout: The request was not completed within the time the server was prepared to wait."
|
30
|
+
when "415"
|
31
|
+
@error = "Unsupported Media Type"
|
32
|
+
when "500"
|
33
|
+
@error = "Internal Server Error"
|
34
|
+
when "502"
|
35
|
+
@error = "Bad Gateway"
|
36
|
+
when "503"
|
37
|
+
@error = "Service Unavailable"
|
38
|
+
when "508"
|
39
|
+
@error = "Loop Detected"
|
40
|
+
else
|
41
|
+
@error = "Unknown Error"
|
42
|
+
raise "Parse error #{code}: #{@error} #{@msg}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_array
|
47
|
+
[ @code, @msg ]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,409 @@
|
|
1
|
+
class Lockstep::Query
|
2
|
+
def initialize(klass)
|
3
|
+
@klass = klass
|
4
|
+
end
|
5
|
+
|
6
|
+
def model
|
7
|
+
@klass
|
8
|
+
end
|
9
|
+
|
10
|
+
# Clone is used as return object as it messes with the OR query in situations like these
|
11
|
+
# unscoped = Lockstep::Connection.unscoped
|
12
|
+
# unscoped.where(a: 1).or(unscoped.where(b: 1))
|
13
|
+
# both the where conditions in the above scenario is modifying the same object causing stack-overflow
|
14
|
+
def clone
|
15
|
+
c = Lockstep::Query.new(@klass)
|
16
|
+
c.criteria.deep_merge!(self.criteria.deep_dup)
|
17
|
+
c
|
18
|
+
end
|
19
|
+
|
20
|
+
def with_clone(&block)
|
21
|
+
c = clone
|
22
|
+
c.instance_exec(&block)
|
23
|
+
c
|
24
|
+
end
|
25
|
+
|
26
|
+
def criteria
|
27
|
+
@criteria ||= { :conditions => [] }
|
28
|
+
end
|
29
|
+
|
30
|
+
def or(query)
|
31
|
+
with_clone do
|
32
|
+
criteria[:conditions] << { type: "or", query: query }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def none
|
37
|
+
with_clone do
|
38
|
+
criteria[:none] = true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def where(args)
|
43
|
+
if args.is_a?(Hash)
|
44
|
+
args.each do |k, v|
|
45
|
+
return none if v.is_a?(Array) and v.blank?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
with_clone do
|
50
|
+
criteria[:conditions] << convert_arg(args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert_arg(arg)
|
55
|
+
return arg.to_pointer if arg.is_a?(Lockstep::ApiRecord)
|
56
|
+
return Lockstep::ApiRecord.to_date_object(arg) if arg.is_a?(Time) || arg.is_a?(Date)
|
57
|
+
if arg.is_a?(Hash)
|
58
|
+
arg.keys.each do |key|
|
59
|
+
@klass.valid_attribute?(key, raise_exception: true)
|
60
|
+
end
|
61
|
+
return arg.update(arg) { |key, inner_arg| convert_arg(inner_arg) }
|
62
|
+
end
|
63
|
+
|
64
|
+
arg
|
65
|
+
end
|
66
|
+
|
67
|
+
def limit(limit)
|
68
|
+
with_clone do
|
69
|
+
# If > 1000, set chunking, because large queries over 1000 need it with Parse
|
70
|
+
# criteria[:chunk] = 1000 if limit > 1000
|
71
|
+
criteria[:limit] = limit
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def page(page_number)
|
76
|
+
with_clone do
|
77
|
+
criteria[:page_number] = page_number
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def include_object(*objects)
|
82
|
+
with_clone do
|
83
|
+
criteria[:include] ||= []
|
84
|
+
criteria[:include] += objects
|
85
|
+
criteria[:include].uniq!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# attr: {customer_name: :desc}
|
90
|
+
def order(*attr)
|
91
|
+
with_clone do
|
92
|
+
criteria[:order] ||= []
|
93
|
+
attr.each do |item|
|
94
|
+
if item.is_a?(Hash)
|
95
|
+
item.each do |k, v|
|
96
|
+
criteria[:order] << "#{k.to_s.camelize.upcase} #{v}"
|
97
|
+
end
|
98
|
+
elsif item.is_a?(String)
|
99
|
+
criteria[:order] << item
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# attr: {customer_name: :desc}
|
106
|
+
def reorder(attr)
|
107
|
+
with_clone do
|
108
|
+
criteria[:order] = []
|
109
|
+
order(attr)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Divides the query into multiple chunks if you're running into RestClient::BadRequest errors.
|
114
|
+
def chunk(count = 100)
|
115
|
+
with_clone do
|
116
|
+
criteria[:chunk] = count
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# def related_to(obj, key)
|
121
|
+
# query = { "$relatedTo" => { "object" => obj.to_pointer, "key" => key } }
|
122
|
+
# criteria[:conditions].merge!(query)
|
123
|
+
# clone
|
124
|
+
# end
|
125
|
+
|
126
|
+
def build_filter
|
127
|
+
filter = ""
|
128
|
+
criteria[:conditions].each do |condition|
|
129
|
+
if condition.is_a?(Hash)
|
130
|
+
if condition[:type] == "or"
|
131
|
+
filter = build_filter_condition(filter, "OR", condition[:query].build_filter)
|
132
|
+
else
|
133
|
+
condition.each do |key, value|
|
134
|
+
filter = build_filter_condition(filter, "AND", build_filter_query(key, value))
|
135
|
+
end
|
136
|
+
end
|
137
|
+
elsif condition.is_a?(String)
|
138
|
+
filter = build_filter_condition(filter, "AND", condition)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
filter
|
142
|
+
end
|
143
|
+
|
144
|
+
def build_filter_condition(filter, merge_predicate, condition)
|
145
|
+
if filter.present?
|
146
|
+
filter = "(#{filter}) #{merge_predicate} (#{condition})"
|
147
|
+
else
|
148
|
+
filter += condition
|
149
|
+
end
|
150
|
+
filter
|
151
|
+
end
|
152
|
+
|
153
|
+
PREDICATES = {
|
154
|
+
_not_eq: "NE",
|
155
|
+
_eq: "EQ",
|
156
|
+
_gteq: "GE",
|
157
|
+
_gt: "GT",
|
158
|
+
_lteq: "LE",
|
159
|
+
_lt: "LT",
|
160
|
+
_in: "IN",
|
161
|
+
_contains: "CONTAINS",
|
162
|
+
_starts_with: "STARTSWITH",
|
163
|
+
_ends_with: "ENDSWITH",
|
164
|
+
_is: "IS",
|
165
|
+
}.with_indifferent_access
|
166
|
+
|
167
|
+
def build_filter_query(key, value)
|
168
|
+
key = key.to_s unless key.is_a?(String)
|
169
|
+
predicate = "eq" # default
|
170
|
+
PREDICATES.each do |k, p|
|
171
|
+
if key.ends_with?(k)
|
172
|
+
key = key.gsub(k, "")
|
173
|
+
predicate = p
|
174
|
+
break
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Build value
|
179
|
+
if value.is_a?(Array)
|
180
|
+
value = "(#{value.map { |v| v.is_a?(String) ? "'#{v}'" : v }.join(",")})"
|
181
|
+
predicate = "in" if predicate == "eq"
|
182
|
+
elsif value.is_a?(String) and !["NULL", "NOT NULL"].include?(value)
|
183
|
+
value = "'#{value}'"
|
184
|
+
elsif value.nil?
|
185
|
+
predicate = "IS" if predicate == "eq"
|
186
|
+
value = "NULL"
|
187
|
+
end
|
188
|
+
|
189
|
+
"#{key.camelize(:lower)} #{predicate} #{value}"
|
190
|
+
end
|
191
|
+
|
192
|
+
def build_params
|
193
|
+
params = {}
|
194
|
+
params.merge!({ :filter => build_filter }) if criteria[:conditions]
|
195
|
+
# Lockstep Platform API does not support a page size of 1
|
196
|
+
params.merge!({ :pageSize => (criteria[:limit] == 1 ? 2 : criteria[:limit]) }) if criteria[:limit]
|
197
|
+
params.merge!({ :pageNumber => criteria[:page_number] }) if criteria[:page_number]
|
198
|
+
params.merge!({ :include => criteria[:include].join(",") }) if criteria[:include]
|
199
|
+
params.merge!({ :order => criteria[:order].join(",") }) if criteria[:order]
|
200
|
+
params.merge!({ :pageSize => 2 }) if criteria[:count]
|
201
|
+
params.reject! { |k, v| v.blank? }
|
202
|
+
params
|
203
|
+
end
|
204
|
+
|
205
|
+
def execute
|
206
|
+
return [] if criteria[:none]
|
207
|
+
# This code automatically adds all related objects
|
208
|
+
#
|
209
|
+
# if @klass.has_many_relations
|
210
|
+
# relations = @klass.has_many_relations.select {|k, val| val[:included]}.keys.map { |relation| relation.to_s }
|
211
|
+
# include_object(*relations)
|
212
|
+
# end
|
213
|
+
|
214
|
+
params = build_params
|
215
|
+
|
216
|
+
return chunk_results(params) if criteria[:chunk]
|
217
|
+
|
218
|
+
get_results(params)
|
219
|
+
end
|
220
|
+
|
221
|
+
def get_results(params = {})
|
222
|
+
resp = @klass.resource.get(@klass.query_path, :params => params)
|
223
|
+
|
224
|
+
return [] if %w(404).include?(resp.code.to_s)
|
225
|
+
# TODO handle non 200 response code. Throwing an exception for now
|
226
|
+
raise StandardError.new("#{resp.code} error while fetching: #{resp.body}") unless %w(201 200).include?(resp.code.to_s)
|
227
|
+
|
228
|
+
if criteria[:count]
|
229
|
+
results = JSON.parse(resp.body)["totalCount"]
|
230
|
+
return results.to_i
|
231
|
+
else
|
232
|
+
results = JSON.parse(resp.body)["records"]
|
233
|
+
results = results[0..(criteria[:limit] - 1)] if criteria[:limit]
|
234
|
+
get_relation_objects results.map { |r|
|
235
|
+
# Convert camelcase to snake-case
|
236
|
+
r = r.transform_keys { |key| key.underscore }
|
237
|
+
@klass.model_name.to_s.constantize.new(r, false)
|
238
|
+
}
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def get_relation_objects(objects)
|
243
|
+
return objects if criteria[:include].blank?
|
244
|
+
|
245
|
+
included_objects = criteria[:include].map { |item| item.to_s.downcase }
|
246
|
+
|
247
|
+
if @klass.has_many_relations
|
248
|
+
objects.each do |item|
|
249
|
+
@klass.has_many_relations.each do |relation, relation_config|
|
250
|
+
next unless included_objects.include?(relation.to_s.downcase)
|
251
|
+
|
252
|
+
if !item.attributes.has_key?(relation.to_s)
|
253
|
+
item.attributes[relation.to_s] = Lockstep::RelationArray.new(@klass, [], relation, relation_config[:class_name])
|
254
|
+
elsif !item.attributes[relation].is_a?(Lockstep::RelationArray)
|
255
|
+
item.attributes[relation.to_s] = Lockstep::RelationArray.new(@klass, item.attributes[relation], relation, relation_config[:class_name])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
objects.each do |item|
|
262
|
+
item.attributes.each do |key, value|
|
263
|
+
if value.is_a?(Array)
|
264
|
+
relation = @klass.has_many_relations[key]
|
265
|
+
next if relation.blank? or !included_objects.include?(key.to_s.downcase)
|
266
|
+
|
267
|
+
value.each do |relation_hash|
|
268
|
+
relation_obj = turn_relation_hash_into_object(relation, relation_hash)
|
269
|
+
value[value.index(relation_hash)] = relation_obj
|
270
|
+
end
|
271
|
+
elsif value.is_a?(Hash)
|
272
|
+
relation = @klass.belongs_to_relations[key]
|
273
|
+
next if relation.blank?
|
274
|
+
|
275
|
+
relation_hash = value
|
276
|
+
relation_obj = turn_relation_hash_into_object(relation, relation_hash)
|
277
|
+
item.attributes[key] = relation_obj
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
objects
|
282
|
+
end
|
283
|
+
|
284
|
+
def chunk_results(params = {})
|
285
|
+
page = criteria[:page] || 0
|
286
|
+
limit = criteria[:limit] || 0
|
287
|
+
# completed = false
|
288
|
+
records = []
|
289
|
+
loop do
|
290
|
+
params[:pageNumber] = page
|
291
|
+
params[:pageSize] = limit > 0 ? [limit - records.size, criteria[:chunk]].min : criteria[:chunk]
|
292
|
+
break if params[:pageSize] == 0
|
293
|
+
|
294
|
+
results = get_results(params)
|
295
|
+
break unless results.present?
|
296
|
+
|
297
|
+
records += results
|
298
|
+
break if limit > 0 and records.size >= limit
|
299
|
+
|
300
|
+
page += 1
|
301
|
+
end
|
302
|
+
|
303
|
+
records
|
304
|
+
end
|
305
|
+
|
306
|
+
def first
|
307
|
+
limit(1)
|
308
|
+
execute.first
|
309
|
+
end
|
310
|
+
|
311
|
+
def all
|
312
|
+
execute
|
313
|
+
end
|
314
|
+
|
315
|
+
def count
|
316
|
+
criteria[:count] = true
|
317
|
+
execute
|
318
|
+
end
|
319
|
+
|
320
|
+
# Find a Lockstep::ApiRecord object by ID
|
321
|
+
#
|
322
|
+
# @param [String] id the ID of the Parse object you want to find.
|
323
|
+
# @return [Lockstep::ApiRecord] an object that subclasses Lockstep::ApiRecord.
|
324
|
+
def find(id)
|
325
|
+
raise Lockstep::Exceptions::RecordNotFound, "Couldn't find #{name} without an ID" if id.blank?
|
326
|
+
record = where(@klass.id_ref => id).first
|
327
|
+
raise Lockstep::Exceptions::RecordNotFound, "Couldn't find #{name} with id: #{id}" if record.blank?
|
328
|
+
record
|
329
|
+
end
|
330
|
+
|
331
|
+
def method_missing(meth, *args, &block)
|
332
|
+
method_name = method_name.to_s
|
333
|
+
if method_name.start_with?("find_by_")
|
334
|
+
attrib = method_name.gsub(/^find_by_/, "")
|
335
|
+
finder_name = "find_all_by_#{attrib}"
|
336
|
+
|
337
|
+
define_singleton_method(finder_name) do |target_value|
|
338
|
+
where({ attrib.to_sym => target_value }).first
|
339
|
+
end
|
340
|
+
|
341
|
+
send(finder_name, args[0])
|
342
|
+
elsif method_name.start_with?("find_all_by_")
|
343
|
+
attrib = method_name.gsub(/^find_all_by_/, "")
|
344
|
+
finder_name = "find_all_by_#{attrib}"
|
345
|
+
|
346
|
+
define_singleton_method(finder_name) do |target_value|
|
347
|
+
where({ attrib.to_sym => target_value }).all
|
348
|
+
end
|
349
|
+
|
350
|
+
send(finder_name, args[0])
|
351
|
+
end
|
352
|
+
|
353
|
+
if @klass.scopes[meth].present?
|
354
|
+
instance_exec *args, &@klass.scopes[meth]
|
355
|
+
elsif Array.method_defined?(meth)
|
356
|
+
all.send(meth, *args, &block)
|
357
|
+
else
|
358
|
+
super
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def respond_to?(meth)
|
363
|
+
if Array.method_defined?(meth)
|
364
|
+
true
|
365
|
+
else
|
366
|
+
super
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def unscoped
|
371
|
+
Lockstep::Query.new(@klass)
|
372
|
+
end
|
373
|
+
|
374
|
+
private
|
375
|
+
|
376
|
+
def turn_relation_hash_into_object(relation, hash)
|
377
|
+
return nil if hash == nil
|
378
|
+
relation_klass = relation[:class_name].to_s.constantize
|
379
|
+
relation_object = relation_klass.new
|
380
|
+
hash.each do |key, value|
|
381
|
+
class_name_in_a_hash = false
|
382
|
+
if value.is_a?(Array)
|
383
|
+
value.each do |item|
|
384
|
+
if item and item.is_a?(Hash)
|
385
|
+
class_name_in_a_hash = true if relation_klass.has_many_relations[key].present?
|
386
|
+
break
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
if value.is_a?(Array) and class_name_in_a_hash
|
392
|
+
value.each do |object_in_array|
|
393
|
+
fresh_object = turn_relation_hash_into_object(relation_klass.has_many_relations[key][:class_name], object_in_array)
|
394
|
+
value[value.index(object_in_array)] = fresh_object
|
395
|
+
end
|
396
|
+
# Convert key from camelcase to snake-case
|
397
|
+
relation_object.attributes[key.underscore] = value
|
398
|
+
elsif value.is_a?(Hash) and relation_klass.belongs_to_relations[key].present?
|
399
|
+
# Convert key from camelcase to snake-case
|
400
|
+
relation_object.attributes[key.underscore] = turn_relation_hash_into_object(relation_klass.belongs_to_relations[key][:class_name], value)
|
401
|
+
else
|
402
|
+
# Convert key from camelcase to snake-case
|
403
|
+
relation_object.attributes[key.underscore] = value if key.to_s != "__type" and key.to_s != "className"
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
relation_object
|
408
|
+
end
|
409
|
+
end
|