Module: Parse::Core::Schema

Included in:
Object
Defined in:
lib/parse/model/core/schema.rb

Overview

Defines the Schema methods applied to a Parse::Object.

Constant Summary collapse

SCHEMA_READONLY_CLASSES =

System classes that cannot be created or modified via the schema API. These are managed automatically by Parse Server.

[
  Parse::Model::CLASS_PUSH_STATUS,
  Parse::Model::CLASS_SCHEMA
].freeze
DEFAULT_PUBLIC_CLP =

Default CLP that grants public access to all operations. Used to reset CLPs before applying new ones.

{
  "find" => { "*" => true },
  "get" => { "*" => true },
  "count" => { "*" => true },
  "create" => { "*" => true },
  "update" => { "*" => true },
  "delete" => { "*" => true },
  "addField" => { "*" => true }
}.freeze

Instance Method Summary collapse

Instance Method Details

#_default_class_level_permissions_for_upgradeObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



196
197
198
199
# File 'lib/parse/model/core/schema.rb', line 196

def _default_class_level_permissions_for_upgrade
  return nil unless defined?(Parse::Schema)
  Parse::Schema.default_class_level_permissions
end

#auto_upgrade!(include_clp: true) ⇒ Parse::Response, Boolean

Note:

This feature requires use of the master_key. No columns or fields are removed, this is a safe non-destructive upgrade.

A class method for non-destructive auto upgrading a remote schema based on the properties and relations you have defined in your local model. If the collection doesn’t exist, we create the schema. If the collection already exists, the current schema is fetched, and only add the additional fields that are missing.

Also updates Class-Level Permissions (CLPs) if defined on the model using the ‘set_clp` and `protect_fields` DSL methods.

Parameters:

  • include_clp (Boolean) (defaults to: true)

    whether to also update CLPs (default: true)

Returns:

  • (Parse::Response)

    if the remote schema was modified.

  • (Boolean)

    if no changes were made to the schema, it returns true.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/parse/model/core/schema.rb', line 122

def auto_upgrade!(include_clp: true)
  # Skip read-only system classes that Parse Server manages automatically
  if SCHEMA_READONLY_CLASSES.include?(parse_class)
    warn "[Parse] Skipping #{parse_class} - managed automatically by Parse Server"
    return true
  end

  unless client.master_key.present?
    warn "[Parse] Schema changes for #{parse_class} is only available with the master key!"
    return false
  end
  # fetch the current schema (requires master key)
  response = fetch_schema

  # if it's a core class that doesn't exist, then create the collection without any fields,
  # since parse-server will automatically create the collection with the set of core fields.
  # then fetch the schema again, to add the missing fields.
  if response.error? && self.to_s.start_with?("Parse::") #is it a core class?
    client.create_schema parse_class, {}
    response = fetch_schema
    # if it still wasn't able to be created, raise an error.
    if response.error?
      warn "[Parse] Schema error: unable to create class #{parse_class}"
      return response
    end
  end

  if response.success?
    #let's figure out the diff fields
    remote_fields = response.result["fields"]
    current_schema = schema
    current_schema[:fields] = current_schema[:fields].reduce({}) do |h, (k, v)|
      #if the field does not exist in Parse, then add it to the update list
      h[k] = v if remote_fields[k.to_s].nil?
      h
    end

    # Handle CLP updates if configured and requested
    if include_clp && respond_to?(:class_permissions) && class_permissions.present?
      # First, reset CLPs to public defaults to clear any old restrictive permissions.
      # Parse Server merges CLPs rather than replacing them, so old keys can persist
      # and cause "Permission denied" errors if not explicitly cleared.
      reset_clp!

      # Now apply the new CLP configuration
      current_schema[:classLevelPermissions] = class_permissions.as_json(include_defaults: true)
    elsif include_clp && _default_class_level_permissions_for_upgrade
      # Opt-in global default: applies only on the initial create path
      # below (existing classes do NOT get rewritten here — we only
      # reach this branch when current_schema[:fields] is non-empty,
      # i.e. there are NEW columns to add; we still avoid mutating
      # existing CLPs to prevent surprising lockouts).
    end

    return true if current_schema[:fields].empty? && !current_schema[:classLevelPermissions]
    return update_schema(current_schema)
  end

  # Create new schema (class doesn't exist)
  initial_schema = schema
  # Include CLPs in initial schema creation if configured. Order of
  # precedence:
  #   1. Per-model `class_permissions` (DSL)
  #   2. Global `Parse::Schema.default_class_level_permissions` opt-in
  #   3. None — Parse Server's wide-open defaults apply
  if include_clp && respond_to?(:class_permissions) && class_permissions.present?
    initial_schema[:classLevelPermissions] = class_permissions.as_json(include_defaults: true)
  elsif include_clp && (default_clps = _default_class_level_permissions_for_upgrade)
    initial_schema[:classLevelPermissions] = default_clps
  end
  client.create_schema parse_class, initial_schema
end

#create_schemaParse::Response

Create a new collection for this model with the schema defined by the local model.

Returns:

See Also:



61
62
63
# File 'lib/parse/model/core/schema.rb', line 61

def create_schema
  client.create_schema parse_class, schema
end

#fetch_schemaParse::Response

Fetche the current schema for this collection from Parse server.

Returns:



67
68
69
# File 'lib/parse/model/core/schema.rb', line 67

def fetch_schema
  client.schema parse_class
end

#reset_clp!(client: nil) ⇒ Parse::Response

Reset the CLP on the server to public defaults. This clears any existing restrictive permissions.

Examples:

Reset CLPs to public

Song.reset_clp!

Parameters:

  • client (Parse::Client) (defaults to: nil)

    optional client to use

Returns:



98
99
100
101
102
103
104
105
106
107
# File 'lib/parse/model/core/schema.rb', line 98

def reset_clp!(client: nil)
  client ||= self.client

  unless client.master_key.present?
    warn "[Parse] CLP reset for #{parse_class} requires the master key!"
    return nil
  end

  client.update_schema(parse_class, { "classLevelPermissions" => DEFAULT_PUBLIC_CLP })
end

#schemaHash

Generate a Parse-server compatible schema hash for performing changes to the structure of the remote collection.

Returns:

  • (Hash)

    the schema for this Parse::Object subclass.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/parse/model/core/schema.rb', line 14

def schema
  sch = { className: parse_class, fields: {} }
  #first go through all the attributes
  attributes.each do |k, v|
    # don't include the base Parse fields
    next if Parse::Properties::BASE.include?(k)
    next if v.nil?
    result = { type: v.to_s.camelize }
    # if it is a basic column property, find the right datatype
    case v
    when :integer, :float
      result[:type] = Parse::Model::TYPE_NUMBER
    when :geopoint, :geo_point
      result[:type] = Parse::Model::TYPE_GEOPOINT
    when :polygon, :geo_polygon
      result[:type] = Parse::Model::TYPE_POLYGON
    when :pointer
      result = { type: Parse::Model::TYPE_POINTER, targetClass: references[k] }
    when :acl
      result[:type] = Parse::Model::ACL
    when :timezone, :time_zone
      result[:type] = "String" # no TimeZone native in Parse
    else
      result[:type] = v.to_s.camelize
    end

    sch[:fields][k] = result
  end
  #then add all the relational column attributes
  relations.each do |k, v|
    sch[:fields][k] = { type: Parse::Model::TYPE_RELATION, targetClass: relations[k] }
  end
  sch
end

#update_schema(schema_updates = nil) ⇒ Parse::Response

Update the remote schema for this Parse collection.

Parameters:

  • schema_updates (Hash) (defaults to: nil)

    the changes to be made to the schema.

Returns:



52
53
54
55
# File 'lib/parse/model/core/schema.rb', line 52

def update_schema(schema_updates = nil)
  schema_updates ||= schema
  client.update_schema parse_class, schema_updates
end