realm
Version:
Realm by MongoDB is an offline-first mobile database: an alternative to SQLite and key-value stores
170 lines • 7.82 kB
JavaScript
;
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2023 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
Object.defineProperty(exports, "__esModule", { value: true });
exports.validatePropertySchema = exports.validateObjectSchema = exports.validateRealmSchema = void 0;
const assert_1 = require("../assert");
const errors_1 = require("../errors");
const indirect_1 = require("../indirect");
// Need to use `CanonicalObjectSchema` rather than `ObjectSchema` due to some
// integration tests using `openRealmHook()`. That function sets `this.realm`
// to the opened realm whose schema is a `CanonicalObjectSchema[]`. Consequently,
// the key `"ctor"` (which doesn't exist on `ObjectSchema`) also needs to be allowed.
const OBJECT_SCHEMA_KEYS = new Set([
"name",
"primaryKey",
"embedded",
"asymmetric",
"properties",
// Not part of `ObjectSchema`
"ctor",
]);
// Need to use `CanonicalPropertySchema` rather than `PropertySchema`
// due to the same reasons as above.
const PROPERTY_SCHEMA_KEYS = new Set([
"type",
"objectType",
"presentation",
"property",
"default",
"optional",
"indexed",
"mapTo",
// Not part of `PropertySchema`
"name",
]);
/**
* Validate the data types of the fields of a user-provided realm schema.
*/
function validateRealmSchema(realmSchema) {
assert_1.assert.array(realmSchema, "realm schema");
for (const objectSchema of realmSchema) {
validateObjectSchema(objectSchema);
}
// TODO: Assert that backlinks point to object schemas that are actually declared
}
exports.validateRealmSchema = validateRealmSchema;
/**
* Validate the data types of the fields of a user-provided object schema.
*/
function validateObjectSchema(objectSchema) {
try {
// Schema is passed via a class based model (RealmObjectConstructor)
if (typeof objectSchema === "function") {
const clazz = objectSchema;
// We assert this later, but want a custom error message
if (!(objectSchema.prototype instanceof indirect_1.indirect.Object)) {
const schemaName = clazz.schema && clazz.schema.name;
if (typeof schemaName === "string" && schemaName !== objectSchema.name) {
throw new TypeError(`Class '${objectSchema.name}' (declaring '${schemaName}' schema) must extend Realm.Object`);
}
else {
throw new TypeError(`Class '${objectSchema.name}' must extend Realm.Object`);
}
}
assert_1.assert.object(clazz.schema, "schema static");
validateObjectSchema(clazz.schema);
}
// Schema is passed as an object (ObjectSchema)
else {
assert_1.assert.object(objectSchema, "object schema", { allowArrays: false });
const { name: objectName, properties, primaryKey, asymmetric, embedded } = objectSchema;
assert_1.assert.string(objectName, "'name' on object schema");
assert_1.assert.object(properties, `'properties' on '${objectName}'`, { allowArrays: false });
if (primaryKey !== undefined) {
assert_1.assert.string(primaryKey, `'primaryKey' on '${objectName}'`);
}
if (embedded !== undefined) {
assert_1.assert.boolean(embedded, `'embedded' on '${objectName}'`);
}
if (asymmetric !== undefined) {
assert_1.assert.boolean(asymmetric, `'asymmetric' on '${objectName}'`);
}
const invalidKeysUsed = filterInvalidKeys(objectSchema, OBJECT_SCHEMA_KEYS);
(0, assert_1.assert)(!invalidKeysUsed.length, `Unexpected field(s) found on the schema for object '${objectName}': '${invalidKeysUsed.join("', '")}'.`);
for (const propertyName in properties) {
const propertySchema = properties[propertyName];
const isUsingShorthand = typeof propertySchema === "string";
if (!isUsingShorthand) {
validatePropertySchema(objectName, propertyName, propertySchema);
}
}
}
}
catch (err) {
// Rethrow as SchemaParseError(s) rather than a mix of Error, TypeError,
// TypeAssertionError, or AssertionError.
if (err instanceof errors_1.PropertySchemaParseError) {
throw err;
}
else if (err instanceof Error) {
// This first line is a workaround to satisfy TS. Runtime check needs to be
// `const objectName = objectSchema?.name || ""` where either `objectSchema`
// or `objectSchema.name` can be undefined or an incorrect type.
const objectName = objectSchema?.name || "";
throw new errors_1.ObjectSchemaParseError(err.message, { objectName });
}
throw err;
}
}
exports.validateObjectSchema = validateObjectSchema;
/**
* Validate the data types of a user-provided property schema that ought to use the
* relaxed object notation.
*/
function validatePropertySchema(objectName, propertyName, propertySchema) {
try {
assert_1.assert.object(propertySchema, `'${propertyName}' on '${objectName}'`, { allowArrays: false });
const { type, objectType, presentation, optional, property, indexed, mapTo } = propertySchema;
assert_1.assert.string(type, `'${propertyName}.type' on '${objectName}'`);
if (objectType !== undefined) {
assert_1.assert.string(objectType, `'${propertyName}.objectType' on '${objectName}'`);
}
if (presentation !== undefined) {
assert_1.assert.string(presentation, `'${propertyName}.presentation' on '${objectName}'`);
}
if (optional !== undefined) {
assert_1.assert.boolean(optional, `'${propertyName}.optional' on '${objectName}'`);
}
if (property !== undefined) {
assert_1.assert.string(property, `'${propertyName}.property' on '${objectName}'`);
}
if (indexed !== undefined) {
(0, assert_1.assert)(typeof indexed === "boolean" || indexed === "full-text", `Expected '${propertyName}.indexed' on '${objectName}' to be a boolean or 'full-text'.`);
}
if (mapTo !== undefined) {
assert_1.assert.string(mapTo, `'${propertyName}.mapTo' on '${objectName}'`);
}
const invalidKeysUsed = filterInvalidKeys(propertySchema, PROPERTY_SCHEMA_KEYS);
(0, assert_1.assert)(!invalidKeysUsed.length, `Unexpected field(s) found on the schema for property '${propertyName}' on '${objectName}': '${invalidKeysUsed.join("', '")}'.`);
}
catch (err) {
if (err instanceof Error) {
throw new errors_1.PropertySchemaParseError(err.message, { objectName, propertyName });
}
throw err;
}
}
exports.validatePropertySchema = validatePropertySchema;
/**
* Get the keys of an object that are not part of the provided valid keys.
*/
function filterInvalidKeys(object, validKeys) {
return Object.keys(object).filter((key) => !validKeys.has(key));
}
//# sourceMappingURL=validate.js.map