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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +77 -0
  4. data/LICENSE +20 -0
  5. data/README.md +1281 -0
  6. data/Rakefile +12 -0
  7. data/bin/console +20 -0
  8. data/bin/server +10 -0
  9. data/bin/setup +7 -0
  10. data/lib/parse/api/all.rb +13 -0
  11. data/lib/parse/api/analytics.rb +16 -0
  12. data/lib/parse/api/apps.rb +37 -0
  13. data/lib/parse/api/batch.rb +148 -0
  14. data/lib/parse/api/cloud_functions.rb +18 -0
  15. data/lib/parse/api/config.rb +22 -0
  16. data/lib/parse/api/files.rb +21 -0
  17. data/lib/parse/api/hooks.rb +68 -0
  18. data/lib/parse/api/objects.rb +77 -0
  19. data/lib/parse/api/push.rb +16 -0
  20. data/lib/parse/api/schemas.rb +25 -0
  21. data/lib/parse/api/sessions.rb +11 -0
  22. data/lib/parse/api/users.rb +43 -0
  23. data/lib/parse/client.rb +225 -0
  24. data/lib/parse/client/authentication.rb +59 -0
  25. data/lib/parse/client/body_builder.rb +69 -0
  26. data/lib/parse/client/caching.rb +103 -0
  27. data/lib/parse/client/protocol.rb +15 -0
  28. data/lib/parse/client/request.rb +43 -0
  29. data/lib/parse/client/response.rb +116 -0
  30. data/lib/parse/model/acl.rb +182 -0
  31. data/lib/parse/model/associations/belongs_to.rb +121 -0
  32. data/lib/parse/model/associations/collection_proxy.rb +202 -0
  33. data/lib/parse/model/associations/has_many.rb +218 -0
  34. data/lib/parse/model/associations/pointer_collection_proxy.rb +71 -0
  35. data/lib/parse/model/associations/relation_collection_proxy.rb +134 -0
  36. data/lib/parse/model/bytes.rb +50 -0
  37. data/lib/parse/model/core/actions.rb +499 -0
  38. data/lib/parse/model/core/properties.rb +377 -0
  39. data/lib/parse/model/core/querying.rb +100 -0
  40. data/lib/parse/model/core/schema.rb +92 -0
  41. data/lib/parse/model/date.rb +50 -0
  42. data/lib/parse/model/file.rb +127 -0
  43. data/lib/parse/model/geopoint.rb +98 -0
  44. data/lib/parse/model/model.rb +120 -0
  45. data/lib/parse/model/object.rb +347 -0
  46. data/lib/parse/model/pointer.rb +106 -0
  47. data/lib/parse/model/push.rb +99 -0
  48. data/lib/parse/query.rb +378 -0
  49. data/lib/parse/query/constraint.rb +130 -0
  50. data/lib/parse/query/constraints.rb +176 -0
  51. data/lib/parse/query/operation.rb +66 -0
  52. data/lib/parse/query/ordering.rb +49 -0
  53. data/lib/parse/stack.rb +11 -0
  54. data/lib/parse/stack/version.rb +5 -0
  55. data/lib/parse/webhooks.rb +228 -0
  56. data/lib/parse/webhooks/payload.rb +115 -0
  57. data/lib/parse/webhooks/registration.rb +139 -0
  58. data/parse-stack.gemspec +45 -0
  59. metadata +340 -0
@@ -0,0 +1,16 @@
1
+
2
+ module Parse
3
+
4
+ module API
5
+ #object fetch methods
6
+ module Push
7
+
8
+ def push(payload = {})
9
+ request :post, "/1/push".freeze, body: payload.as_json
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -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,11 @@
1
+
2
+
3
+ module Parse
4
+
5
+ module API
6
+ module Sessions
7
+
8
+ end
9
+ end
10
+
11
+ 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
@@ -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