1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489 |
- "use strict";
- var _node = require("parse/node");
- var _lodash = _interopRequireDefault(require("lodash"));
- var _intersect = _interopRequireDefault(require("intersect"));
- var _deepcopy = _interopRequireDefault(require("deepcopy"));
- var _logger = _interopRequireDefault(require("../logger"));
- var _Utils = _interopRequireDefault(require("../Utils"));
- var SchemaController = _interopRequireWildcard(require("./SchemaController"));
- var _StorageAdapter = require("../Adapters/Storage/StorageAdapter");
- var _MongoStorageAdapter = _interopRequireDefault(require("../Adapters/Storage/Mongo/MongoStorageAdapter"));
- var _PostgresStorageAdapter = _interopRequireDefault(require("../Adapters/Storage/Postgres/PostgresStorageAdapter"));
- var _SchemaCache = _interopRequireDefault(require("../Adapters/Cache/SchemaCache"));
- const _excluded = ["ACL"],
- _excluded2 = ["_rperm", "_wperm"];
- function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
- function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], t.indexOf(o) >= 0 || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
- function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.indexOf(n) >= 0) continue; t[n] = r[n]; } return t; }
- function addWriteACL(query, acl) {
- const newQuery = _lodash.default.cloneDeep(query);
-
- newQuery._wperm = {
- $in: [null, ...acl]
- };
- return newQuery;
- }
- function addReadACL(query, acl) {
- const newQuery = _lodash.default.cloneDeep(query);
-
- newQuery._rperm = {
- $in: [null, '*', ...acl]
- };
- return newQuery;
- }
- const transformObjectACL = _ref => {
- let {
- ACL
- } = _ref,
- result = _objectWithoutProperties(_ref, _excluded);
- if (!ACL) {
- return result;
- }
- result._wperm = [];
- result._rperm = [];
- for (const entry in ACL) {
- if (ACL[entry].read) {
- result._rperm.push(entry);
- }
- if (ACL[entry].write) {
- result._wperm.push(entry);
- }
- }
- return result;
- };
- const specialQueryKeys = ['$and', '$or', '$nor', '_rperm', '_wperm'];
- const specialMasterQueryKeys = [...specialQueryKeys, '_email_verify_token', '_perishable_token', '_tombstone', '_email_verify_token_expires_at', '_failed_login_count', '_account_lockout_expires_at', '_password_changed_at', '_password_history'];
- const validateQuery = (query, isMaster, isMaintenance, update) => {
- if (isMaintenance) {
- isMaster = true;
- }
- if (query.ACL) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_QUERY, 'Cannot query on ACL.');
- }
- if (query.$or) {
- if (query.$or instanceof Array) {
- query.$or.forEach(value => validateQuery(value, isMaster, isMaintenance, update));
- } else {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_QUERY, 'Bad $or format - use an array value.');
- }
- }
- if (query.$and) {
- if (query.$and instanceof Array) {
- query.$and.forEach(value => validateQuery(value, isMaster, isMaintenance, update));
- } else {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_QUERY, 'Bad $and format - use an array value.');
- }
- }
- if (query.$nor) {
- if (query.$nor instanceof Array && query.$nor.length > 0) {
- query.$nor.forEach(value => validateQuery(value, isMaster, isMaintenance, update));
- } else {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_QUERY, 'Bad $nor format - use an array of at least 1 value.');
- }
- }
- Object.keys(query).forEach(key => {
- if (query && query[key] && query[key].$regex) {
- if (typeof query[key].$options === 'string') {
- if (!query[key].$options.match(/^[imxs]+$/)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_QUERY, `Bad $options value for query: ${query[key].$options}`);
- }
- }
- }
- if (!key.match(/^[a-zA-Z][a-zA-Z0-9_\.]*$/) && (!specialQueryKeys.includes(key) && !isMaster && !update || update && isMaster && !specialMasterQueryKeys.includes(key))) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, `Invalid key name: ${key}`);
- }
- });
- };
- const filterSensitiveData = (isMaster, isMaintenance, aclGroup, auth, operation, schema, className, protectedFields, object) => {
- let userId = null;
- if (auth && auth.user) userId = auth.user.id;
-
- const perms = schema && schema.getClassLevelPermissions ? schema.getClassLevelPermissions(className) : {};
- if (perms) {
- const isReadOperation = ['get', 'find'].indexOf(operation) > -1;
- if (isReadOperation && perms.protectedFields) {
-
- const protectedFieldsPointerPerm = Object.keys(perms.protectedFields).filter(key => key.startsWith('userField:')).map(key => {
- return {
- key: key.substring(10),
- value: perms.protectedFields[key]
- };
- });
- const newProtectedFields = [];
- let overrideProtectedFields = false;
-
- protectedFieldsPointerPerm.forEach(pointerPerm => {
- let pointerPermIncludesUser = false;
- const readUserFieldValue = object[pointerPerm.key];
- if (readUserFieldValue) {
- if (Array.isArray(readUserFieldValue)) {
- pointerPermIncludesUser = readUserFieldValue.some(user => user.objectId && user.objectId === userId);
- } else {
- pointerPermIncludesUser = readUserFieldValue.objectId && readUserFieldValue.objectId === userId;
- }
- }
- if (pointerPermIncludesUser) {
- overrideProtectedFields = true;
- newProtectedFields.push(pointerPerm.value);
- }
- });
-
-
-
- if (overrideProtectedFields && protectedFields) {
- newProtectedFields.push(protectedFields);
- }
-
- newProtectedFields.forEach(fields => {
- if (fields) {
-
-
- if (!protectedFields) {
- protectedFields = fields;
- } else {
- protectedFields = protectedFields.filter(v => fields.includes(v));
- }
- }
- });
- }
- }
- const isUserClass = className === '_User';
- if (isUserClass) {
- object.password = object._hashed_password;
- delete object._hashed_password;
- delete object.sessionToken;
- }
- if (isMaintenance) {
- return object;
- }
-
- if (!(isUserClass && userId && object.objectId === userId)) {
- var _perms$protectedField;
- protectedFields && protectedFields.forEach(k => delete object[k]);
-
-
- perms === null || perms === void 0 || (_perms$protectedField = perms.protectedFields) === null || _perms$protectedField === void 0 || (_perms$protectedField = _perms$protectedField.temporaryKeys) === null || _perms$protectedField === void 0 || _perms$protectedField.forEach(k => delete object[k]);
- }
- for (const key in object) {
- if (key.charAt(0) === '_') {
- delete object[key];
- }
- }
- if (!isUserClass || isMaster) {
- return object;
- }
- if (aclGroup.indexOf(object.objectId) > -1) {
- return object;
- }
- delete object.authData;
- return object;
- };
- const specialKeysForUpdate = ['_hashed_password', '_perishable_token', '_email_verify_token', '_email_verify_token_expires_at', '_account_lockout_expires_at', '_failed_login_count', '_perishable_token_expires_at', '_password_changed_at', '_password_history'];
- const isSpecialUpdateKey = key => {
- return specialKeysForUpdate.indexOf(key) >= 0;
- };
- function joinTableName(className, key) {
- return `_Join:${key}:${className}`;
- }
- const flattenUpdateOperatorsForCreate = object => {
- for (const key in object) {
- if (object[key] && object[key].__op) {
- switch (object[key].__op) {
- case 'Increment':
- if (typeof object[key].amount !== 'number') {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_JSON, 'objects to add must be an array');
- }
- object[key] = object[key].amount;
- break;
- case 'SetOnInsert':
- object[key] = object[key].amount;
- break;
- case 'Add':
- if (!(object[key].objects instanceof Array)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_JSON, 'objects to add must be an array');
- }
- object[key] = object[key].objects;
- break;
- case 'AddUnique':
- if (!(object[key].objects instanceof Array)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_JSON, 'objects to add must be an array');
- }
- object[key] = object[key].objects;
- break;
- case 'Remove':
- if (!(object[key].objects instanceof Array)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_JSON, 'objects to add must be an array');
- }
- object[key] = [];
- break;
- case 'Delete':
- delete object[key];
- break;
- default:
- throw new _node.Parse.Error(_node.Parse.Error.COMMAND_UNAVAILABLE, `The ${object[key].__op} operator is not supported yet.`);
- }
- }
- }
- };
- const transformAuthData = (className, object, schema) => {
- if (object.authData && className === '_User') {
- Object.keys(object.authData).forEach(provider => {
- const providerData = object.authData[provider];
- const fieldName = `_auth_data_${provider}`;
- if (providerData == null) {
- object[fieldName] = {
- __op: 'Delete'
- };
- } else {
- object[fieldName] = providerData;
- schema.fields[fieldName] = {
- type: 'Object'
- };
- }
- });
- delete object.authData;
- }
- };
- const untransformObjectACL = _ref2 => {
- let {
- _rperm,
- _wperm
- } = _ref2,
- output = _objectWithoutProperties(_ref2, _excluded2);
- if (_rperm || _wperm) {
- output.ACL = {};
- (_rperm || []).forEach(entry => {
- if (!output.ACL[entry]) {
- output.ACL[entry] = {
- read: true
- };
- } else {
- output.ACL[entry]['read'] = true;
- }
- });
- (_wperm || []).forEach(entry => {
- if (!output.ACL[entry]) {
- output.ACL[entry] = {
- write: true
- };
- } else {
- output.ACL[entry]['write'] = true;
- }
- });
- }
- return output;
- };
- const getRootFieldName = fieldName => {
- return fieldName.split('.')[0];
- };
- const relationSchema = {
- fields: {
- relatedId: {
- type: 'String'
- },
- owningId: {
- type: 'String'
- }
- }
- };
- const convertEmailToLowercase = (object, className, options) => {
- if (className === '_User' && options.convertEmailToLowercase) {
- if (typeof object['email'] === 'string') {
- object['email'] = object['email'].toLowerCase();
- }
- }
- };
- const convertUsernameToLowercase = (object, className, options) => {
- if (className === '_User' && options.convertUsernameToLowercase) {
- if (typeof object['username'] === 'string') {
- object['username'] = object['username'].toLowerCase();
- }
- }
- };
- class DatabaseController {
- constructor(adapter, options) {
- this.adapter = adapter;
- this.options = options || {};
- this.idempotencyOptions = this.options.idempotencyOptions || {};
-
-
- this.schemaPromise = null;
- this._transactionalSession = null;
- this.options = options;
- }
- collectionExists(className) {
- return this.adapter.classExists(className);
- }
- purgeCollection(className) {
- return this.loadSchema().then(schemaController => schemaController.getOneSchema(className)).then(schema => this.adapter.deleteObjectsByQuery(className, schema, {}));
- }
- validateClassName(className) {
- if (!SchemaController.classNameIsValid(className)) {
- return Promise.reject(new _node.Parse.Error(_node.Parse.Error.INVALID_CLASS_NAME, 'invalid className: ' + className));
- }
- return Promise.resolve();
- }
-
- loadSchema(options = {
- clearCache: false
- }) {
- if (this.schemaPromise != null) {
- return this.schemaPromise;
- }
- this.schemaPromise = SchemaController.load(this.adapter, options);
- this.schemaPromise.then(() => delete this.schemaPromise, () => delete this.schemaPromise);
- return this.loadSchema(options);
- }
- loadSchemaIfNeeded(schemaController, options = {
- clearCache: false
- }) {
- return schemaController ? Promise.resolve(schemaController) : this.loadSchema(options);
- }
-
-
-
- redirectClassNameForKey(className, key) {
- return this.loadSchema().then(schema => {
- var t = schema.getExpectedType(className, key);
- if (t != null && typeof t !== 'string' && t.type === 'Relation') {
- return t.targetClass;
- }
- return className;
- });
- }
-
-
-
-
- validateObject(className, object, query, runOptions, maintenance) {
- let schema;
- const acl = runOptions.acl;
- const isMaster = acl === undefined;
- var aclGroup = acl || [];
- return this.loadSchema().then(s => {
- schema = s;
- if (isMaster) {
- return Promise.resolve();
- }
- return this.canAddField(schema, className, object, aclGroup, runOptions);
- }).then(() => {
- return schema.validateObject(className, object, query, maintenance);
- });
- }
- update(className, query, update, {
- acl,
- many,
- upsert,
- addsField
- } = {}, skipSanitization = false, validateOnly = false, validSchemaController) {
- try {
- _Utils.default.checkProhibitedKeywords(this.options, update);
- } catch (error) {
- return Promise.reject(new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, error));
- }
- const originalQuery = query;
- const originalUpdate = update;
-
- update = (0, _deepcopy.default)(update);
- var relationUpdates = [];
- var isMaster = acl === undefined;
- var aclGroup = acl || [];
- return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => {
- return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'update')).then(() => {
- relationUpdates = this.collectRelationUpdates(className, originalQuery.objectId, update);
- if (!isMaster) {
- query = this.addPointerPermissions(schemaController, className, 'update', query, aclGroup);
- if (addsField) {
- query = {
- $and: [query, this.addPointerPermissions(schemaController, className, 'addField', query, aclGroup)]
- };
- }
- }
- if (!query) {
- return Promise.resolve();
- }
- if (acl) {
- query = addWriteACL(query, acl);
- }
- validateQuery(query, isMaster, false, true);
- return schemaController.getOneSchema(className, true).catch(error => {
-
-
- if (error === undefined) {
- return {
- fields: {}
- };
- }
- throw error;
- }).then(schema => {
- Object.keys(update).forEach(fieldName => {
- if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`);
- }
- const rootFieldName = getRootFieldName(fieldName);
- if (!SchemaController.fieldNameIsValid(rootFieldName, className) && !isSpecialUpdateKey(rootFieldName)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`);
- }
- });
- for (const updateOperation in update) {
- if (update[updateOperation] && typeof update[updateOperation] === 'object' && Object.keys(update[updateOperation]).some(innerKey => innerKey.includes('$') || innerKey.includes('.'))) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_NESTED_KEY, "Nested keys should not contain the '$' or '.' characters");
- }
- }
- update = transformObjectACL(update);
- convertEmailToLowercase(update, className, this.options);
- convertUsernameToLowercase(update, className, this.options);
- transformAuthData(className, update, schema);
- if (validateOnly) {
- return this.adapter.find(className, schema, query, {}).then(result => {
- if (!result || !result.length) {
- throw new _node.Parse.Error(_node.Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
- }
- return {};
- });
- }
- if (many) {
- return this.adapter.updateObjectsByQuery(className, schema, query, update, this._transactionalSession);
- } else if (upsert) {
- return this.adapter.upsertOneObject(className, schema, query, update, this._transactionalSession);
- } else {
- return this.adapter.findOneAndUpdate(className, schema, query, update, this._transactionalSession);
- }
- });
- }).then(result => {
- if (!result) {
- throw new _node.Parse.Error(_node.Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
- }
- if (validateOnly) {
- return result;
- }
- return this.handleRelationUpdates(className, originalQuery.objectId, update, relationUpdates).then(() => {
- return result;
- });
- }).then(result => {
- if (skipSanitization) {
- return Promise.resolve(result);
- }
- return this._sanitizeDatabaseResult(originalUpdate, result);
- });
- });
- }
-
-
-
- collectRelationUpdates(className, objectId, update) {
- var ops = [];
- var deleteMe = [];
- objectId = update.objectId || objectId;
- var process = (op, key) => {
- if (!op) {
- return;
- }
- if (op.__op == 'AddRelation') {
- ops.push({
- key,
- op
- });
- deleteMe.push(key);
- }
- if (op.__op == 'RemoveRelation') {
- ops.push({
- key,
- op
- });
- deleteMe.push(key);
- }
- if (op.__op == 'Batch') {
- for (var x of op.ops) {
- process(x, key);
- }
- }
- };
- for (const key in update) {
- process(update[key], key);
- }
- for (const key of deleteMe) {
- delete update[key];
- }
- return ops;
- }
-
-
- handleRelationUpdates(className, objectId, update, ops) {
- var pending = [];
- objectId = update.objectId || objectId;
- ops.forEach(({
- key,
- op
- }) => {
- if (!op) {
- return;
- }
- if (op.__op == 'AddRelation') {
- for (const object of op.objects) {
- pending.push(this.addRelation(key, className, objectId, object.objectId));
- }
- }
- if (op.__op == 'RemoveRelation') {
- for (const object of op.objects) {
- pending.push(this.removeRelation(key, className, objectId, object.objectId));
- }
- }
- });
- return Promise.all(pending);
- }
-
-
- addRelation(key, fromClassName, fromId, toId) {
- const doc = {
- relatedId: toId,
- owningId: fromId
- };
- return this.adapter.upsertOneObject(`_Join:${key}:${fromClassName}`, relationSchema, doc, doc, this._transactionalSession);
- }
-
-
-
- removeRelation(key, fromClassName, fromId, toId) {
- var doc = {
- relatedId: toId,
- owningId: fromId
- };
- return this.adapter.deleteObjectsByQuery(`_Join:${key}:${fromClassName}`, relationSchema, doc, this._transactionalSession).catch(error => {
-
- if (error.code == _node.Parse.Error.OBJECT_NOT_FOUND) {
- return;
- }
- throw error;
- });
- }
-
-
-
-
-
-
-
- destroy(className, query, {
- acl
- } = {}, validSchemaController) {
- const isMaster = acl === undefined;
- const aclGroup = acl || [];
- return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => {
- return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'delete')).then(() => {
- if (!isMaster) {
- query = this.addPointerPermissions(schemaController, className, 'delete', query, aclGroup);
- if (!query) {
- throw new _node.Parse.Error(_node.Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
- }
- }
-
- if (acl) {
- query = addWriteACL(query, acl);
- }
- validateQuery(query, isMaster, false, false);
- return schemaController.getOneSchema(className).catch(error => {
-
-
- if (error === undefined) {
- return {
- fields: {}
- };
- }
- throw error;
- }).then(parseFormatSchema => this.adapter.deleteObjectsByQuery(className, parseFormatSchema, query, this._transactionalSession)).catch(error => {
-
- if (className === '_Session' && error.code === _node.Parse.Error.OBJECT_NOT_FOUND) {
- return Promise.resolve({});
- }
- throw error;
- });
- });
- });
- }
-
-
- create(className, object, {
- acl
- } = {}, validateOnly = false, validSchemaController) {
- try {
- _Utils.default.checkProhibitedKeywords(this.options, object);
- } catch (error) {
- return Promise.reject(new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, error));
- }
-
- const originalObject = object;
- object = transformObjectACL(object);
- convertEmailToLowercase(object, className, this.options);
- convertUsernameToLowercase(object, className, this.options);
- object.createdAt = {
- iso: object.createdAt,
- __type: 'Date'
- };
- object.updatedAt = {
- iso: object.updatedAt,
- __type: 'Date'
- };
- var isMaster = acl === undefined;
- var aclGroup = acl || [];
- const relationUpdates = this.collectRelationUpdates(className, null, object);
- return this.validateClassName(className).then(() => this.loadSchemaIfNeeded(validSchemaController)).then(schemaController => {
- return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, 'create')).then(() => schemaController.enforceClassExists(className)).then(() => schemaController.getOneSchema(className, true)).then(schema => {
- transformAuthData(className, object, schema);
- flattenUpdateOperatorsForCreate(object);
- if (validateOnly) {
- return {};
- }
- return this.adapter.createObject(className, SchemaController.convertSchemaToAdapterSchema(schema), object, this._transactionalSession);
- }).then(result => {
- if (validateOnly) {
- return originalObject;
- }
- return this.handleRelationUpdates(className, object.objectId, object, relationUpdates).then(() => {
- return this._sanitizeDatabaseResult(originalObject, result.ops[0]);
- });
- });
- });
- }
- canAddField(schema, className, object, aclGroup, runOptions) {
- const classSchema = schema.schemaData[className];
- if (!classSchema) {
- return Promise.resolve();
- }
- const fields = Object.keys(object);
- const schemaFields = Object.keys(classSchema.fields);
- const newKeys = fields.filter(field => {
-
- if (object[field] && object[field].__op && object[field].__op === 'Delete') {
- return false;
- }
- return schemaFields.indexOf(getRootFieldName(field)) < 0;
- });
- if (newKeys.length > 0) {
-
- runOptions.addsField = true;
- const action = runOptions.action;
- return schema.validatePermission(className, aclGroup, 'addField', action);
- }
- return Promise.resolve();
- }
-
-
- deleteEverything(fast = false) {
- this.schemaPromise = null;
- _SchemaCache.default.clear();
- return this.adapter.deleteAllClasses(fast);
- }
-
-
- relatedIds(className, key, owningId, queryOptions) {
- const {
- skip,
- limit,
- sort
- } = queryOptions;
- const findOptions = {};
- if (sort && sort.createdAt && this.adapter.canSortOnJoinTables) {
- findOptions.sort = {
- _id: sort.createdAt
- };
- findOptions.limit = limit;
- findOptions.skip = skip;
- queryOptions.skip = 0;
- }
- return this.adapter.find(joinTableName(className, key), relationSchema, {
- owningId
- }, findOptions).then(results => results.map(result => result.relatedId));
- }
-
-
- owningIds(className, key, relatedIds) {
- return this.adapter.find(joinTableName(className, key), relationSchema, {
- relatedId: {
- $in: relatedIds
- }
- }, {
- keys: ['owningId']
- }).then(results => results.map(result => result.owningId));
- }
-
-
-
- reduceInRelation(className, query, schema) {
-
-
- const promises = [];
- if (query['$or']) {
- const ors = query['$or'];
- promises.push(...ors.map((aQuery, index) => {
- return this.reduceInRelation(className, aQuery, schema).then(aQuery => {
- query['$or'][index] = aQuery;
- });
- }));
- }
- if (query['$and']) {
- const ands = query['$and'];
- promises.push(...ands.map((aQuery, index) => {
- return this.reduceInRelation(className, aQuery, schema).then(aQuery => {
- query['$and'][index] = aQuery;
- });
- }));
- }
- const otherKeys = Object.keys(query).map(key => {
- if (key === '$and' || key === '$or') {
- return;
- }
- const t = schema.getExpectedType(className, key);
- if (!t || t.type !== 'Relation') {
- return Promise.resolve(query);
- }
- let queries = null;
- if (query[key] && (query[key]['$in'] || query[key]['$ne'] || query[key]['$nin'] || query[key].__type == 'Pointer')) {
-
- queries = Object.keys(query[key]).map(constraintKey => {
- let relatedIds;
- let isNegation = false;
- if (constraintKey === 'objectId') {
- relatedIds = [query[key].objectId];
- } else if (constraintKey == '$in') {
- relatedIds = query[key]['$in'].map(r => r.objectId);
- } else if (constraintKey == '$nin') {
- isNegation = true;
- relatedIds = query[key]['$nin'].map(r => r.objectId);
- } else if (constraintKey == '$ne') {
- isNegation = true;
- relatedIds = [query[key]['$ne'].objectId];
- } else {
- return;
- }
- return {
- isNegation,
- relatedIds
- };
- });
- } else {
- queries = [{
- isNegation: false,
- relatedIds: []
- }];
- }
-
- delete query[key];
-
-
- const promises = queries.map(q => {
- if (!q) {
- return Promise.resolve();
- }
- return this.owningIds(className, key, q.relatedIds).then(ids => {
- if (q.isNegation) {
- this.addNotInObjectIdsIds(ids, query);
- } else {
- this.addInObjectIdsIds(ids, query);
- }
- return Promise.resolve();
- });
- });
- return Promise.all(promises).then(() => {
- return Promise.resolve();
- });
- });
- return Promise.all([...promises, ...otherKeys]).then(() => {
- return Promise.resolve(query);
- });
- }
-
-
- reduceRelationKeys(className, query, queryOptions) {
- if (query['$or']) {
- return Promise.all(query['$or'].map(aQuery => {
- return this.reduceRelationKeys(className, aQuery, queryOptions);
- }));
- }
- if (query['$and']) {
- return Promise.all(query['$and'].map(aQuery => {
- return this.reduceRelationKeys(className, aQuery, queryOptions);
- }));
- }
- var relatedTo = query['$relatedTo'];
- if (relatedTo) {
- return this.relatedIds(relatedTo.object.className, relatedTo.key, relatedTo.object.objectId, queryOptions).then(ids => {
- delete query['$relatedTo'];
- this.addInObjectIdsIds(ids, query);
- return this.reduceRelationKeys(className, query, queryOptions);
- }).then(() => {});
- }
- }
- addInObjectIdsIds(ids = null, query) {
- const idsFromString = typeof query.objectId === 'string' ? [query.objectId] : null;
- const idsFromEq = query.objectId && query.objectId['$eq'] ? [query.objectId['$eq']] : null;
- const idsFromIn = query.objectId && query.objectId['$in'] ? query.objectId['$in'] : null;
-
- const allIds = [idsFromString, idsFromEq, idsFromIn, ids].filter(list => list !== null);
- const totalLength = allIds.reduce((memo, list) => memo + list.length, 0);
- let idsIntersection = [];
- if (totalLength > 125) {
- idsIntersection = _intersect.default.big(allIds);
- } else {
- idsIntersection = (0, _intersect.default)(allIds);
- }
-
- if (!('objectId' in query)) {
- query.objectId = {
- $in: undefined
- };
- } else if (typeof query.objectId === 'string') {
- query.objectId = {
- $in: undefined,
- $eq: query.objectId
- };
- }
- query.objectId['$in'] = idsIntersection;
- return query;
- }
- addNotInObjectIdsIds(ids = [], query) {
- const idsFromNin = query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : [];
- let allIds = [...idsFromNin, ...ids].filter(list => list !== null);
-
- allIds = [...new Set(allIds)];
-
- if (!('objectId' in query)) {
- query.objectId = {
- $nin: undefined
- };
- } else if (typeof query.objectId === 'string') {
- query.objectId = {
- $nin: undefined,
- $eq: query.objectId
- };
- }
- query.objectId['$nin'] = allIds;
- return query;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- find(className, query, {
- skip,
- limit,
- acl,
- sort = {},
- count,
- keys,
- op,
- distinct,
- pipeline,
- readPreference,
- hint,
- caseInsensitive = false,
- explain,
- comment
- } = {}, auth = {}, validSchemaController) {
- const isMaintenance = auth.isMaintenance;
- const isMaster = acl === undefined || isMaintenance;
- const aclGroup = acl || [];
- op = op || (typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find');
-
- op = count === true ? 'count' : op;
- let classExists = true;
- return this.loadSchemaIfNeeded(validSchemaController).then(schemaController => {
-
-
-
- return schemaController.getOneSchema(className, isMaster).catch(error => {
-
-
- if (error === undefined) {
- classExists = false;
- return {
- fields: {}
- };
- }
- throw error;
- }).then(schema => {
-
-
-
- if (sort._created_at) {
- sort.createdAt = sort._created_at;
- delete sort._created_at;
- }
- if (sort._updated_at) {
- sort.updatedAt = sort._updated_at;
- delete sort._updated_at;
- }
- const queryOptions = {
- skip,
- limit,
- sort,
- keys,
- readPreference,
- hint,
- caseInsensitive: this.options.enableCollationCaseComparison ? false : caseInsensitive,
- explain,
- comment
- };
- Object.keys(sort).forEach(fieldName => {
- if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
- }
- const rootFieldName = getRootFieldName(fieldName);
- if (!SchemaController.fieldNameIsValid(rootFieldName, className)) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
- }
- if (!schema.fields[fieldName.split('.')[0]] && fieldName !== 'score') {
- delete sort[fieldName];
- }
- });
- return (isMaster ? Promise.resolve() : schemaController.validatePermission(className, aclGroup, op)).then(() => this.reduceRelationKeys(className, query, queryOptions)).then(() => this.reduceInRelation(className, query, schemaController)).then(() => {
- let protectedFields;
- if (!isMaster) {
- query = this.addPointerPermissions(schemaController, className, op, query, aclGroup);
-
- protectedFields = this.addProtectedFields(schemaController, className, query, aclGroup, auth, queryOptions);
- }
- if (!query) {
- if (op === 'get') {
- throw new _node.Parse.Error(_node.Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
- } else {
- return [];
- }
- }
- if (!isMaster) {
- if (op === 'update' || op === 'delete') {
- query = addWriteACL(query, aclGroup);
- } else {
- query = addReadACL(query, aclGroup);
- }
- }
- validateQuery(query, isMaster, isMaintenance, false);
- if (count) {
- if (!classExists) {
- return 0;
- } else {
- return this.adapter.count(className, schema, query, readPreference, undefined, hint, comment);
- }
- } else if (distinct) {
- if (!classExists) {
- return [];
- } else {
- return this.adapter.distinct(className, schema, query, distinct);
- }
- } else if (pipeline) {
- if (!classExists) {
- return [];
- } else {
- return this.adapter.aggregate(className, schema, pipeline, readPreference, hint, explain, comment);
- }
- } else if (explain) {
- return this.adapter.find(className, schema, query, queryOptions);
- } else {
- return this.adapter.find(className, schema, query, queryOptions).then(objects => objects.map(object => {
- object = untransformObjectACL(object);
- return filterSensitiveData(isMaster, isMaintenance, aclGroup, auth, op, schemaController, className, protectedFields, object);
- })).catch(error => {
- throw new _node.Parse.Error(_node.Parse.Error.INTERNAL_SERVER_ERROR, error);
- });
- }
- });
- });
- });
- }
- deleteSchema(className) {
- let schemaController;
- return this.loadSchema({
- clearCache: true
- }).then(s => {
- schemaController = s;
- return schemaController.getOneSchema(className, true);
- }).catch(error => {
- if (error === undefined) {
- return {
- fields: {}
- };
- } else {
- throw error;
- }
- }).then(schema => {
- return this.collectionExists(className).then(() => this.adapter.count(className, {
- fields: {}
- }, null, '', false)).then(count => {
- if (count > 0) {
- throw new _node.Parse.Error(255, `Class ${className} is not empty, contains ${count} objects, cannot drop schema.`);
- }
- return this.adapter.deleteClass(className);
- }).then(wasParseCollection => {
- if (wasParseCollection) {
- const relationFieldNames = Object.keys(schema.fields).filter(fieldName => schema.fields[fieldName].type === 'Relation');
- return Promise.all(relationFieldNames.map(name => this.adapter.deleteClass(joinTableName(className, name)))).then(() => {
- _SchemaCache.default.del(className);
- return schemaController.reloadData();
- });
- } else {
- return Promise.resolve();
- }
- });
- });
- }
-
-
-
- objectToEntriesStrings(query) {
- return Object.entries(query).map(a => a.map(s => JSON.stringify(s)).join(':'));
- }
-
- reduceOrOperation(query) {
- if (!query.$or) {
- return query;
- }
- const queries = query.$or.map(q => this.objectToEntriesStrings(q));
- let repeat = false;
- do {
- repeat = false;
- for (let i = 0; i < queries.length - 1; i++) {
- for (let j = i + 1; j < queries.length; j++) {
- const [shorter, longer] = queries[i].length > queries[j].length ? [j, i] : [i, j];
- const foundEntries = queries[shorter].reduce((acc, entry) => acc + (queries[longer].includes(entry) ? 1 : 0), 0);
- const shorterEntries = queries[shorter].length;
- if (foundEntries === shorterEntries) {
-
-
- query.$or.splice(longer, 1);
- queries.splice(longer, 1);
- repeat = true;
- break;
- }
- }
- }
- } while (repeat);
- if (query.$or.length === 1) {
- query = _objectSpread(_objectSpread({}, query), query.$or[0]);
- delete query.$or;
- }
- return query;
- }
-
- reduceAndOperation(query) {
- if (!query.$and) {
- return query;
- }
- const queries = query.$and.map(q => this.objectToEntriesStrings(q));
- let repeat = false;
- do {
- repeat = false;
- for (let i = 0; i < queries.length - 1; i++) {
- for (let j = i + 1; j < queries.length; j++) {
- const [shorter, longer] = queries[i].length > queries[j].length ? [j, i] : [i, j];
- const foundEntries = queries[shorter].reduce((acc, entry) => acc + (queries[longer].includes(entry) ? 1 : 0), 0);
- const shorterEntries = queries[shorter].length;
- if (foundEntries === shorterEntries) {
-
-
- query.$and.splice(shorter, 1);
- queries.splice(shorter, 1);
- repeat = true;
- break;
- }
- }
- }
- } while (repeat);
- if (query.$and.length === 1) {
- query = _objectSpread(_objectSpread({}, query), query.$and[0]);
- delete query.$and;
- }
- return query;
- }
-
-
-
-
-
- addPointerPermissions(schema, className, operation, query, aclGroup = []) {
-
-
- if (schema.testPermissionsForClassName(className, aclGroup, operation)) {
- return query;
- }
- const perms = schema.getClassLevelPermissions(className);
- const userACL = aclGroup.filter(acl => {
- return acl.indexOf('role:') != 0 && acl != '*';
- });
- const groupKey = ['get', 'find', 'count'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
- const permFields = [];
- if (perms[operation] && perms[operation].pointerFields) {
- permFields.push(...perms[operation].pointerFields);
- }
- if (perms[groupKey]) {
- for (const field of perms[groupKey]) {
- if (!permFields.includes(field)) {
- permFields.push(field);
- }
- }
- }
-
- if (permFields.length > 0) {
-
-
-
- if (userACL.length != 1) {
- return;
- }
- const userId = userACL[0];
- const userPointer = {
- __type: 'Pointer',
- className: '_User',
- objectId: userId
- };
- const queries = permFields.map(key => {
- const fieldDescriptor = schema.getExpectedType(className, key);
- const fieldType = fieldDescriptor && typeof fieldDescriptor === 'object' && Object.prototype.hasOwnProperty.call(fieldDescriptor, 'type') ? fieldDescriptor.type : null;
- let queryClause;
- if (fieldType === 'Pointer') {
-
- queryClause = {
- [key]: userPointer
- };
- } else if (fieldType === 'Array') {
-
- queryClause = {
- [key]: {
- $all: [userPointer]
- }
- };
- } else if (fieldType === 'Object') {
-
- queryClause = {
- [key]: userPointer
- };
- } else {
-
-
- throw Error(`An unexpected condition occurred when resolving pointer permissions: ${className} ${key}`);
- }
-
- if (Object.prototype.hasOwnProperty.call(query, key)) {
- return this.reduceAndOperation({
- $and: [queryClause, query]
- });
- }
-
- return Object.assign({}, query, queryClause);
- });
- return queries.length === 1 ? queries[0] : this.reduceOrOperation({
- $or: queries
- });
- } else {
- return query;
- }
- }
- addProtectedFields(schema, className, query = {}, aclGroup = [], auth = {}, queryOptions = {}) {
- const perms = schema && schema.getClassLevelPermissions ? schema.getClassLevelPermissions(className) : schema;
- if (!perms) return null;
- const protectedFields = perms.protectedFields;
- if (!protectedFields) return null;
- if (aclGroup.indexOf(query.objectId) > -1) return null;
-
-
-
-
- const preserveKeys = queryOptions.keys;
-
-
-
- const serverOnlyKeys = [];
- const authenticated = auth.user;
-
- const roles = (auth.userRoles || []).reduce((acc, r) => {
- acc[r] = protectedFields[r];
- return acc;
- }, {});
-
- const protectedKeysSets = [];
- for (const key in protectedFields) {
-
- if (key.startsWith('userField:')) {
- if (preserveKeys) {
- const fieldName = key.substring(10);
- if (!preserveKeys.includes(fieldName)) {
-
- queryOptions.keys && queryOptions.keys.push(fieldName);
-
- serverOnlyKeys.push(fieldName);
- }
- }
- continue;
- }
-
- if (key === '*') {
- protectedKeysSets.push(protectedFields[key]);
- continue;
- }
- if (authenticated) {
- if (key === 'authenticated') {
-
- protectedKeysSets.push(protectedFields[key]);
- continue;
- }
- if (roles[key] && key.startsWith('role:')) {
-
- protectedKeysSets.push(roles[key]);
- }
- }
- }
-
- if (authenticated) {
- const userId = auth.user.id;
- if (perms.protectedFields[userId]) {
- protectedKeysSets.push(perms.protectedFields[userId]);
- }
- }
-
- if (serverOnlyKeys.length > 0) {
- perms.protectedFields.temporaryKeys = serverOnlyKeys;
- }
- let protectedKeys = protectedKeysSets.reduce((acc, next) => {
- if (next) {
- acc.push(...next);
- }
- return acc;
- }, []);
-
- protectedKeysSets.forEach(fields => {
- if (fields) {
- protectedKeys = protectedKeys.filter(v => fields.includes(v));
- }
- });
- return protectedKeys;
- }
- createTransactionalSession() {
- return this.adapter.createTransactionalSession().then(transactionalSession => {
- this._transactionalSession = transactionalSession;
- });
- }
- commitTransactionalSession() {
- if (!this._transactionalSession) {
- throw new Error('There is no transactional session to commit');
- }
- return this.adapter.commitTransactionalSession(this._transactionalSession).then(() => {
- this._transactionalSession = null;
- });
- }
- abortTransactionalSession() {
- if (!this._transactionalSession) {
- throw new Error('There is no transactional session to abort');
- }
- return this.adapter.abortTransactionalSession(this._transactionalSession).then(() => {
- this._transactionalSession = null;
- });
- }
-
-
- async performInitialization() {
- await this.adapter.performInitialization({
- VolatileClassesSchemas: SchemaController.VolatileClassesSchemas
- });
- const requiredUserFields = {
- fields: _objectSpread(_objectSpread({}, SchemaController.defaultColumns._Default), SchemaController.defaultColumns._User)
- };
- const requiredRoleFields = {
- fields: _objectSpread(_objectSpread({}, SchemaController.defaultColumns._Default), SchemaController.defaultColumns._Role)
- };
- const requiredIdempotencyFields = {
- fields: _objectSpread(_objectSpread({}, SchemaController.defaultColumns._Default), SchemaController.defaultColumns._Idempotency)
- };
- await this.loadSchema().then(schema => schema.enforceClassExists('_User'));
- await this.loadSchema().then(schema => schema.enforceClassExists('_Role'));
- await this.loadSchema().then(schema => schema.enforceClassExists('_Idempotency'));
- await this.adapter.ensureUniqueness('_User', requiredUserFields, ['username']).catch(error => {
- _logger.default.warn('Unable to ensure uniqueness for usernames: ', error);
- throw error;
- });
- if (!this.options.enableCollationCaseComparison) {
- await this.adapter.ensureIndex('_User', requiredUserFields, ['username'], 'case_insensitive_username', true).catch(error => {
- _logger.default.warn('Unable to create case insensitive username index: ', error);
- throw error;
- });
- await this.adapter.ensureIndex('_User', requiredUserFields, ['email'], 'case_insensitive_email', true).catch(error => {
- _logger.default.warn('Unable to create case insensitive email index: ', error);
- throw error;
- });
- }
- await this.adapter.ensureUniqueness('_User', requiredUserFields, ['email']).catch(error => {
- _logger.default.warn('Unable to ensure uniqueness for user email addresses: ', error);
- throw error;
- });
- await this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name']).catch(error => {
- _logger.default.warn('Unable to ensure uniqueness for role name: ', error);
- throw error;
- });
- await this.adapter.ensureUniqueness('_Idempotency', requiredIdempotencyFields, ['reqId']).catch(error => {
- _logger.default.warn('Unable to ensure uniqueness for idempotency request ID: ', error);
- throw error;
- });
- const isMongoAdapter = this.adapter instanceof _MongoStorageAdapter.default;
- const isPostgresAdapter = this.adapter instanceof _PostgresStorageAdapter.default;
- if (isMongoAdapter || isPostgresAdapter) {
- let options = {};
- if (isMongoAdapter) {
- options = {
- ttl: 0
- };
- } else if (isPostgresAdapter) {
- options = this.idempotencyOptions;
- options.setIdempotencyFunction = true;
- }
- await this.adapter.ensureIndex('_Idempotency', requiredIdempotencyFields, ['expire'], 'ttl', false, options).catch(error => {
- _logger.default.warn('Unable to create TTL index for idempotency expire date: ', error);
- throw error;
- });
- }
- await this.adapter.updateSchemaWithIndexes();
- }
- _expandResultOnKeyPath(object, key, value) {
- if (key.indexOf('.') < 0) {
- object[key] = value[key];
- return object;
- }
- const path = key.split('.');
- const firstKey = path[0];
- const nextPath = path.slice(1).join('.');
-
- if (this.options && this.options.requestKeywordDenylist) {
-
- for (const keyword of this.options.requestKeywordDenylist) {
- const match = _Utils.default.objectContainsKeyValue({
- [firstKey]: true,
- [nextPath]: true
- }, keyword.key, true);
- if (match) {
- throw new _node.Parse.Error(_node.Parse.Error.INVALID_KEY_NAME, `Prohibited keyword in request data: ${JSON.stringify(keyword)}.`);
- }
- }
- }
- object[firstKey] = this._expandResultOnKeyPath(object[firstKey] || {}, nextPath, value[firstKey]);
- delete object[key];
- return object;
- }
- _sanitizeDatabaseResult(originalObject, result) {
- const response = {};
- if (!result) {
- return Promise.resolve(response);
- }
- Object.keys(originalObject).forEach(key => {
- const keyUpdate = originalObject[key];
-
- if (keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op && ['Add', 'AddUnique', 'Remove', 'Increment', 'SetOnInsert'].indexOf(keyUpdate.__op) > -1) {
-
-
- this._expandResultOnKeyPath(response, key, result);
-
- if (key.includes('.')) {
- const [field, index] = key.split('.');
- const isArrayIndex = Array.from(index).every(c => c >= '0' && c <= '9');
- if (isArrayIndex && Array.isArray(result[field]) && !Array.isArray(response[field])) {
- response[field] = result[field];
- }
- }
- }
- });
- return Promise.resolve(response);
- }
- }
- module.exports = DatabaseController;
- module.exports._validateQuery = validateQuery;
- module.exports.filterSensitiveData = filterSensitiveData;
|