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
104
105
106
107
108
109
110
111
112
113
114
# 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]
  # Webhook trigger payloads (beforeSave/afterSave/etc.) are delivered by
  # Parse Server and, when a webhook key is configured (the default; see
  # Parse::Webhooks.allow_unauthenticated for the opt-out used in tests /
  # local dev), authenticated by it -- so they are treated as trusted,
  # server-authoritative state. A handler is meant to receive the full
  # object -- createdAt/updatedAt, ACL, internal fields and all. The only
  # thing stripped here is genuine credential material a handler never
  # legitimately needs to read (live session tokens, offline-crackable
  # password hashes); see WEBHOOK_TRIGGER_CREDENTIAL_KEYS. Protection
  # against *persisting* forged privileged fields lives on the write path
  # (changes_payload emits only declared, dirty-tracked properties), not on
  # this read path.
  if hash[:user].present?
    # Trusted hydration via .build (not .new) so server-sent timestamps and
    # data fields remain readable; credentials are removed first. Note
    # Parse::User applies its own protections, so `payload.user.auth_data`
    # is not exposed here. The built object is pristine, so a handler that
    # saves payload.user transmits nothing (no dirty changes) and cannot
    # persist forgeries.
    @user = Parse::User.build(self.class.scrub_credentials(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_credentials(hash[:object])
  @trigger_name = hash[:trigger_name]
  @original = self.class.scrub_credentials(hash[:original])
  @update = self.class.scrub_credentials(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)


219
220
221
# File 'lib/parse/webhooks/payload.rb', line 219

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

#after_find?Boolean

true if this is a afterFind webhook trigger request.

Returns:

  • (Boolean)


229
230
231
# File 'lib/parse/webhooks/payload.rb', line 229

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

#after_save?Boolean

true if this is a afterSave webhook trigger request.

Returns:

  • (Boolean)


209
210
211
# File 'lib/parse/webhooks/payload.rb', line 209

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)


199
200
201
# File 'lib/parse/webhooks/payload.rb', line 199

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

#attributesATTRIBUTES

Returns:



148
149
150
# File 'lib/parse/webhooks/payload.rb', line 148

def attributes
  ATTRIBUTES
end

#before_delete?Boolean

true if this is a beforeDelete webhook trigger request.

Returns:

  • (Boolean)


214
215
216
# File 'lib/parse/webhooks/payload.rb', line 214

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

#before_find?Boolean

true if this is a beforeFind webhook trigger request.

Returns:

  • (Boolean)


224
225
226
# File 'lib/parse/webhooks/payload.rb', line 224

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

#before_save?Boolean

true if this is a beforeSave webhook trigger request.

Returns:

  • (Boolean)


204
205
206
# File 'lib/parse/webhooks/payload.rb', line 204

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)


194
195
196
# File 'lib/parse/webhooks/payload.rb', line 194

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)



384
385
386
# File 'lib/parse/webhooks/payload.rb', line 384

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



339
340
341
# File 'lib/parse/webhooks/payload.rb', line 339

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

#function?Boolean

true if this is a webhook function request.

Returns:

  • (Boolean)


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

def function?
  @function_name.present?
end

#master?Boolean

true if the master key was used for this request.

Returns:

  • (Boolean)


169
170
171
# File 'lib/parse/webhooks/payload.rb', line 169

def master?
  @master.present?
end

#object?Boolean

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

Returns:

  • (Boolean)


234
235
236
# File 'lib/parse/webhooks/payload.rb', line 234

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

#original_parse_objectParse::Object

Returns a Parse::Object from the original object.

Returns:



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

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.



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

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.



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

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



253
254
255
256
257
258
259
260
261
262
263
# File 'lib/parse/webhooks/payload.rb', line 253

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.



344
345
346
347
# File 'lib/parse/webhooks/payload.rb', line 344

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



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/parse/webhooks/payload.rb', line 354

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)


189
190
191
# File 'lib/parse/webhooks/payload.rb', line 189

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.



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

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