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,130 @@
1
+ require_relative 'operation'
2
+ require 'time'
3
+ # Constraints are the heart of the Parse::Query system.
4
+ # Each constraint is made up of an Operation and a value (the right side
5
+ # of an operator). Constraints are responsible for making their specific
6
+ # Parse hash format required when sending Queries to Parse. All constraints can
7
+ # be combined by merging different constraints (since they are multiple hashes)
8
+ # and some constraints may have higher precedence than others (ex. equality is higher
9
+ # precedence than an "in" query).
10
+ # All constraints should inherit from Parse::Constraint and should
11
+ # register their specific Operation method (ex. :eq or :lte)
12
+ # For more information about the query design pattern from DataMapper
13
+ # that inspired this, see http://datamapper.org/docs/find.html
14
+ module Parse
15
+ class Constraint
16
+
17
+ attr_accessor :operation, :value
18
+ # A constraint needs an operation and a value.
19
+ # You may also pass a block to modify the operation if needed
20
+ def initialize(operation, value)
21
+ # if the first parameter is not an Operation, but it is a symbol
22
+ # it most likely is just the field name, so let's assume they want
23
+ # the default equality operation.
24
+ if operation.is_a?(Operation) == false && operation.respond_to?(:to_sym)
25
+ operation = Operation.new(operation.to_sym, :eq)
26
+ end
27
+ @operation = operation
28
+ @value = value
29
+ yield(self) if block_given?
30
+
31
+ end
32
+
33
+ # Creates a new constraint given an operation and value.
34
+ def self.create(operation, value)
35
+ #default to a generic equality constraint if not passed an operation
36
+ unless operation.is_a?(Parse::Operation) && operation.valid?
37
+ return self.new(operation, value)
38
+ end
39
+ operation.constraint(value)
40
+ end
41
+
42
+ class << self
43
+ # The class attributes keep track of the Parse key (special Parse
44
+ # text symbol representing this operation. Ex. local method could be called
45
+ # .ex, where the Parse Query operation that should be sent out is "$exists")
46
+ # in this case, key should be set to "$exists"
47
+ attr_accessor :key
48
+ # Precedence defines the priority of this operation when merging.
49
+ # The higher the more priority it will receive.
50
+ attr_accessor :precedence
51
+
52
+ # method to set the keyword for this Constaint (subclasses)
53
+ def contraint_keyword(k)
54
+ @key = k
55
+ end
56
+
57
+ def precedence(v = nil)
58
+ @precedence = 0 if @precedence.nil?
59
+ @precedence = v unless v.nil?
60
+ @precedence
61
+ end
62
+
63
+ end
64
+
65
+ def precedence
66
+ self.class.precedence
67
+ end
68
+
69
+ def key
70
+ self.class.key
71
+ end
72
+
73
+ # All subclasses should register their operation and themselves
74
+ # as the handler.
75
+ def self.register(op, klass = self)
76
+ Operation.register op, klass
77
+ end
78
+
79
+ def operand
80
+ @operation.operand unless @operation.nil?
81
+ end
82
+ def operand=(o)
83
+ @operation.operand = o unless @operation.nil?
84
+ end
85
+
86
+ def operator
87
+ @operation.operator unless @operation.nil?
88
+ end
89
+
90
+ def operator=(o)
91
+ @operation.operator = o unless @operation.nil?
92
+ end
93
+
94
+ def inspect
95
+ "<#{self.class} #{operator.to_s}(#{operand.inspect}, `#{value}`)>"
96
+ end
97
+
98
+ def as_json(*args)
99
+ build
100
+ end
101
+
102
+ # subclasses should override the build method depending on how they
103
+ # need to construct the Parse formatted query hash
104
+ # The default case below is for supporting equality.
105
+ # Before the final value is set int he hash, we call formatted_value in case
106
+ # we need to format the value for particular data types.
107
+ def build
108
+ return { @operation.operand => formatted_value } if @operation.operator == :eq || key.nil?
109
+ { @operation.operand => { key => formatted_value } }
110
+ end
111
+
112
+ def to_s
113
+ inspect
114
+ end
115
+
116
+ # This method formats the value based on some specific data types.
117
+ def formatted_value
118
+ d = @value
119
+ d = { __type: "Date", iso: d.iso8601(3) } if d.respond_to?(:iso8601)
120
+ d = d.pointer if d.respond_to?(:pointer) #simplified query object
121
+ #d = d.pointer if d.is_a?(Parse::Object) #simplified query object
122
+ d = d.source if d.is_a?(Regexp)
123
+ d
124
+ end
125
+
126
+ register :eq, Constraint
127
+ register :eql, Constraint
128
+ precedence 100
129
+ end
130
+ end
@@ -0,0 +1,176 @@
1
+ require_relative 'constraint'
2
+
3
+ # Eac constraint type is a subclass of Parse::Constraint
4
+ # We register each keyword (which is the Parse query operator)
5
+ # and the local operator we want to use. Each of the registered local
6
+ # operators are added as methods to the Symbol class.
7
+ # For more information: https://parse.com/docs/rest/guide#queries
8
+ # For more information about the query design pattern from DataMapper
9
+ # that inspired this, see http://datamapper.org/docs/find.html
10
+ module Parse
11
+
12
+ class CompoundQueryConstraint < Constraint
13
+ contraint_keyword :$or
14
+ register :or
15
+
16
+ def build
17
+ or_clauses = formatted_value
18
+ or_clauses = [or_clauses] unless or_clauses.is_a?(Array)
19
+ return { :$or => or_clauses }
20
+ end
21
+
22
+ end
23
+
24
+ class LessOrEqualConstraint < Constraint
25
+ contraint_keyword :$lte
26
+ register :lte
27
+ end
28
+
29
+ class LessThanConstraint < Constraint
30
+ contraint_keyword :$lt
31
+ register :lt
32
+ end
33
+
34
+ class GreaterThanConstraint < Constraint
35
+ contraint_keyword :$gt
36
+ register :gt
37
+ end
38
+
39
+ class GreaterOrEqualConstraint < Constraint
40
+ contraint_keyword :$gte
41
+ register :gte
42
+ end
43
+
44
+ class NotEqualConstraint < Constraint
45
+ contraint_keyword :$ne
46
+ register :not
47
+ end
48
+
49
+ # Mapps all items contained in the array
50
+ class ContainedInConstraint < Constraint
51
+ contraint_keyword :$in
52
+ register :in
53
+ register :contained_in
54
+
55
+ def build
56
+ val = formatted_value
57
+ val = [val].compact unless val.is_a?(Array)
58
+ { @operation.operand => { key => val } }
59
+ end
60
+
61
+ end
62
+
63
+ # Nullabiliity constraint maps $exist Parse clause a bit differently
64
+ # Parse currently has a bug that if you select items near a location
65
+ # and want to make sure a different column has a value, you need to
66
+ # search where the column does not contani a null/undefined value.
67
+ # Therefore we override the build method to change the operation to a
68
+ # NotEqualConstraint
69
+ class NullabilityConstraint < Constraint
70
+ contraint_keyword :$exists
71
+ register :null
72
+ def build
73
+ # if nullability is equal true, then $exists should be set to false
74
+
75
+ if formatted_value == true
76
+ return { @operation.operand => { key => false} }
77
+ else
78
+ #current bug in parse where if you want exists => true with geo queries
79
+ # we should map it to a "not equal to null" constraint
80
+ return { @operation.operand => { Parse::NotEqualConstraint.key => nil } }
81
+ end
82
+
83
+ end
84
+ end
85
+
86
+ class ExistsConstraint < Constraint
87
+ contraint_keyword :$exists
88
+ register :exists
89
+ def build
90
+ # if nullability is equal true, then $exists should be set to false
91
+ return { @operation.operand => { key => formatted_value } }
92
+ end
93
+ end
94
+
95
+ class NotContainedInConstraint < Constraint
96
+ contraint_keyword :$nin
97
+ register :not_in
98
+ register :not_contained_in
99
+ end
100
+
101
+ # All Things must be contained
102
+ class ContainsAllConstraint < Constraint
103
+ contraint_keyword :$all
104
+ register :all
105
+ register :contains_all
106
+ end
107
+
108
+ class SelectionConstraint < Constraint
109
+ #This matches a value for a key in the result of a different query
110
+ contraint_keyword :$select
111
+ register :select
112
+ end
113
+
114
+ class RejectionConstraint < Constraint
115
+ #requires that a key's value not match a value for a key in the result of a different query
116
+ contraint_keyword :$dontSelect
117
+ register :reject
118
+
119
+ end
120
+
121
+ class RegularExpressionConstraint < Constraint
122
+ #Requires that a key's value match a regular expression
123
+ contraint_keyword :$regex
124
+ register :like
125
+ register :regex
126
+ end
127
+
128
+ # Does the propert relational constraint.
129
+ class RelationQueryConstraint < Constraint
130
+ # matches objects in a specific column in a different class table
131
+ contraint_keyword :$relatedTo
132
+ register :related_to
133
+ register :rel
134
+ def build
135
+ # pointer = formatted_value
136
+ # unless pointer.is_a?(Parse::Pointer)
137
+ # raise "Invalid Parse::Pointer passed to :related(#{@operation.operand}) constraint : #{pointer}"
138
+ # end
139
+ { :$relatedTo => { object: formatted_value, key: @operation.operand } }
140
+ end
141
+ end
142
+
143
+ class JoinQueryConstraint < Constraint
144
+ contraint_keyword :$inQuery
145
+ register :join
146
+ register :in_query
147
+
148
+ end
149
+
150
+ class DisjointQueryConstraint < Constraint
151
+ contraint_keyword :$notInQuery
152
+ register :exclude
153
+ register :not_in_query
154
+
155
+ end
156
+
157
+ class NearSphereQueryConstraint < Constraint
158
+ contraint_keyword :$nearSphere
159
+ register :near
160
+
161
+ def build
162
+ point = formatted_value
163
+ max_miles = nil
164
+ if point.is_a?(Array) && point.count > 1
165
+ max_miles = point[2] if point.count == 3
166
+ point = { __type: "GeoPoint", latitude: point[0], longitude: point[1] }
167
+ end
168
+ if max_miles.present? && max_miles > 0
169
+ return { @operation.operand => { key => point, :$maxDistanceInMiles => max_miles.to_f } }
170
+ end
171
+ { @operation.operand => { key => point } }
172
+ end
173
+
174
+ end
175
+
176
+ end
@@ -0,0 +1,66 @@
1
+ require 'active_support/inflector'
2
+
3
+ # The base operation class used in generating queries.
4
+ # An Operation contains an operand (field) and the
5
+ # operator (ex. equals, greater than, etc)
6
+ # Each unique operation type needs a handler that is responsible
7
+ # for creating a Constraint with a given value.
8
+ # When creating a new operation, you need to register the operation
9
+ # method and the class that will be the handler.
10
+ module Parse
11
+ class Operation
12
+ attr_accessor :operand, :operator
13
+ class << self
14
+ attr_accessor :operators
15
+ def operators
16
+ @operators ||= {}
17
+ end
18
+ end
19
+ # a valid Operation has a handler, operand and operator.
20
+ def valid?
21
+ ! (@operand.nil? || @operator.nil? || handler.nil?)
22
+ end
23
+
24
+ # returns the constraint class designed to handle this operator
25
+ def handler
26
+ Operation.operators[@operator] unless @operator.nil?
27
+ end
28
+
29
+ def initialize(field, op)
30
+ self.operand = field.to_sym
31
+ self.operand = :objectId if operand == :id
32
+ self.operator = op.to_sym
33
+ end
34
+
35
+ def inspect
36
+ "#{operator.inspect}(#{operand.inspect})"
37
+ end
38
+
39
+ # create a new constraint based on the handler that had
40
+ # been registered with this operation.
41
+ def constraint(value = nil)
42
+ handler.new(self, value)
43
+ end
44
+
45
+ # have a way to register an operation type.
46
+ # Example:
47
+ # register :eq, MyEqualityHandlerClass
48
+ # the above registered the equality operator which we define to be
49
+ # a new method on the Symbol class ('eq'), which when passed a value
50
+ # we will forward the request to the MyEqualityHandlerClass, so that
51
+ # for a field called 'name', we can do
52
+ #
53
+ # :name.eq (returns operation)
54
+ # :name.eq(value) # returns constraint provided by the handler
55
+ #
56
+ def self.register(op, klass)
57
+ Operation.operators[op.to_sym] = klass
58
+ Symbol.send :define_method, op do |value = nil|
59
+ operation = Operation.new self, op
60
+ value.nil? ? operation : operation.constraint(value)
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,49 @@
1
+ # Ordering is implemented similarly as constraints in which we add
2
+ # special methods to the Symbol class. The developer can then pass one
3
+ # or an array of fields (as symbols) and call the particular ordering
4
+ # polarity (ex. :name.asc would create a Parse::Order where we want
5
+ # things to be sortd by the name field in ascending order)
6
+ # For more information about the query design pattern from DataMapper
7
+ # that inspired this, see http://datamapper.org/docs/find.html
8
+ module Parse
9
+ class Order
10
+ # We only support ascending and descending
11
+ ORDERING = {asc: '', desc: '-'}.freeze
12
+ attr_accessor :field, :direction
13
+
14
+ def initialize(field, order = :asc)
15
+ @field = field.to_sym || :objectId
16
+ @direction = order
17
+ end
18
+
19
+ def field=(f)
20
+ @field = f.to_sym
21
+ end
22
+
23
+ # get the Parse keyword for ordering.
24
+ def polarity
25
+ ORDERING[@direction] || ORDERING[:asc]
26
+ end # polarity
27
+
28
+ def to_s
29
+ "" if @field.nil?
30
+ polarity + @field.to_s
31
+ end
32
+
33
+ def inspect
34
+ "#{@direction.to_s}(#{@field.inspect})"
35
+ end
36
+
37
+ end # Order
38
+
39
+ end
40
+
41
+ # Add all the operator instance methods to the symbol classes
42
+ class Symbol
43
+ Parse::Order::ORDERING.keys.each do |sym|
44
+ define_method(sym) do
45
+ Parse::Order.new self, sym
46
+ end
47
+ end # each
48
+
49
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "stack/version"
2
+ require_relative 'client'
3
+ require_relative 'query'
4
+ require_relative 'model/object'
5
+ require_relative 'webhooks'
6
+
7
+ module Parse
8
+ module Stack
9
+ # Your code goes here...
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Parse
2
+ module Stack
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,228 @@
1
+
2
+ require 'active_model'
3
+ require 'active_support'
4
+ require 'active_support/inflector'
5
+ require 'active_support/core_ext/object'
6
+ require 'active_model_serializers'
7
+ require 'rack'
8
+ require_relative 'client'
9
+ require_relative 'stack'
10
+ require_relative 'model/object'
11
+ require_relative 'webhooks/payload'
12
+ require_relative 'webhooks/registration'
13
+
14
+
15
+ =begin
16
+ Some methods take a block, and this pattern frequently appears for a block:
17
+
18
+ {|x| x.foo}
19
+ and people would like to write that in a more concise way. In order to do that,
20
+ a symbol, the method Symbol#to_proc, implicit class casting, and & operator
21
+ are used in combination. If you put & in front of a Proc instance in the
22
+ argument position, that will be interpreted as a block. If you combine
23
+ something other than a Proc instance with &, then implicit class casting
24
+ will try to convert that to a Proc instance using to_proc method defined on
25
+ that object if there is any. In case of a Symbol instance, to_proc works in
26
+ this way:
27
+
28
+ :foo.to_proc # => ->x{x.foo}
29
+
30
+ For example, suppose you write like this:
31
+
32
+ bar(&:foo)
33
+ The & operator is combined with :foo, which is not a Proc instance, so implicit class cast applies Symbol#to_proc to it, which gives ->x{x.foo}. The & now applies to this and is interpreted as a block, which gives:
34
+
35
+ bar{|x| x.foo}
36
+ =end
37
+
38
+ module Parse
39
+
40
+ class Object
41
+
42
+ def self.webhook_function(functionName, block = nil)
43
+ if block_given?
44
+ Parse::Webhooks.route(:function, functionName, &Proc.new)
45
+ else
46
+ block = functionName.to_s.underscore.to_sym if block.blank?
47
+ block = method(block.to_sym) if block.is_a?(Symbol)
48
+ Parse::Webhooks.route(:function, functionName, block)
49
+ end
50
+ end
51
+
52
+ def self.webhook(type, block = nil)
53
+
54
+ if type == :function
55
+ unless block.is_a?(String) || block.is_a?(Symbol)
56
+ raise "Invalid Cloud Code function name: #{block}"
57
+ end
58
+ Parse::Webhooks.route(:function, block, &Proc.new)
59
+ # then block must be a symbol or a string
60
+ else
61
+ if block_given?
62
+ Parse::Webhooks.route(type, self, &Proc.new)
63
+ else
64
+ Parse::Webhooks.route(type, self, block)
65
+ end
66
+ end
67
+ #if block
68
+
69
+ end
70
+
71
+ def update_payload
72
+ h = attribute_updates
73
+ if relation_changes?
74
+ r = relation_change_operations.select { |s| s.present? }.first
75
+ h.merge!(r) if r.present?
76
+ end
77
+ h.merge!(className: parse_class) unless h.empty?
78
+ h.as_json
79
+ end
80
+
81
+ end
82
+
83
+ class Webhooks
84
+
85
+ include Client::Connectable
86
+ extend Webhook::Registration
87
+
88
+ HTTP_PARSE_WEBHOOK = "HTTP_X_PARSE_WEBHOOK_KEY".freeze
89
+ HTTP_PARSE_APPLICATION_ID = "HTTP_X_PARSE_APPLICATION_ID".freeze
90
+ CONTENT_TYPE = "application/json".freeze
91
+
92
+ attr_accessor :key
93
+ class << self
94
+ attr_accessor :logging
95
+ def routes
96
+ @routes ||= OpenStruct.new( {
97
+ before_save: {}, after_save: {},
98
+ before_delete: {}, after_delete: {}, function: {}
99
+ })
100
+ end
101
+
102
+ def route(type, className, block = nil)
103
+ type = type.to_s.underscore.to_sym #support camelcase
104
+ if type != :function && className.respond_to?(:parse_class)
105
+ className = className.parse_class
106
+ end
107
+ className = className.to_s
108
+ block = Proc.new if block_given?
109
+ if routes[type].nil? || block.respond_to?(:call) == false
110
+ raise "Invalid Webhook registration trigger #{type} #{className}"
111
+ end
112
+
113
+ # AfterSave/AfterDelete hooks support more than one
114
+ if type == :after_save || type == :after_delete
115
+
116
+ routes[type][className] ||= []
117
+ routes[type][className].push block
118
+ else
119
+ routes[type][className] = block
120
+ end
121
+ #puts "Webhook: #{type} -> #{className}..."
122
+ end
123
+
124
+ def call_route(type, className, payload = nil)
125
+ type = type.to_s.underscore.to_sym #support camelcase
126
+ className = className.parse_class if className.respond_to?(:parse_class)
127
+ className = className.to_s
128
+
129
+ return unless routes[type].present? && routes[type][className].present?
130
+ registry = routes[type][className]
131
+
132
+ if registry.is_a?(Array)
133
+ results = registry.map { |hook| hook.call(payload) }
134
+ return results.last
135
+ else
136
+ return registry.call(payload)
137
+ end
138
+ nil
139
+ end
140
+
141
+ def success(data = true)
142
+ { success: data }.to_json
143
+ end
144
+
145
+ def error(data = false)
146
+ { error: data }.to_json
147
+ end
148
+
149
+ def key
150
+ @key ||= ENV['PARSE_WEBHOOK_KEY']
151
+ end
152
+
153
+ def call(env)
154
+
155
+ request = Rack::Request.new env
156
+ response = Rack::Response.new
157
+
158
+ if @key.present? && @key =! request.env[HTTP_PARSE_WEBHOOK]
159
+ response.write error("Invalid Parse-Webhook Key")
160
+ return response.finish
161
+ end
162
+
163
+ unless request.content_type.include?(CONTENT_TYPE)
164
+ response.write error("Invalid content-type format. Should be application/json.")
165
+ return response.finish
166
+ end
167
+
168
+ request.body.rewind
169
+ begin
170
+ payload = Parse::Payload.new request.body.read
171
+ rescue Exception => e
172
+ warn "Invalid webhook payload format: #{e}"
173
+ response.write error("Invalid payload format. Should be valid JSON.")
174
+ return response.finish
175
+ end
176
+
177
+ if self.logging.present?
178
+ if payload.trigger?
179
+ puts "[ParseWebhooks Request] --> #{payload.trigger_name} #{payload.parse_class}:#{payload.parse_id}"
180
+ elsif payload.function?
181
+ puts "[ParseWebhooks Request] --> Function #{payload.function_name}"
182
+ end
183
+ if self.logging == :debug
184
+ puts "[ParseWebhooks Payload] ----------------------------"
185
+ puts payload.as_json
186
+ puts "----------------------------------------------------\n"
187
+ end
188
+ end
189
+
190
+ begin
191
+ result = true
192
+ if payload.function? && payload.function_name.present?
193
+ result = Parse::Webhooks.call_route(:function, payload.function_name, payload)
194
+ elsif payload.trigger? && payload.parse_class.present? && payload.trigger_name.present?
195
+ # call hooks subscribed to the specific class
196
+ result = Parse::Webhooks.call_route(payload.trigger_name, payload.parse_class, payload)
197
+
198
+ # call hooks subscribed to any class route
199
+ generic_result = Parse::Webhooks.call_route(payload.trigger_name, "*", payload)
200
+ result = generic_result if generic_result.present? && result.nil?
201
+ else
202
+ puts "[ParseWebhooks] --> Could not find mapping route for #{payload}"
203
+ end
204
+
205
+ result = true if result.blank?
206
+ if self.logging.present?
207
+ puts "[ParseWebhooks Response] ----------------------------"
208
+ puts success(result)
209
+ puts "----------------------------------------------------\n"
210
+ end
211
+ response.write success(result)
212
+ return response.finish
213
+ rescue Exception => e
214
+ puts "[ParseWebhooks Error] >> #{e}"
215
+ puts e.backtrace
216
+ response.write error( e.to_s )
217
+ return response.finish
218
+ end
219
+
220
+ #check if we can handle the type trigger/functionName
221
+ response.write( success )
222
+ response.finish
223
+ end # call
224
+
225
+ end #class << self
226
+ end # Webhooks
227
+
228
+ end # Parse