Class: Parse::Webhooks::Payload

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Serializers::JSON
Defined in:
lib/parse/webhooks/payload.rb

Overview

Represents the data structure that Parse server sends to a registered webhook. Parse Parse allows you to receive Cloud Code webhooks on your own hosted server. The ‘Parse::Webhooks` class is a lightweight Rack application that routes incoming Cloud Code webhook requests and payloads to locally registered handlers. The payloads are Payload type of objects that represent that data that Parse sends webhook handlers.

Constant Summary collapse

ATTRIBUTES =

The set of keys that can be contained in a Parse hash payload for a webhook.

{ master: nil, user: nil,
installationId: nil, params: nil,
functionName: nil, object: nil,
original: nil, update: nil,
query: nil, log: nil,
objects: nil,
triggerName: nil }.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}) ⇒ Payload

You would normally never create a Parse::Webhooks::Payload object since it is automatically provided to you when using Parse::Webhooks.

See Also:



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/parse/webhooks/payload.rb', line 76

def initialize(hash = {})
  hash = JSON.parse(hash, max_nesting: 20) if hash.is_a?(String)
  hash = Hash[hash.map { |k, v| [k.to_s.underscore.to_sym, v] }]
  @raw = hash
  @master = hash[:master]
  # Strip protected mass-assignment keys (sessionToken, _rperm, _wperm,
  # _hashed_password, authData, roles, etc.) BEFORE constructing the
  # user object. Without this, an attacker reaching the webhook
  # endpoint with a valid key (or with the optional unauthenticated
  # mode enabled) can forge any of these fields on +payload.user+
  # via the +objectId+-present hydration branch that bypasses the
  # +Parse::Object#apply_attributes!+ protected-key filter.
  if hash[:user].present?
    @user = Parse::User.new(self.class.scrub_protected_keys(hash[:user]))
  end
  @installation_id = hash[:installation_id]
  @params = hash[:params]
  @params = @params.with_indifferent_access if @params.is_a?(Hash)
  @function_name = hash[:function_name]
  @object = self.class.scrub_protected_keys(hash[:object])
  @trigger_name = hash[:trigger_name]
  @original = self.class.scrub_protected_keys(hash[:original])
  @update = self.class.scrub_protected_keys(hash[:update]) || {}
  # Added for beforeFind and afterFind triggers
  @query = hash[:query]
  @objects = hash[:objects] || []
  @log = hash[:log]
end

Instance Attribute Details

#function_nameString Also known as: functionName

Returns the name of the function.

Returns:

  • (String)

    the name of the function.



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#installation_idString Also known as: installationId

Returns The identifier of the device that submitted the request.

Returns:

  • (String)

    The identifier of the device that submitted the request.



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#logObject

The query request in a beforeFind trigger. Available in Parse Server 2.3.1 or later.

@return [Parse::Query]

The set of matching objects in an afterFind trigger. Available in Parse Server 2.3.1 or later.

@return [<Parse::Object>]

Logging information if available. Available in Parse Server 2.3.1 or later.

@return [Hash] the set of matching objects in an afterFind trigger.


64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#masterBoolean

Returns whether the master key was used for this request.

Returns:

  • (Boolean)

    whether the master key was used for this request.



64
65
66
# File 'lib/parse/webhooks/payload.rb', line 64

def master
  @master
end

#objectHash

In a beforeSave, this attribute is the final object that will be persisted.

Returns:

  • (Hash)

    the object hash related to a webhook trigger request.

See Also:



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#objectsObject

The query request in a beforeFind trigger. Available in Parse Server 2.3.1 or later.

@return [Parse::Query]

The set of matching objects in an afterFind trigger. Available in Parse Server 2.3.1 or later.

@return [<Parse::Object>]

Logging information if available. Available in Parse Server 2.3.1 or later.

@return [Hash] the set of matching objects in an afterFind trigger.


64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#originalHash

In a beforeSave, for previously saved objects, this attribute is the Parse::Object that was previously in the persistent store.

Returns:

  • (Hash)

    the object hash related to a webhook trigger request.

See Also:



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#paramsHash

Returns The list of function arguments submitted for a function request.

Returns:

  • (Hash)

    The list of function arguments submitted for a function request.



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#queryObject

The query request in a beforeFind trigger. Available in Parse Server 2.3.1 or later.

@return [Parse::Query]

The set of matching objects in an afterFind trigger. Available in Parse Server 2.3.1 or later.

@return [<Parse::Object>]

Logging information if available. Available in Parse Server 2.3.1 or later.

@return [Hash] the set of matching objects in an afterFind trigger.


64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#rawHash

Returns the raw payload from Parse server.

Returns:

  • (Hash)

    the raw payload from Parse server.



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#trigger_nameString Also known as: triggerName

Returns the name of the trigger (ex. beforeSave, afterSave, etc.).

Returns:

  • (String)

    the name of the trigger (ex. beforeSave, afterSave, etc.)



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#updateHash

Returns the update payload in the request.

Returns:

  • (Hash)

    the update payload in the request.



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

#userParse::User

Returns the user who performed this request or action.

Returns:

  • (Parse::User)

    the user who performed this request or action.



64
# File 'lib/parse/webhooks/payload.rb', line 64

attr_accessor :master, :user, :installation_id, :params, :function_name, :object, :trigger_name

Instance Method Details

#after_delete?Boolean

true if this is a afterDelete webhook trigger request.

Returns:

  • (Boolean)


201
202
203
# File 'lib/parse/webhooks/payload.rb', line 201

def after_delete?
  trigger? && @trigger_name.to_sym == :afterDelete
end

#after_find?Boolean

true if this is a afterFind webhook trigger request.

Returns:

  • (Boolean)


211
212
213
# File 'lib/parse/webhooks/payload.rb', line 211

def after_find?
  trigger? && @trigger_name.to_sym == :afterFind
end

#after_save?Boolean

true if this is a afterSave webhook trigger request.

Returns:

  • (Boolean)


191
192
193
# File 'lib/parse/webhooks/payload.rb', line 191

def after_save?
  trigger? && @trigger_name.to_sym == :afterSave
end

#after_trigger?Boolean

true if this is a afterSave or afterDelete webhook trigger request.

Returns:

  • (Boolean)


181
182
183
# File 'lib/parse/webhooks/payload.rb', line 181

def after_trigger?
  after_save? || after_delete? || after_find?
end

#attributesATTRIBUTES

Returns:



130
131
132
# File 'lib/parse/webhooks/payload.rb', line 130

def attributes
  ATTRIBUTES
end

#before_delete?Boolean

true if this is a beforeDelete webhook trigger request.

Returns:

  • (Boolean)


196
197
198
# File 'lib/parse/webhooks/payload.rb', line 196

def before_delete?
  trigger? && @trigger_name.to_sym == :beforeDelete
end

#before_find?Boolean

true if this is a beforeFind webhook trigger request.

Returns:

  • (Boolean)


206
207
208
# File 'lib/parse/webhooks/payload.rb', line 206

def before_find?
  trigger? && @trigger_name.to_sym == :beforeFind
end

#before_save?Boolean

true if this is a beforeSave webhook trigger request.

Returns:

  • (Boolean)


186
187
188
# File 'lib/parse/webhooks/payload.rb', line 186

def before_save?
  trigger? && @trigger_name.to_sym == :beforeSave
end

#before_trigger?Boolean

true if this is a beforeSave or beforeDelete webhook trigger request.

Returns:

  • (Boolean)


176
177
178
# File 'lib/parse/webhooks/payload.rb', line 176

def before_trigger?
  before_save? || before_delete? || before_find?
end

#client_initiated?Boolean

Returns true if this webhook was triggered by a client request (JavaScript, iOS, Android, etc.) This is the inverse of ruby_initiated? and is useful for callback logic that should only run for client-initiated operations.

Returns:

  • (Boolean)

    true if the request originated from a client (not Ruby)



355
356
357
# File 'lib/parse/webhooks/payload.rb', line 355

def client_initiated?
  !ruby_initiated?
end

#error!(msg = "") ⇒ Parse::Webhooks::ResponseError

This method will intentionally raise a ResponseError with a specific message. When used inside of a registered cloud code webhook function or trigger, will halt processing and return the proper error response code back to the Parse server.

Parameters:

  • msg (String) (defaults to: "")

    the error message to send back.

Returns:

Raises:

  • Parse::Webhooks::ResponseError



310
311
312
# File 'lib/parse/webhooks/payload.rb', line 310

def error!(msg = "")
  raise Parse::Webhooks::ResponseError, msg
end

#function?Boolean

true if this is a webhook function request.

Returns:

  • (Boolean)


146
147
148
# File 'lib/parse/webhooks/payload.rb', line 146

def function?
  @function_name.present?
end

#master?Boolean

true if the master key was used for this request.

Returns:

  • (Boolean)


151
152
153
# File 'lib/parse/webhooks/payload.rb', line 151

def master?
  @master.present?
end

#object?Boolean

true if this request is a trigger that contains an object.

Returns:

  • (Boolean)


216
217
218
# File 'lib/parse/webhooks/payload.rb', line 216

def object?
  trigger? && @object.present?
end

#original_parse_objectParse::Object

Returns a Parse::Object from the original object.

Returns:



221
222
223
224
225
226
227
# File 'lib/parse/webhooks/payload.rb', line 221

def original_parse_object
  return nil unless @original.is_a?(Hash)
  # Always pass the trigger's expected class explicitly so the
  # className inside the payload cannot redirect this hydration to a
  # different class.
  Parse::Object.build(@original, parse_class)
end

#parse_classString

Returns the name of the Parse class for this request.

Returns:

  • (String)

    the name of the Parse class for this request.



156
157
158
159
160
# File 'lib/parse/webhooks/payload.rb', line 156

def parse_class
  return @webhook_class if @webhook_class.present?
  return nil unless @object.present?
  @object[Parse::Model::KEY_CLASS_NAME] || @object[:className]
end

#parse_idString Also known as: objectId

Returns the objectId in this request.

Returns:

  • (String)

    the objectId in this request.



163
164
165
166
# File 'lib/parse/webhooks/payload.rb', line 163

def parse_id
  return nil unless @object.present?
  @object[Parse::Model::OBJECT_ID] || @object[:objectId]
end

#parse_object(pristine = false) ⇒ Parse::Object

This method returns a Parse::Object by combining the original object, if was provided, with the final object. This will return a dirty tracked Parse::Object subclass, that will have information on which fields have changed between the previous state in the persistent store and the one about to be saved.

Parameters:

  • pristine (Boolean) (defaults to: false)

    whether the object should be returned without dirty tracking.

Returns:

  • (Parse::Object)

    a dirty tracked Parse::Object subclass instance



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/parse/webhooks/payload.rb', line 235

def parse_object(pristine = false)
  return nil unless object?
  return Parse::Object.build(@object, parse_class) if pristine
  # Memoize so pre-block guard application and the user webhook handler
  # observe the same instance. Otherwise field_guards applied on the
  # framework's pre-built object would be invisible to the block's
  # later parse_object call (which would construct a fresh dirty-tracked
  # object from @object/@original).
  return @parse_object if defined?(@parse_object) && !@parse_object.nil?
  @parse_object = build_parse_object
end

#parse_queryParse::Query

Returns the Parse query for a beforeFind trigger.

Returns:

  • (Parse::Query)

    the Parse query for a beforeFind trigger.



315
316
317
318
# File 'lib/parse/webhooks/payload.rb', line 315

def parse_query
  return nil unless parse_class.present? && @query.is_a?(Hash)
  Parse::Query.new parse_class, @query
end

#ruby_initiated?Boolean

Returns true if this webhook was triggered by a Ruby Parse Stack request. This is determined by checking for the ‘RB’ prefix in the request ID header. This flag is useful for preventing callback loops and implementing intelligent callback handling based on the request origin.

Returns:

  • (Boolean)

    true if the request originated from Ruby Parse Stack



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/parse/webhooks/payload.rb', line 325

def ruby_initiated?
  @ruby_initiated ||= begin
      request_id = nil

      if @raw.respond_to?(:[])
        # Check for headers at the top level first
        request_id = @raw["x-parse-request-id"] || @raw["X-Parse-Request-Id"] ||
                     @raw[:x_parse_request_id] || @raw[:'X-Parse-Request-Id']

        # If not found at top level, check nested headers
        if request_id.nil?
          headers_sym = @raw[:headers] if @raw[:headers].is_a?(Hash)
          headers_str = @raw["headers"] if @raw["headers"].is_a?(Hash)

          if headers_sym
            request_id = headers_sym["x-parse-request-id"] || headers_sym["X-Parse-Request-Id"]
          elsif headers_str
            request_id = headers_str["x-parse-request-id"] || headers_str["X-Parse-Request-Id"]
          end
        end
      end

      request_id&.start_with?("_RB_") || false
    end
end

#trigger?Boolean

true if this is a webhook trigger request.

Returns:

  • (Boolean)


171
172
173
# File 'lib/parse/webhooks/payload.rb', line 171

def trigger?
  @trigger_name.present?
end

#wlog(s) ⇒ Object

Method to print to standard that utilizes the an internal id to make it easier to trace incoming requests.



136
137
138
139
140
141
142
143
# File 'lib/parse/webhooks/payload.rb', line 136

def wlog(s)
  # generates a unique random number in order to be used in logging. This
  # is useful when debugging issues in production where one server instance
  # may be running multiple threads and you want to trace the incoming call.
  @rid ||= rand(999).to_s.rjust(3)
  puts "[> #{@rid}] #{s}"
  @rid
end