parse-stack 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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