parse-stack 1.0.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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +77 -0
- data/LICENSE +20 -0
- data/README.md +1281 -0
- data/Rakefile +12 -0
- data/bin/console +20 -0
- data/bin/server +10 -0
- data/bin/setup +7 -0
- data/lib/parse/api/all.rb +13 -0
- data/lib/parse/api/analytics.rb +16 -0
- data/lib/parse/api/apps.rb +37 -0
- data/lib/parse/api/batch.rb +148 -0
- data/lib/parse/api/cloud_functions.rb +18 -0
- data/lib/parse/api/config.rb +22 -0
- data/lib/parse/api/files.rb +21 -0
- data/lib/parse/api/hooks.rb +68 -0
- data/lib/parse/api/objects.rb +77 -0
- data/lib/parse/api/push.rb +16 -0
- data/lib/parse/api/schemas.rb +25 -0
- data/lib/parse/api/sessions.rb +11 -0
- data/lib/parse/api/users.rb +43 -0
- data/lib/parse/client.rb +225 -0
- data/lib/parse/client/authentication.rb +59 -0
- data/lib/parse/client/body_builder.rb +69 -0
- data/lib/parse/client/caching.rb +103 -0
- data/lib/parse/client/protocol.rb +15 -0
- data/lib/parse/client/request.rb +43 -0
- data/lib/parse/client/response.rb +116 -0
- data/lib/parse/model/acl.rb +182 -0
- data/lib/parse/model/associations/belongs_to.rb +121 -0
- data/lib/parse/model/associations/collection_proxy.rb +202 -0
- data/lib/parse/model/associations/has_many.rb +218 -0
- data/lib/parse/model/associations/pointer_collection_proxy.rb +71 -0
- data/lib/parse/model/associations/relation_collection_proxy.rb +134 -0
- data/lib/parse/model/bytes.rb +50 -0
- data/lib/parse/model/core/actions.rb +499 -0
- data/lib/parse/model/core/properties.rb +377 -0
- data/lib/parse/model/core/querying.rb +100 -0
- data/lib/parse/model/core/schema.rb +92 -0
- data/lib/parse/model/date.rb +50 -0
- data/lib/parse/model/file.rb +127 -0
- data/lib/parse/model/geopoint.rb +98 -0
- data/lib/parse/model/model.rb +120 -0
- data/lib/parse/model/object.rb +347 -0
- data/lib/parse/model/pointer.rb +106 -0
- data/lib/parse/model/push.rb +99 -0
- data/lib/parse/query.rb +378 -0
- data/lib/parse/query/constraint.rb +130 -0
- data/lib/parse/query/constraints.rb +176 -0
- data/lib/parse/query/operation.rb +66 -0
- data/lib/parse/query/ordering.rb +49 -0
- data/lib/parse/stack.rb +11 -0
- data/lib/parse/stack/version.rb +5 -0
- data/lib/parse/webhooks.rb +228 -0
- data/lib/parse/webhooks/payload.rb +115 -0
- data/lib/parse/webhooks/registration.rb +139 -0
- data/parse-stack.gemspec +45 -0
- metadata +340 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Parse
|
4
|
+
|
5
|
+
module API
|
6
|
+
#object fetch methods
|
7
|
+
module Schema
|
8
|
+
SCHEMA_PREFIX = "/1/schemas/".freeze
|
9
|
+
def schema(className)
|
10
|
+
request :get, "#{SCHEMA_PREFIX}#{className}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_schema(className, schema)
|
14
|
+
request :post, "#{SCHEMA_PREFIX}#{className}", body: schema
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_schema(className, schema)
|
18
|
+
request :put, "#{SCHEMA_PREFIX}#{className}", body: schema
|
19
|
+
end
|
20
|
+
|
21
|
+
end #Schema
|
22
|
+
|
23
|
+
end #API
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Parse
|
4
|
+
|
5
|
+
module API
|
6
|
+
module Users
|
7
|
+
# Note that Parse::Objects mainly use the objects.rb API since we can
|
8
|
+
# detect class names to proper URI handlers
|
9
|
+
USER_PATH_PREFIX = "/1/users".freeze
|
10
|
+
USER_CLASS = "_User".freeze
|
11
|
+
def fetch_user(id)
|
12
|
+
request :get, "#{USER_PATH_PREFIX}/#{id}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_users(query = {})
|
16
|
+
response = request :get, "#{USER_PATH_PREFIX}", query: query
|
17
|
+
response.parse_class = USER_CLASS
|
18
|
+
response
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_me(query = {})
|
22
|
+
response = request :get, "#{USER_PATH_PREFIX}/me", query: query
|
23
|
+
response.parse_class = USER_CLASS
|
24
|
+
response
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_user(id, body = {})
|
28
|
+
response = request :put, "#{USER_PATH_PREFIX}/#{id}", body: body
|
29
|
+
response.parse_class = USER_CLASS
|
30
|
+
response
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_user(id)
|
34
|
+
request :delete, "#{USER_PATH_PREFIX}/#{id}"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
end # Users
|
40
|
+
|
41
|
+
end #API
|
42
|
+
|
43
|
+
end #Parse
|
data/lib/parse/client.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require_relative "client/request"
|
4
|
+
require_relative "client/response"
|
5
|
+
require_relative "client/body_builder"
|
6
|
+
require_relative "client/authentication"
|
7
|
+
require_relative "client/caching"
|
8
|
+
require_relative "api/all"
|
9
|
+
|
10
|
+
module Parse
|
11
|
+
# Main class for the client. The client class is based on a Faraday stack.
|
12
|
+
# The Faraday stack is similar to a Rack-style application in which you can define middlewares
|
13
|
+
# that will be called for each request going out and coming back in. We use this in order to setup
|
14
|
+
# some helper middlewares such as encoding to JSON, handling Parse authentication and caching.
|
15
|
+
class Client
|
16
|
+
include Parse::API::Objects
|
17
|
+
include Parse::API::Config
|
18
|
+
include Parse::API::Files
|
19
|
+
include Parse::API::CloudFunctions
|
20
|
+
include Parse::API::Users
|
21
|
+
include Parse::API::Sessions
|
22
|
+
include Parse::API::Hooks
|
23
|
+
include Parse::API::Apps
|
24
|
+
include Parse::API::Batch
|
25
|
+
include Parse::API::Push
|
26
|
+
include Parse::API::Schema
|
27
|
+
|
28
|
+
attr_accessor :session, :cache
|
29
|
+
attr_reader :application_id, :api_key, :master_key, :version, :host
|
30
|
+
# The client can support multiple sessions. The first session created, will be placed
|
31
|
+
# under the default session tag. The :default session will be the default client to be used
|
32
|
+
# by the other classes including Parse::Query and Parse::Objects
|
33
|
+
@@sessions = { default: nil }
|
34
|
+
|
35
|
+
# get a session for a given tag. This will also create a new one for the tag if not specified.
|
36
|
+
def self.session(connection = :default)
|
37
|
+
#warn "Please call Parse::Client.setup() to initialize your parse session" if @@sessions.empty? || @@sessions[:default].nil?
|
38
|
+
@@sessions[connection] ||= self.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.setup(opts = {})
|
42
|
+
# If Proc.new is called from inside a method without any arguments of
|
43
|
+
# its own, it will return a new Proc containing the block given to
|
44
|
+
# its surrounding method.
|
45
|
+
# http://mudge.name/2011/01/26/passing-blocks-in-ruby-without-block.html
|
46
|
+
@@sessions[:default] = self.new(opts, &Proc.new)
|
47
|
+
end
|
48
|
+
|
49
|
+
# This builds a new Parse::Client stack. The options are:
|
50
|
+
# required
|
51
|
+
# :application_id - Parse Application ID. If not set it will be read from the
|
52
|
+
# PARSE_APP_ID environment variable
|
53
|
+
# :api_key - the Parse REST API Key. If not set it will be
|
54
|
+
# read from PARSE_API_KEY environment variable
|
55
|
+
# :master_key - the Parse Master Key (optional). If PARSE_MASTER_KEY env is set
|
56
|
+
# it will be used.
|
57
|
+
# optional
|
58
|
+
# :logger - boolean - whether to print the requests and responses.
|
59
|
+
# :cache - Moneta::Transformer - if set, it should be a Moneta store instance
|
60
|
+
# :expires - Integer - if set, it should be a Moneta store instance
|
61
|
+
# :adapter - the HTTP adapter to use with Faraday, defaults to Faraday.default_adapter
|
62
|
+
# :host - defaults to Parse::Protocol::Host (api.parse.com)
|
63
|
+
def initialize(opts = {})
|
64
|
+
@host = Parse::Protocol::HOST
|
65
|
+
@version = 1
|
66
|
+
@application_id = opts[:application_id] || ENV["PARSE_APP_ID"]
|
67
|
+
@api_key = opts[:api_key] || ENV["PARSE_API_KEY"]
|
68
|
+
@master_key = opts[:master_key] || ENV["PARSE_MASTER_KEY"]
|
69
|
+
opts[:adapter] ||= Faraday.default_adapter
|
70
|
+
opts[:expires] ||= 3
|
71
|
+
if @application_id.nil? || ( @api_key.nil? && @master_key.nil? )
|
72
|
+
raise "Please call Parse.setup(application_id:, api_key:) to setup a session"
|
73
|
+
end
|
74
|
+
@version_prefix = "/1/".freeze
|
75
|
+
#Configure Faraday
|
76
|
+
@session = Faraday.new("https://#{@host}", opts[:faraday]) do |conn|
|
77
|
+
#conn.request :json
|
78
|
+
|
79
|
+
conn.response :logger if opts[:logging]
|
80
|
+
|
81
|
+
# This middleware handles sending the proper authentication headers to Parse
|
82
|
+
# on each request.
|
83
|
+
|
84
|
+
# this is the required authentication middleware. Should be the first thing
|
85
|
+
# so that other middlewares have access to the env that is being set by
|
86
|
+
# this middleware. First added is first to brocess.
|
87
|
+
conn.use Parse::Middleware::Authentication,
|
88
|
+
application_id: @application_id,
|
89
|
+
master_key: @master_key,
|
90
|
+
api_key: @api_key
|
91
|
+
# This middleware turns the result from Parse into a Parse::Response object
|
92
|
+
# and making sure request that are going out, follow the proper MIME format.
|
93
|
+
# We place it after the Authentication middleware in case we need to use then
|
94
|
+
# authentication information when building request and responses.
|
95
|
+
conn.use Parse::Middleware::BodyBuilder
|
96
|
+
if opts[:cache].present? && opts[:expires].to_i > 0
|
97
|
+
unless opts[:cache].is_a?(Moneta::Transformer)
|
98
|
+
raise "Parse::Client option :cache needs to be a type of Moneta::Transformer store."
|
99
|
+
end
|
100
|
+
self.cache = opts[:cache]
|
101
|
+
conn.use Parse::Middleware::Caching, self.cache, {expires: opts[:expires].to_i }
|
102
|
+
end
|
103
|
+
|
104
|
+
yield(conn) if block_given?
|
105
|
+
|
106
|
+
conn.adapter opts[:adapter]
|
107
|
+
|
108
|
+
end
|
109
|
+
@@sessions[:default] ||= self
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def clear_cache!
|
114
|
+
self.cache.clear if self.cache.present?
|
115
|
+
end
|
116
|
+
|
117
|
+
# This is the base method to make raw requests. The first parameter is a symbol
|
118
|
+
# of the type of request - either :get, :put, :post, :delete. The second parameter
|
119
|
+
# is the path api to use. For example, to make a request for objects, you would pass the "/1/classes/<ClassName>".
|
120
|
+
# After the first two parameters, the rest are named parameters. If the request is of type :get, you can pass
|
121
|
+
# any query string parameters in hash form with query:. If it is any other request, the body of the request should be sent
|
122
|
+
# with the body: parameter. Note that the middleware will handle turning the hash sent into the body: parameter into JSON.
|
123
|
+
# If you need to override or add additional headers to a specific request (ex. when uploading a Parse File), you can do so
|
124
|
+
# with the header: paramter (also a hash).
|
125
|
+
# This method also takes in a Parse::Request object instead of the arguments listed above.
|
126
|
+
def request(method, uri = nil, body: nil, query: nil, headers: nil)
|
127
|
+
headers ||= {}
|
128
|
+
# if the first argument is a Parse::Request object, then construct it
|
129
|
+
if method.is_a?(Request)
|
130
|
+
request = method
|
131
|
+
method = request.method
|
132
|
+
uri ||= request.path
|
133
|
+
query ||= request.query
|
134
|
+
body ||= request.body
|
135
|
+
headers.merge! request.headers
|
136
|
+
end
|
137
|
+
|
138
|
+
# http method
|
139
|
+
method = method.downcase.to_sym
|
140
|
+
# set the User-Agent
|
141
|
+
headers["User-Agent".freeze] = "Parse-Ruby-Mapper v#{Parse::Stack::VERSION}".freeze
|
142
|
+
#if it is a :get request, then use query params, otherwise body.
|
143
|
+
params = (method == :get ? query : body) || {}
|
144
|
+
# if the path does not start with the '/1/' prefix, then add it to be nice.
|
145
|
+
uri.replace(@version_prefix + uri) unless uri.start_with?(@version_prefix)
|
146
|
+
# actually send the request and return the body
|
147
|
+
@session.send(method, uri, params, headers).body
|
148
|
+
rescue Faraday::Error::ClientError => e
|
149
|
+
raise Parse::ConnectionError, e.message
|
150
|
+
end
|
151
|
+
|
152
|
+
# shorthand for request(:get, uri, query: {})
|
153
|
+
def get(uri, query = nil, headers = {})
|
154
|
+
request :get, uri, query: query, headers: headers
|
155
|
+
end
|
156
|
+
|
157
|
+
# shorthand for request(:post, uri, body: {})
|
158
|
+
def post(uri, body = nil, headers = {} )
|
159
|
+
request :post, uri, body: body, headers: headers
|
160
|
+
end
|
161
|
+
|
162
|
+
# shorthand for request(:put, uri, body: {})
|
163
|
+
def put(uri, body = nil, headers = {})
|
164
|
+
request :put, uri, body: body, headers: headers
|
165
|
+
end
|
166
|
+
|
167
|
+
# shorthand for request(:delete, uri, body: {})
|
168
|
+
def delete(uri, body = nil, headers = {})
|
169
|
+
request :delete, uri, body: body, headers: headers
|
170
|
+
end
|
171
|
+
|
172
|
+
def send_request(req) #Parse::Request object
|
173
|
+
raise "Object not of Parse::Request type." unless req.is_a?(Parse::Request)
|
174
|
+
request req.method, req.path, req.body, req.headers
|
175
|
+
end
|
176
|
+
|
177
|
+
# The connectable module adds methods to objects so that they can get a default
|
178
|
+
# Parse::Client object if needed. This is mainly used for Parse::Query and Parse::Object classes.
|
179
|
+
# This is included in the Parse::Model class.
|
180
|
+
# Any subclass can override their `client` methods to provide a different session to use
|
181
|
+
module Connectable
|
182
|
+
|
183
|
+
def self.included(baseClass)
|
184
|
+
baseClass.extend ClassMethods
|
185
|
+
end
|
186
|
+
|
187
|
+
module ClassMethods
|
188
|
+
attr_accessor :client
|
189
|
+
def client
|
190
|
+
@client ||= Parse::Client.session #defaults to :default tag
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def client
|
195
|
+
self.class.client
|
196
|
+
end
|
197
|
+
|
198
|
+
end #Connectable
|
199
|
+
end
|
200
|
+
|
201
|
+
# Helper method that users should call to setup the client stack.
|
202
|
+
# A block can be passed in order to do additional client configuration.
|
203
|
+
def self.setup(opts = {})
|
204
|
+
if block_given?
|
205
|
+
Parse::Client.new(opts, &Proc.new)
|
206
|
+
else
|
207
|
+
Parse::Client.new(opts)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Helper method to call cloud functions and get results
|
212
|
+
def self.trigger_job(name, body, session: :default, raw: false)
|
213
|
+
response = Parse::Client.session(session).trigger_job(name, body)
|
214
|
+
return response if raw
|
215
|
+
response.error? ? nil : response.result["result"]
|
216
|
+
end
|
217
|
+
|
218
|
+
# Helper method to call cloud functions and get results
|
219
|
+
def self.call_function(name, body, session: :default, raw: false)
|
220
|
+
response = Parse::Client.session(session).call_function(name, body)
|
221
|
+
return response if raw
|
222
|
+
response.error? ? nil : response.result["result"]
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require_relative 'protocol'
|
4
|
+
# All Parse requests require authentication with specific header values.
|
5
|
+
# This middleware takes all outgoing requests and adds the proper header values
|
6
|
+
# base on the client configuration.
|
7
|
+
module Parse
|
8
|
+
|
9
|
+
module Middleware
|
10
|
+
|
11
|
+
class Authentication < Faraday::Middleware
|
12
|
+
include Parse::Protocol
|
13
|
+
attr_accessor :application_id
|
14
|
+
attr_accessor :api_key
|
15
|
+
attr_accessor :master_key
|
16
|
+
# The options hash should contain the proper keys to be added to the request.
|
17
|
+
def initialize(app, options = {})
|
18
|
+
super(app)
|
19
|
+
@application_id = options[:application_id]
|
20
|
+
@api_key = options[:api_key]
|
21
|
+
@master_key = options[:master_key]
|
22
|
+
@content_type = options[:content_type] || CONTENT_TYPE_FORMAT
|
23
|
+
end
|
24
|
+
|
25
|
+
# we dup the call for thread-safety
|
26
|
+
def call(env)
|
27
|
+
dup.call!(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call!(env)
|
31
|
+
# We add the main Parse protocol headers
|
32
|
+
headers = {}
|
33
|
+
raise "No Parse Application Id specified for authentication." unless @application_id.present?
|
34
|
+
headers[APP_ID] = @application_id
|
35
|
+
|
36
|
+
# Set a user agent
|
37
|
+
headers["User-Agent".freeze] = "Parse-Stack v0.0.1".freeze
|
38
|
+
headers[API_KEY] = @api_key unless @api_key.blank?
|
39
|
+
headers[MASTER_KEY] = @master_key unless @master_key.blank?
|
40
|
+
# merge the headers with the current provided headers
|
41
|
+
env[:request_headers].merge! headers
|
42
|
+
# set the content type of the request if it was not provided already.
|
43
|
+
env[:request_headers][CONTENT_TYPE] ||= @content_type
|
44
|
+
# only modify header
|
45
|
+
|
46
|
+
@app.call(env).on_complete do |response_env|
|
47
|
+
# check for return code raise an error when authentication was a failure
|
48
|
+
# if response_env[:status] == 401
|
49
|
+
# warn "Unauthorized Parse API Credentials for Application Id: #{@application_id}"
|
50
|
+
# end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end # Authenticator
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require_relative 'response'
|
4
|
+
require_relative 'protocol'
|
5
|
+
# This middleware takes an incoming response (after an outgoing request)
|
6
|
+
# and creates a Parse::Response object.
|
7
|
+
module Parse
|
8
|
+
module Middleware
|
9
|
+
class BodyBuilder < Faraday::Middleware
|
10
|
+
class << self
|
11
|
+
attr_accessor :logging
|
12
|
+
end
|
13
|
+
|
14
|
+
include Parse::Protocol
|
15
|
+
HTTP_OVERRIDE = 'X-Http-Method-Override'.freeze
|
16
|
+
|
17
|
+
# thread-safety
|
18
|
+
def call(env)
|
19
|
+
dup.call!(env)
|
20
|
+
end
|
21
|
+
|
22
|
+
def call!(env)
|
23
|
+
# the maximum url size is ~2KB, so if we request a Parse API url greater than this
|
24
|
+
# (which is most likely a very complicated query), we need to override the request method
|
25
|
+
# to be POST instead of GET and send the query parameters in the body of the POST request.
|
26
|
+
# The standard maximum POST request (which is a server setting), is usually set to 20MBs
|
27
|
+
if env[:method] == :get && env[:url].to_s.length > 2_000
|
28
|
+
env[:request_headers][HTTP_OVERRIDE] = 'GET'
|
29
|
+
env[:request_headers][CONTENT_TYPE] = 'application/x-www-form-urlencoded'.freeze
|
30
|
+
env[:body] = env[:url].query
|
31
|
+
env[:url].query = nil
|
32
|
+
#override
|
33
|
+
env[:method] = :post
|
34
|
+
# else if not a get, always make sure the request is JSON encoded if the content type matches
|
35
|
+
elsif env[:request_headers][CONTENT_TYPE] == CONTENT_TYPE_FORMAT &&
|
36
|
+
(env[:body].is_a?(Hash) || env[:body].is_a?(Array))
|
37
|
+
env[:body] = env[:body].to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
if self.class.logging
|
41
|
+
puts "[Request #{env.method.upcase}] #{env[:url]}"
|
42
|
+
puts "[Request Body] #{env[:body]}"
|
43
|
+
end
|
44
|
+
@app.call(env).on_complete do |response_env|
|
45
|
+
# on a response, create a new Parse::Response and replace the :body
|
46
|
+
# of the env
|
47
|
+
# TODO: CHECK FOR HTTP STATUS CODES
|
48
|
+
if self.class.logging
|
49
|
+
puts "[[Response ]] --------------------------------------"
|
50
|
+
puts response_env.body
|
51
|
+
puts "[[Response]] --------------------------------------\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
r = Parse::Response.new(response_env.body)
|
56
|
+
rescue Exception => e
|
57
|
+
r = Parse::Response.new
|
58
|
+
r.code = response_env.status
|
59
|
+
r.error = "Invalid Parse Response: #{e}"
|
60
|
+
end
|
61
|
+
|
62
|
+
r.code ||= response_env[:status] if r.error.present?
|
63
|
+
response_env[:body] = r
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end #Middleware
|
69
|
+
end
|