mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
370f5f0eaa | ||
|
|
94bbc8e7d0 | ||
|
|
1469aaad17 | ||
|
|
40af386021 | ||
|
|
9a3774dc1c | ||
|
|
ec5337b07a | ||
|
|
75d97522cd | ||
|
|
0698c20997 | ||
|
|
3c13d8015c | ||
|
|
f2fb6bd610 | ||
|
|
19086a8112 | ||
|
|
fc805fc5da | ||
|
|
b791e13dfd | ||
|
|
c2fa25d846 | ||
|
|
12432f6455 | ||
|
|
1760dedf99 | ||
|
|
51e21d36a2 | ||
|
|
cc5f14e11a | ||
|
|
bfdcba8d5c | ||
|
|
bcd5f1717e | ||
|
|
48beb2e4b5 | ||
|
|
b08571774f | ||
|
|
81b22b21b5 | ||
|
|
1045eeb700 |
+33
-7
@@ -1,7 +1,33 @@
|
||||
## Parse Server Changelog
|
||||
|
||||
### master
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.1.0...master)
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.2.0...master)
|
||||
|
||||
### 4.2.0
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.1.0...4.2.0)
|
||||
- UPGRADE: Parse JS SDK to 2.12.0 [#6548](https://github.com/parse-community/parse-server/pull/6548)
|
||||
- NEW: Support Group aggregation on multiple columns for Postgres [#6483](https://github.com/parse-community/parse-server/pull/6483). Thanks to [Siddharth Ramesh](https://github.com/srameshr).
|
||||
- FIX: Improve test reliability by instructing Travis to only install one version of Postgres [#6490](https://github.com/parse-community/parse-server/pull/6490). Thanks to
|
||||
[Corey Baker](https://github.com/cbaker6).
|
||||
- FIX: Unknown type bug on overloaded types [#6494](https://github.com/parse-community/parse-server/pull/6494). Thanks to [Antoine Cormouls](https://github.com/Moumouls).
|
||||
- FIX: Improve reliability of 'SignIn with AppleID' [#6416](https://github.com/parse-community/parse-server/pull/6416). Thanks to [Andy King](https://github.com/andrewking0207).
|
||||
- FIX: Improve Travis reliability by separating Postgres & Mongo scripts [#6505](https://github.com/parse-community/parse-server/pull/6505). Thanks to
|
||||
[Corey Baker](https://github.com/cbaker6).
|
||||
- NEW: Apple SignIn support for multiple IDs [#6523](https://github.com/parse-community/parse-server/pull/6523). Thanks to [UnderratedDev](https://github.com/UnderratedDev).
|
||||
- NEW: Add support for new Instagram API [#6398](https://github.com/parse-community/parse-server/pull/6398). Thanks to [Maravilho Singa](https://github.com/maravilhosinga).
|
||||
- FIX: Updating Postgres/Postgis Call and Postgis to 3.0 [#6528](https://github.com/parse-community/parse-server/pull/6528). Thanks to
|
||||
[Corey Baker](https://github.com/cbaker6).
|
||||
- FIX: enableExpressErrorHandler logic [#6423](https://github.com/parse-community/parse-server/pull/6423). Thanks to [Nikolay Andryukhin](https://github.com/hybeats).
|
||||
- FIX: Change Order Enum Strategy for GraphQL [#6515](https://github.com/parse-community/parse-server/pull/6515). Thanks to [Antoine Cormouls](https://github.com/Moumouls).
|
||||
- FIX: Switch ACL to Relay Global Id for GraphQL [#6495](https://github.com/parse-community/parse-server/pull/6495). Thanks to [Antoine Cormouls](https://github.com/Moumouls).
|
||||
- FIX: Handle keys for pointer fields properly for GraphQL [#6499](https://github.com/parse-community/parse-server/pull/6499). Thanks to [Antoine Cormouls](https://github.com/Moumouls).
|
||||
- FIX: GraphQL file mutation [#6507](https://github.com/parse-community/parse-server/pull/6507). Thanks to [Antoine Cormouls](https://github.com/Moumouls).
|
||||
- FIX: Aggregate geoNear with date query [#6540](https://github.com/parse-community/parse-server/pull/6540). Thanks to [Manuel](https://github.com/mtrezza).
|
||||
- NEW: Add file triggers and file meta data [#6344](https://github.com/parse-community/parse-server/pull/6344). Thanks to [stevestencil](https://github.com/stevestencil).
|
||||
- FIX: Improve local testing of postgres [#6531](https://github.com/parse-community/parse-server/pull/6531). Thanks to
|
||||
[Corey Baker](https://github.com/cbaker6).
|
||||
- NEW: Case insensitive username and email indexing and query planning for Postgres [#6506](https://github.com/parse-community/parse-server/issues/6441). Thanks to
|
||||
[Corey Baker](https://github.com/cbaker6).
|
||||
|
||||
### 4.1.0
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.0.2...4.1.0)
|
||||
@@ -17,11 +43,11 @@ __BREAKING CHANGES:__
|
||||
2. Change username and email validation to be case insensitive. This change should be transparent in most use cases. The validation behavior should now behave 'as expected'. See [#5634](https://github.com/parse-community/parse-server/pull/5634) for details.
|
||||
|
||||
> __Special Note on Upgrading to Parse Server 4.0.0 and above__
|
||||
>
|
||||
>
|
||||
> In addition to the breaking changes noted above, [#5634](https://github.com/parse-community/parse-server/pull/5634) introduces a two new case insensitive indexes on the `User` collection. Special care should be taken when upgrading to this version to ensure that:
|
||||
>
|
||||
>
|
||||
> 1. The new indexes can be successfully created (see issue [#6465](https://github.com/parse-community/parse-server/issues/6465) for details on a potential issue for your installation).
|
||||
>
|
||||
>
|
||||
> 2. Care is taken ensure that there is adequate compute capacity to create the index in the background while still servicing requests.
|
||||
|
||||
- FIX: attempt to get travis to deploy to npmjs again. See [#6475](https://github.com/parse-community/parse-server/pull/6457). Thanks to [Arthur Cinader](https://github.com/acinader).
|
||||
@@ -35,11 +61,11 @@ __BREAKING CHANGES:__
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.10.0...4.0.0)
|
||||
|
||||
> __Special Note on Upgrading to Parse Server 4.0.0 and above__
|
||||
>
|
||||
>
|
||||
> In addition to the breaking changes noted below, [#5634](https://github.com/parse-community/parse-server/pull/5634) introduces a two new case insensitive indexes on the `User` collection. Special care should be taken when upgrading to this version to ensure that:
|
||||
>
|
||||
>
|
||||
> 1. The new indexes can be successfully created (see issue [#6465](https://github.com/parse-community/parse-server/issues/6465) for details on a potential issue for your installation).
|
||||
>
|
||||
>
|
||||
> 2. Care is taken ensure that there is adequate compute capacity to create the index in the background while still servicing requests.
|
||||
|
||||
- NEW: add hint option to Parse.Query [#6322](https://github.com/parse-community/parse-server/pull/6322). Thanks to [Steve Stencil](https://github.com/stevestencil)
|
||||
|
||||
@@ -60,7 +60,7 @@ The full documentation for Parse Server is available in the [wiki](https://githu
|
||||
- [Configuring File Adapters](#configuring-file-adapters)
|
||||
- [Logging](#logging)
|
||||
- [Live Queries](#live-queries)
|
||||
- [GraphQL (beta)](#graphql-beta)
|
||||
- [GraphQL](#graphql)
|
||||
- [Upgrading to 3.0.0](#upgrading-to-300)
|
||||
- [Support](#support)
|
||||
- [Ride the Bleeding Edge](#want-to-ride-the-bleeding-edge)
|
||||
@@ -410,12 +410,10 @@ Live queries are meant to be used in real-time reactive applications, where just
|
||||
|
||||
Take a look at [Live Query Guide](https://docs.parseplatform.org/parse-server/guide/#live-queries), [Live Query Server Setup Guide](https://docs.parseplatform.org/parse-server/guide/#scalability) and [Live Query Protocol Specification](https://github.com/parse-community/parse-server/wiki/Parse-LiveQuery-Protocol-Specification). You can setup a standalone server or multiple instances for scalability (recommended).
|
||||
|
||||
# GraphQL (beta)
|
||||
# GraphQL
|
||||
|
||||
[GraphQL](https://graphql.org/), developed by Facebook, is an open-source data query and manipulation language for APIs. In addition to the traditional REST API, Parse Server automatically generates a GraphQL API based on your current application schema. Parse Server also allows you to define your custom GraphQL queries and mutations, whose resolvers can be bound to your cloud code functions.
|
||||
|
||||
⚠️ The Parse GraphQL ```beta``` implementation is fully functional but discussions are taking place on how to improve it. So new versions of Parse Server can bring breaking changes to the current API.
|
||||
|
||||
## Running
|
||||
|
||||
### Using the CLI
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12
-1
File diff suppressed because one or more lines are too long
Generated
+515
-524
File diff suppressed because it is too large
Load Diff
+15
-14
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parse-server",
|
||||
"version": "4.1.0",
|
||||
"version": "4.2.0",
|
||||
"description": "An express module providing a Parse-compatible API server",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
@@ -24,7 +24,7 @@
|
||||
"@parse/push-adapter": "3.2.0",
|
||||
"@parse/s3-files-adapter": "1.4.0",
|
||||
"@parse/simple-mailgun-adapter": "1.1.0",
|
||||
"apollo-server-express": "2.11.0",
|
||||
"apollo-server-express": "2.12.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.19.0",
|
||||
"commander": "5.0.0",
|
||||
@@ -32,7 +32,7 @@
|
||||
"deepcopy": "2.0.0",
|
||||
"express": "4.17.1",
|
||||
"follow-redirects": "1.11.0",
|
||||
"graphql": "15.0.0",
|
||||
"graphql": "^14.5.8",
|
||||
"graphql-list-fields": "2.0.2",
|
||||
"graphql-relay": "^0.6.0",
|
||||
"graphql-tools": "^4.0.7",
|
||||
@@ -49,7 +49,7 @@
|
||||
"pg-promise": "10.5.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"redis": "3.0.2",
|
||||
"semver": "7.1.3",
|
||||
"semver": "7.2.0",
|
||||
"subscriptions-transport-ws": "0.9.16",
|
||||
"tv4": "1.3.0",
|
||||
"uuid": "3.4.0",
|
||||
@@ -60,15 +60,15 @@
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.8.4",
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.9.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "7.9.5",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.9.0",
|
||||
"@babel/preset-env": "7.9.0",
|
||||
"@babel/preset-env": "7.9.5",
|
||||
"@parse/minami": "1.0.0",
|
||||
"apollo-cache-inmemory": "1.6.5",
|
||||
"apollo-client": "2.6.6",
|
||||
"apollo-link": "1.2.13",
|
||||
"apollo-link-http": "1.5.16",
|
||||
"apollo-link-ws": "1.0.19",
|
||||
"apollo-link": "1.2.14",
|
||||
"apollo-link-http": "1.5.17",
|
||||
"apollo-link-ws": "1.0.20",
|
||||
"apollo-upload-client": "13.0.0",
|
||||
"apollo-utilities": "1.3.3",
|
||||
"babel-eslint": "10.1.0",
|
||||
@@ -81,21 +81,22 @@
|
||||
"form-data": "3.0.0",
|
||||
"gaze": "1.1.3",
|
||||
"graphql-tag": "^2.10.1",
|
||||
"husky": "4.2.3",
|
||||
"husky": "4.2.4",
|
||||
"jasmine": "3.5.0",
|
||||
"jsdoc": "3.6.3",
|
||||
"jsdoc-babel": "0.5.0",
|
||||
"lint-staged": "10.1.1",
|
||||
"lint-staged": "10.1.3",
|
||||
"mongodb-runner": "4.8.0",
|
||||
"node-fetch": "2.6.0",
|
||||
"nyc": "15.0.0",
|
||||
"prettier": "2.0.2"
|
||||
"nyc": "15.0.1",
|
||||
"prettier": "2.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"definitions": "node ./resources/buildConfigDefinitions.js",
|
||||
"docs": "jsdoc -c ./jsdoc-conf.json",
|
||||
"dev": "npm run build && node bin/dev",
|
||||
"lint": "flow && eslint --cache ./",
|
||||
"lint-fix": "eslint --fix --cache ./",
|
||||
"build": "babel src/ -d lib/ --copy-files",
|
||||
"watch": "babel --watch src/ -d lib/ --copy-files",
|
||||
"pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=4.0.4} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} MONGODB_STORAGE_ENGINE=${MONGODB_STORAGE_ENGINE:=mmapv1} mongodb-runner start",
|
||||
@@ -136,7 +137,7 @@
|
||||
"lint-staged": {
|
||||
"{src,spec}/**/*.js": [
|
||||
"prettier --write",
|
||||
"eslint --cache",
|
||||
"eslint --fix --cache",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -356,4 +356,15 @@ describe('middlewares', () => {
|
||||
middlewares.DEFAULT_ALLOWED_HEADERS
|
||||
);
|
||||
});
|
||||
|
||||
it('should use user provided on field userFromJWT', done => {
|
||||
AppCache.put(fakeReq.body._ApplicationId, {
|
||||
masterKey: 'masterKey',
|
||||
});
|
||||
fakeReq.userFromJWT = 'fake-user';
|
||||
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
|
||||
expect(fakeReq.auth.user).toEqual('fake-user');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,12 +34,12 @@ const ReadPreference = mongodb.ReadPreference;
|
||||
|
||||
const MongoSchemaCollectionName = '_SCHEMA';
|
||||
|
||||
const storageAdapterAllCollections = mongoAdapter => {
|
||||
const storageAdapterAllCollections = (mongoAdapter) => {
|
||||
return mongoAdapter
|
||||
.connect()
|
||||
.then(() => mongoAdapter.database.collections())
|
||||
.then(collections => {
|
||||
return collections.filter(collection => {
|
||||
.then((collections) => {
|
||||
return collections.filter((collection) => {
|
||||
if (collection.namespace.match(/\.system\./)) {
|
||||
return false;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const encodedUri = formatUrl(parseUrl(this._uri));
|
||||
|
||||
this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions)
|
||||
.then(client => {
|
||||
.then((client) => {
|
||||
// Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client
|
||||
// Fortunately, we can get back the options and use them to select the proper DB.
|
||||
// https://github.com/mongodb/node-mongodb-native/blob/2c35d76f08574225b8db02d7bef687123e6bb018/lib/mongo_client.js#L885
|
||||
@@ -183,7 +183,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
this.client = client;
|
||||
this.database = database;
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
delete this.connectionPromise;
|
||||
return Promise.reject(err);
|
||||
});
|
||||
@@ -212,14 +212,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
_adaptiveCollection(name: string) {
|
||||
return this.connect()
|
||||
.then(() => this.database.collection(this._collectionPrefix + name))
|
||||
.then(rawCollection => new MongoCollection(rawCollection))
|
||||
.catch(err => this.handleError(err));
|
||||
.then((rawCollection) => new MongoCollection(rawCollection))
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
_schemaCollection(): Promise<MongoSchemaCollection> {
|
||||
return this.connect()
|
||||
.then(() => this._adaptiveCollection(MongoSchemaCollectionName))
|
||||
.then(collection => new MongoSchemaCollection(collection));
|
||||
.then((collection) => new MongoSchemaCollection(collection));
|
||||
}
|
||||
|
||||
classExists(name: string) {
|
||||
@@ -229,20 +229,20 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
.listCollections({ name: this._collectionPrefix + name })
|
||||
.toArray();
|
||||
})
|
||||
.then(collections => {
|
||||
.then((collections) => {
|
||||
return collections.length > 0;
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
setClassLevelPermissions(className: string, CLPs: any): Promise<void> {
|
||||
return this._schemaCollection()
|
||||
.then(schemaCollection =>
|
||||
.then((schemaCollection) =>
|
||||
schemaCollection.updateSchema(className, {
|
||||
$set: { '_metadata.class_permissions': CLPs },
|
||||
})
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
setIndexesWithSchemaFormat(
|
||||
@@ -259,7 +259,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
const deletePromises = [];
|
||||
const insertedIndexes = [];
|
||||
Object.keys(submittedIndexes).forEach(name => {
|
||||
Object.keys(submittedIndexes).forEach((name) => {
|
||||
const field = submittedIndexes[name];
|
||||
if (existingIndexes[name] && field.__op !== 'Delete') {
|
||||
throw new Parse.Error(
|
||||
@@ -278,7 +278,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
deletePromises.push(promise);
|
||||
delete existingIndexes[name];
|
||||
} else {
|
||||
Object.keys(field).forEach(key => {
|
||||
Object.keys(field).forEach((key) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
@@ -300,17 +300,17 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
return Promise.all(deletePromises)
|
||||
.then(() => insertPromise)
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection =>
|
||||
.then((schemaCollection) =>
|
||||
schemaCollection.updateSchema(className, {
|
||||
$set: { '_metadata.indexes': existingIndexes },
|
||||
})
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
setIndexesFromMongo(className: string) {
|
||||
return this.getIndexes(className)
|
||||
.then(indexes => {
|
||||
.then((indexes) => {
|
||||
indexes = indexes.reduce((obj, index) => {
|
||||
if (index.key._fts) {
|
||||
delete index.key._fts;
|
||||
@@ -322,13 +322,13 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
obj[index.name] = index.key;
|
||||
return obj;
|
||||
}, {});
|
||||
return this._schemaCollection().then(schemaCollection =>
|
||||
return this._schemaCollection().then((schemaCollection) =>
|
||||
schemaCollection.updateSchema(className, {
|
||||
$set: { '_metadata.indexes': indexes },
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch(err => this.handleError(err))
|
||||
.catch((err) => this.handleError(err))
|
||||
.catch(() => {
|
||||
// Ignore if collection not found
|
||||
return Promise.resolve();
|
||||
@@ -351,8 +351,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
schema.fields
|
||||
)
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection => schemaCollection.insertSchema(mongoObject))
|
||||
.catch(err => this.handleError(err));
|
||||
.then((schemaCollection) => schemaCollection.insertSchema(mongoObject))
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
addFieldIfNotExists(
|
||||
@@ -361,11 +361,11 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
type: any
|
||||
): Promise<void> {
|
||||
return this._schemaCollection()
|
||||
.then(schemaCollection =>
|
||||
.then((schemaCollection) =>
|
||||
schemaCollection.addFieldIfNotExists(className, fieldName, type)
|
||||
)
|
||||
.then(() => this.createIndexesIfNeeded(className, fieldName, type))
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.)
|
||||
@@ -373,8 +373,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
deleteClass(className: string) {
|
||||
return (
|
||||
this._adaptiveCollection(className)
|
||||
.then(collection => collection.drop())
|
||||
.catch(error => {
|
||||
.then((collection) => collection.drop())
|
||||
.catch((error) => {
|
||||
// 'ns not found' means collection was already gone. Ignore deletion attempt.
|
||||
if (error.message == 'ns not found') {
|
||||
return;
|
||||
@@ -383,17 +383,17 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
})
|
||||
// We've dropped the collection, now remove the _SCHEMA document
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection =>
|
||||
.then((schemaCollection) =>
|
||||
schemaCollection.findAndDeleteSchema(className)
|
||||
)
|
||||
.catch(err => this.handleError(err))
|
||||
.catch((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
deleteAllClasses(fast: boolean) {
|
||||
return storageAdapterAllCollections(this).then(collections =>
|
||||
return storageAdapterAllCollections(this).then((collections) =>
|
||||
Promise.all(
|
||||
collections.map(collection =>
|
||||
collections.map((collection) =>
|
||||
fast ? collection.deleteMany({}) : collection.drop()
|
||||
)
|
||||
)
|
||||
@@ -421,7 +421,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
|
||||
// Returns a Promise.
|
||||
deleteFields(className: string, schema: SchemaType, fieldNames: string[]) {
|
||||
const mongoFormatNames = fieldNames.map(fieldName => {
|
||||
const mongoFormatNames = fieldNames.map((fieldName) => {
|
||||
if (schema.fields[fieldName].type === 'Pointer') {
|
||||
return `_p_${fieldName}`;
|
||||
} else {
|
||||
@@ -429,23 +429,23 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
});
|
||||
const collectionUpdate = { $unset: {} };
|
||||
mongoFormatNames.forEach(name => {
|
||||
mongoFormatNames.forEach((name) => {
|
||||
collectionUpdate['$unset'][name] = null;
|
||||
});
|
||||
|
||||
const schemaUpdate = { $unset: {} };
|
||||
fieldNames.forEach(name => {
|
||||
fieldNames.forEach((name) => {
|
||||
schemaUpdate['$unset'][name] = null;
|
||||
schemaUpdate['$unset'][`_metadata.fields_options.${name}`] = null;
|
||||
});
|
||||
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection.updateMany({}, collectionUpdate))
|
||||
.then((collection) => collection.updateMany({}, collectionUpdate))
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection =>
|
||||
.then((schemaCollection) =>
|
||||
schemaCollection.updateSchema(className, schemaUpdate)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Return a promise for all schemas known to this adapter, in Parse format. In case the
|
||||
@@ -453,10 +453,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// rejection reason are TBD.
|
||||
getAllClasses(): Promise<StorageClass[]> {
|
||||
return this._schemaCollection()
|
||||
.then(schemasCollection =>
|
||||
.then((schemasCollection) =>
|
||||
schemasCollection._fetchAllSchemasFrom_SCHEMA()
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Return a promise for the schema with the given name, in Parse format. If
|
||||
@@ -464,10 +464,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// undefined as the reason.
|
||||
getClass(className: string): Promise<StorageClass> {
|
||||
return this._schemaCollection()
|
||||
.then(schemasCollection =>
|
||||
.then((schemasCollection) =>
|
||||
schemasCollection._fetchOneSchemaFrom_SCHEMA(className)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
|
||||
@@ -486,10 +486,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
schema
|
||||
);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.insertOne(mongoObject, transactionalSession)
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error.code === 11000) {
|
||||
// Duplicate value
|
||||
const err = new Parse.Error(
|
||||
@@ -509,7 +509,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Remove all objects that match the given Parse Query.
|
||||
@@ -523,11 +523,11 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
) {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => {
|
||||
.then((collection) => {
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
return collection.deleteMany(mongoWhere, transactionalSession);
|
||||
})
|
||||
.catch(err => this.handleError(err))
|
||||
.catch((err) => this.handleError(err))
|
||||
.then(
|
||||
({ result }) => {
|
||||
if (result.n === 0) {
|
||||
@@ -559,10 +559,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const mongoUpdate = transformUpdate(className, update, schema);
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.updateMany(mongoWhere, mongoUpdate, transactionalSession)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Atomically finds and updates an object based on query.
|
||||
@@ -578,14 +578,16 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const mongoUpdate = transformUpdate(className, update, schema);
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection._mongoCollection.findOneAndUpdate(mongoWhere, mongoUpdate, {
|
||||
returnOriginal: false,
|
||||
session: transactionalSession || undefined,
|
||||
})
|
||||
)
|
||||
.then(result => mongoObjectToParseObject(className, result.value, schema))
|
||||
.catch(error => {
|
||||
.then((result) =>
|
||||
mongoObjectToParseObject(className, result.value, schema)
|
||||
)
|
||||
.catch((error) => {
|
||||
if (error.code === 11000) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
@@ -594,7 +596,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Hopefully we can get rid of this. It's only used for config and hooks.
|
||||
@@ -609,10 +611,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const mongoUpdate = transformUpdate(className, update, schema);
|
||||
const mongoWhere = transformWhere(className, query, schema);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.upsertOne(mongoWhere, mongoUpdate, transactionalSession)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
|
||||
@@ -650,10 +652,17 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
{}
|
||||
);
|
||||
|
||||
// If we aren't requesting the `_id` field, we need to explicitly opt out
|
||||
// of it. Doing so in parse-server is unusual, but it can allow us to
|
||||
// optimize some queries with covering indexes.
|
||||
if (keys && !mongoKeys._id) {
|
||||
mongoKeys._id = 0;
|
||||
}
|
||||
|
||||
readPreference = this._parseReadPreference(readPreference);
|
||||
return this.createTextIndexesIfNeeded(className, query, schema)
|
||||
.then(() => this._adaptiveCollection(className))
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.find(mongoWhere, {
|
||||
skip,
|
||||
limit,
|
||||
@@ -666,15 +675,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
explain,
|
||||
})
|
||||
)
|
||||
.then(objects => {
|
||||
.then((objects) => {
|
||||
if (explain) {
|
||||
return objects;
|
||||
}
|
||||
return objects.map(object =>
|
||||
return objects.map((object) =>
|
||||
mongoObjectToParseObject(className, object, schema)
|
||||
);
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
ensureIndex(
|
||||
@@ -687,10 +696,10 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
): Promise<any> {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const indexCreationRequest = {};
|
||||
const mongoFieldNames = fieldNames.map(fieldName =>
|
||||
const mongoFieldNames = fieldNames.map((fieldName) =>
|
||||
transformKey(className, fieldName, schema)
|
||||
);
|
||||
mongoFieldNames.forEach(fieldName => {
|
||||
mongoFieldNames.forEach((fieldName) => {
|
||||
indexCreationRequest[fieldName] = indexType;
|
||||
});
|
||||
|
||||
@@ -707,16 +716,16 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
|
||||
return this._adaptiveCollection(className)
|
||||
.then(
|
||||
collection =>
|
||||
(collection) =>
|
||||
new Promise((resolve, reject) =>
|
||||
collection._mongoCollection.createIndex(
|
||||
indexCreationRequest,
|
||||
indexOptions,
|
||||
error => (error ? reject(error) : resolve())
|
||||
(error) => (error ? reject(error) : resolve())
|
||||
)
|
||||
)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
|
||||
@@ -731,17 +740,17 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
) {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
const indexCreationRequest = {};
|
||||
const mongoFieldNames = fieldNames.map(fieldName =>
|
||||
const mongoFieldNames = fieldNames.map((fieldName) =>
|
||||
transformKey(className, fieldName, schema)
|
||||
);
|
||||
mongoFieldNames.forEach(fieldName => {
|
||||
mongoFieldNames.forEach((fieldName) => {
|
||||
indexCreationRequest[fieldName] = 1;
|
||||
});
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection._ensureSparseUniqueIndexInBackground(indexCreationRequest)
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error.code === 11000) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
@@ -750,18 +759,18 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Used in tests
|
||||
_rawFind(className: string, query: QueryType) {
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.find(query, {
|
||||
maxTimeMS: this._maxTimeMS,
|
||||
})
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// Executes a count.
|
||||
@@ -775,14 +784,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
schema = convertParseSchemaToMongoSchema(schema);
|
||||
readPreference = this._parseReadPreference(readPreference);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.count(transformWhere(className, query, schema, true), {
|
||||
maxTimeMS: this._maxTimeMS,
|
||||
readPreference,
|
||||
hint,
|
||||
})
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
distinct(
|
||||
@@ -797,22 +806,22 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
const transformField = transformKey(className, fieldName, schema);
|
||||
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.distinct(
|
||||
transformField,
|
||||
transformWhere(className, query, schema)
|
||||
)
|
||||
)
|
||||
.then(objects => {
|
||||
objects = objects.filter(obj => obj != null);
|
||||
return objects.map(object => {
|
||||
.then((objects) => {
|
||||
objects = objects.filter((obj) => obj != null);
|
||||
return objects.map((object) => {
|
||||
if (isPointerField) {
|
||||
return transformPointerString(schema, fieldName, object);
|
||||
}
|
||||
return mongoObjectToParseObject(className, object, schema);
|
||||
});
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
aggregate(
|
||||
@@ -824,7 +833,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
explain?: boolean
|
||||
) {
|
||||
let isPointerField = false;
|
||||
pipeline = pipeline.map(stage => {
|
||||
pipeline = pipeline.map((stage) => {
|
||||
if (stage.$group) {
|
||||
stage.$group = this._parseAggregateGroupArgs(schema, stage.$group);
|
||||
if (
|
||||
@@ -845,13 +854,16 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
);
|
||||
}
|
||||
if (stage.$geoNear) {
|
||||
stage.$geoNear.query = this._parseAggregateArgs(schema, stage.$geoNear.query);
|
||||
stage.$geoNear.query = this._parseAggregateArgs(
|
||||
schema,
|
||||
stage.$geoNear.query
|
||||
);
|
||||
}
|
||||
return stage;
|
||||
});
|
||||
readPreference = this._parseReadPreference(readPreference);
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection =>
|
||||
.then((collection) =>
|
||||
collection.aggregate(pipeline, {
|
||||
readPreference,
|
||||
maxTimeMS: this._maxTimeMS,
|
||||
@@ -859,8 +871,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
explain,
|
||||
})
|
||||
)
|
||||
.then(results => {
|
||||
results.forEach(result => {
|
||||
.then((results) => {
|
||||
results.forEach((result) => {
|
||||
if (Object.prototype.hasOwnProperty.call(result, '_id')) {
|
||||
if (isPointerField && result._id) {
|
||||
result._id = result._id.split('$')[1];
|
||||
@@ -879,12 +891,12 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
return results;
|
||||
})
|
||||
.then(objects =>
|
||||
objects.map(object =>
|
||||
.then((objects) =>
|
||||
objects.map((object) =>
|
||||
mongoObjectToParseObject(className, object, schema)
|
||||
)
|
||||
)
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
// This function will recursively traverse the pipeline and convert any Pointer or Date columns.
|
||||
@@ -910,7 +922,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
if (pipeline === null) {
|
||||
return null;
|
||||
} else if (Array.isArray(pipeline)) {
|
||||
return pipeline.map(value => this._parseAggregateArgs(schema, value));
|
||||
return pipeline.map((value) => this._parseAggregateArgs(schema, value));
|
||||
} else if (typeof pipeline === 'object') {
|
||||
const returnValue = {};
|
||||
for (const field in pipeline) {
|
||||
@@ -985,7 +997,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
// updatedAt or objectId and change it accordingly.
|
||||
_parseAggregateGroupArgs(schema: any, pipeline: any): any {
|
||||
if (Array.isArray(pipeline)) {
|
||||
return pipeline.map(value =>
|
||||
return pipeline.map((value) =>
|
||||
this._parseAggregateGroupArgs(schema, value)
|
||||
);
|
||||
} else if (typeof pipeline === 'object') {
|
||||
@@ -1065,14 +1077,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
|
||||
createIndex(className: string, index: any) {
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection._mongoCollection.createIndex(index))
|
||||
.catch(err => this.handleError(err));
|
||||
.then((collection) => collection._mongoCollection.createIndex(index))
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
createIndexes(className: string, indexes: any) {
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection._mongoCollection.createIndexes(indexes))
|
||||
.catch(err => this.handleError(err));
|
||||
.then((collection) => collection._mongoCollection.createIndexes(indexes))
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
createIndexesIfNeeded(className: string, fieldName: string, type: any) {
|
||||
@@ -1110,7 +1122,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
textIndex,
|
||||
existingIndexes,
|
||||
schema.fields
|
||||
).catch(error => {
|
||||
).catch((error) => {
|
||||
if (error.code === 85) {
|
||||
// Index exist with different options
|
||||
return this.setIndexesFromMongo(className);
|
||||
@@ -1123,31 +1135,31 @@ export class MongoStorageAdapter implements StorageAdapter {
|
||||
|
||||
getIndexes(className: string) {
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection._mongoCollection.indexes())
|
||||
.catch(err => this.handleError(err));
|
||||
.then((collection) => collection._mongoCollection.indexes())
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
dropIndex(className: string, index: any) {
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection._mongoCollection.dropIndex(index))
|
||||
.catch(err => this.handleError(err));
|
||||
.then((collection) => collection._mongoCollection.dropIndex(index))
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
dropAllIndexes(className: string) {
|
||||
return this._adaptiveCollection(className)
|
||||
.then(collection => collection._mongoCollection.dropIndexes())
|
||||
.catch(err => this.handleError(err));
|
||||
.then((collection) => collection._mongoCollection.dropIndexes())
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
updateSchemaWithIndexes(): Promise<any> {
|
||||
return this.getAllClasses()
|
||||
.then(classes => {
|
||||
const promises = classes.map(schema => {
|
||||
.then((classes) => {
|
||||
const promises = classes.map((schema) => {
|
||||
return this.setIndexesFromMongo(schema.className);
|
||||
});
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.catch(err => this.handleError(err));
|
||||
.catch((err) => this.handleError(err));
|
||||
}
|
||||
|
||||
createTransactionalSession(): Promise<any> {
|
||||
|
||||
@@ -15,7 +15,7 @@ const PostgresUniqueIndexViolationError = '23505';
|
||||
const PostgresTransactionAbortedError = '25P02';
|
||||
const logger = require('../../../logger');
|
||||
|
||||
const debug = function(...args: any) {
|
||||
const debug = function (...args: any) {
|
||||
args = ['PG: ' + arguments[0]].concat(args.slice(1, args.length));
|
||||
const log = logger.getLogger();
|
||||
log.debug.apply(log, args);
|
||||
@@ -24,7 +24,7 @@ const debug = function(...args: any) {
|
||||
import { StorageAdapter } from '../StorageAdapter';
|
||||
import type { SchemaType, QueryType, QueryOptions } from '../StorageAdapter';
|
||||
|
||||
const parseTypeToPostgresType = type => {
|
||||
const parseTypeToPostgresType = (type) => {
|
||||
switch (type.type) {
|
||||
case 'String':
|
||||
return 'text';
|
||||
@@ -79,7 +79,7 @@ const mongoAggregateToPostgres = {
|
||||
$year: 'YEAR',
|
||||
};
|
||||
|
||||
const toPostgresValue = value => {
|
||||
const toPostgresValue = (value) => {
|
||||
if (typeof value === 'object') {
|
||||
if (value.__type === 'Date') {
|
||||
return value.iso;
|
||||
@@ -91,7 +91,7 @@ const toPostgresValue = value => {
|
||||
return value;
|
||||
};
|
||||
|
||||
const transformValue = value => {
|
||||
const transformValue = (value) => {
|
||||
if (typeof value === 'object' && value.__type === 'Pointer') {
|
||||
return value.objectId;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ const defaultCLPS = Object.freeze({
|
||||
protectedFields: { '*': [] },
|
||||
});
|
||||
|
||||
const toParseSchema = schema => {
|
||||
const toParseSchema = (schema) => {
|
||||
if (schema.className === '_User') {
|
||||
delete schema.fields._hashed_password;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ const toParseSchema = schema => {
|
||||
};
|
||||
};
|
||||
|
||||
const toPostgresSchema = schema => {
|
||||
const toPostgresSchema = (schema) => {
|
||||
if (!schema) {
|
||||
return schema;
|
||||
}
|
||||
@@ -159,8 +159,8 @@ const toPostgresSchema = schema => {
|
||||
return schema;
|
||||
};
|
||||
|
||||
const handleDotFields = object => {
|
||||
Object.keys(object).forEach(fieldName => {
|
||||
const handleDotFields = (object) => {
|
||||
Object.keys(object).forEach((fieldName) => {
|
||||
if (fieldName.indexOf('.') > -1) {
|
||||
const components = fieldName.split('.');
|
||||
const first = components.shift();
|
||||
@@ -186,7 +186,7 @@ const handleDotFields = object => {
|
||||
return object;
|
||||
};
|
||||
|
||||
const transformDotFieldToComponents = fieldName => {
|
||||
const transformDotFieldToComponents = (fieldName) => {
|
||||
return fieldName.split('.').map((cmpt, index) => {
|
||||
if (index === 0) {
|
||||
return `"${cmpt}"`;
|
||||
@@ -195,7 +195,7 @@ const transformDotFieldToComponents = fieldName => {
|
||||
});
|
||||
};
|
||||
|
||||
const transformDotField = fieldName => {
|
||||
const transformDotField = (fieldName) => {
|
||||
if (fieldName.indexOf('.') === -1) {
|
||||
return `"${fieldName}"`;
|
||||
}
|
||||
@@ -205,7 +205,7 @@ const transformDotField = fieldName => {
|
||||
return name;
|
||||
};
|
||||
|
||||
const transformAggregateField = fieldName => {
|
||||
const transformAggregateField = (fieldName) => {
|
||||
if (typeof fieldName !== 'string') {
|
||||
return fieldName;
|
||||
}
|
||||
@@ -218,7 +218,7 @@ const transformAggregateField = fieldName => {
|
||||
return fieldName.substr(1);
|
||||
};
|
||||
|
||||
const validateKeys = object => {
|
||||
const validateKeys = (object) => {
|
||||
if (typeof object == 'object') {
|
||||
for (const key in object) {
|
||||
if (typeof object[key] == 'object') {
|
||||
@@ -236,10 +236,10 @@ const validateKeys = object => {
|
||||
};
|
||||
|
||||
// Returns the list of join tables on a schema
|
||||
const joinTablesForSchema = schema => {
|
||||
const joinTablesForSchema = (schema) => {
|
||||
const list = [];
|
||||
if (schema) {
|
||||
Object.keys(schema.fields).forEach(field => {
|
||||
Object.keys(schema.fields).forEach((field) => {
|
||||
if (schema.fields[field].type === 'Relation') {
|
||||
list.push(`_Join:${field}:${schema.className}`);
|
||||
}
|
||||
@@ -343,7 +343,7 @@ const buildWhereClause = ({
|
||||
} else if (['$or', '$nor', '$and'].includes(fieldName)) {
|
||||
const clauses = [];
|
||||
const clauseValues = [];
|
||||
fieldValue.forEach(subQuery => {
|
||||
fieldValue.forEach((subQuery) => {
|
||||
const clause = buildWhereClause({
|
||||
schema,
|
||||
query: subQuery,
|
||||
@@ -378,8 +378,9 @@ const buildWhereClause = ({
|
||||
// if not null, we need to manually exclude null
|
||||
if (fieldValue.$ne.__type === 'GeoPoint') {
|
||||
patterns.push(
|
||||
`($${index}:name <> POINT($${index + 1}, $${index +
|
||||
2}) OR $${index}:name IS NULL)`
|
||||
`($${index}:name <> POINT($${index + 1}, $${
|
||||
index + 2
|
||||
}) OR $${index}:name IS NULL)`
|
||||
);
|
||||
} else {
|
||||
if (fieldName.indexOf('.') >= 0) {
|
||||
@@ -489,13 +490,13 @@ const buildWhereClause = ({
|
||||
};
|
||||
if (fieldValue.$in) {
|
||||
createConstraint(
|
||||
_.flatMap(fieldValue.$in, elt => elt),
|
||||
_.flatMap(fieldValue.$in, (elt) => elt),
|
||||
false
|
||||
);
|
||||
}
|
||||
if (fieldValue.$nin) {
|
||||
createConstraint(
|
||||
_.flatMap(fieldValue.$nin, elt => elt),
|
||||
_.flatMap(fieldValue.$nin, (elt) => elt),
|
||||
true
|
||||
);
|
||||
}
|
||||
@@ -609,8 +610,9 @@ const buildWhereClause = ({
|
||||
);
|
||||
}
|
||||
patterns.push(
|
||||
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${index +
|
||||
2}, $${index + 3})`
|
||||
`to_tsvector($${index}, $${index + 1}:name) @@ to_tsquery($${
|
||||
index + 2
|
||||
}, $${index + 3})`
|
||||
);
|
||||
values.push(language, fieldName, language, search.$term);
|
||||
index += 4;
|
||||
@@ -621,12 +623,14 @@ const buildWhereClause = ({
|
||||
const distance = fieldValue.$maxDistance;
|
||||
const distanceInKM = distance * 6371 * 1000;
|
||||
patterns.push(
|
||||
`ST_DistanceSphere($${index}:name::geometry, POINT($${index +
|
||||
1}, $${index + 2})::geometry) <= $${index + 3}`
|
||||
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${
|
||||
index + 2
|
||||
})::geometry) <= $${index + 3}`
|
||||
);
|
||||
sorts.push(
|
||||
`ST_DistanceSphere($${index}:name::geometry, POINT($${index +
|
||||
1}, $${index + 2})::geometry) ASC`
|
||||
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${
|
||||
index + 2
|
||||
})::geometry) ASC`
|
||||
);
|
||||
values.push(fieldName, point.longitude, point.latitude, distanceInKM);
|
||||
index += 4;
|
||||
@@ -673,8 +677,9 @@ const buildWhereClause = ({
|
||||
}
|
||||
const distanceInKM = distance * 6371 * 1000;
|
||||
patterns.push(
|
||||
`ST_DistanceSphere($${index}:name::geometry, POINT($${index +
|
||||
1}, $${index + 2})::geometry) <= $${index + 3}`
|
||||
`ST_DistanceSphere($${index}:name::geometry, POINT($${index + 1}, $${
|
||||
index + 2
|
||||
})::geometry) <= $${index + 3}`
|
||||
);
|
||||
values.push(fieldName, point.longitude, point.latitude, distanceInKM);
|
||||
index += 4;
|
||||
@@ -706,7 +711,7 @@ const buildWhereClause = ({
|
||||
);
|
||||
}
|
||||
points = points
|
||||
.map(point => {
|
||||
.map((point) => {
|
||||
if (point instanceof Array && point.length === 2) {
|
||||
Parse.GeoPoint._validate(point[1], point[0]);
|
||||
return `(${point[0]}, ${point[1]})`;
|
||||
@@ -794,7 +799,7 @@ const buildWhereClause = ({
|
||||
index += 2;
|
||||
}
|
||||
|
||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||
Object.keys(ParseToPosgresComparator).forEach((cmp) => {
|
||||
if (fieldValue[cmp] || fieldValue[cmp] === 0) {
|
||||
const pgComparator = ParseToPosgresComparator[cmp];
|
||||
const postgresValue = toPostgresValue(fieldValue[cmp]);
|
||||
@@ -853,10 +858,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
//Note that analyze=true will run the query, executing INSERTS, DELETES, etc.
|
||||
createExplainableQuery (query: string, analyze:boolean = false) {
|
||||
if (analyze){
|
||||
createExplainableQuery(query: string, analyze: boolean = false) {
|
||||
if (analyze) {
|
||||
return 'EXPLAIN (ANALYZE, FORMAT JSON) ' + query;
|
||||
}else{
|
||||
} else {
|
||||
return 'EXPLAIN (FORMAT JSON) ' + query;
|
||||
}
|
||||
}
|
||||
@@ -874,7 +879,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
.none(
|
||||
'CREATE TABLE IF NOT EXISTS "_SCHEMA" ( "className" varChar(120), "schema" jsonb, "isParseClass" bool, PRIMARY KEY ("className") )'
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (
|
||||
error.code === PostgresDuplicateRelationError ||
|
||||
error.code === PostgresUniqueIndexViolationError ||
|
||||
@@ -891,13 +896,13 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
return this._client.one(
|
||||
'SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)',
|
||||
[name],
|
||||
a => a.exists
|
||||
(a) => a.exists
|
||||
);
|
||||
}
|
||||
|
||||
async setClassLevelPermissions(className: string, CLPs: any) {
|
||||
const self = this;
|
||||
await this._client.task('set-class-level-permissions', async t => {
|
||||
await this._client.task('set-class-level-permissions', async (t) => {
|
||||
await self._ensureSchemaCollectionExists(t);
|
||||
const values = [
|
||||
className,
|
||||
@@ -929,7 +934,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
const deletedIndexes = [];
|
||||
const insertedIndexes = [];
|
||||
Object.keys(submittedIndexes).forEach(name => {
|
||||
Object.keys(submittedIndexes).forEach((name) => {
|
||||
const field = submittedIndexes[name];
|
||||
if (existingIndexes[name] && field.__op !== 'Delete') {
|
||||
throw new Parse.Error(
|
||||
@@ -947,7 +952,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
deletedIndexes.push(name);
|
||||
delete existingIndexes[name];
|
||||
} else {
|
||||
Object.keys(field).forEach(key => {
|
||||
Object.keys(field).forEach((key) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
@@ -962,7 +967,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
}
|
||||
});
|
||||
await conn.tx('set-indexes-with-schema-format', async t => {
|
||||
await conn.tx('set-indexes-with-schema-format', async (t) => {
|
||||
if (insertedIndexes.length > 0) {
|
||||
await self.createIndexes(className, insertedIndexes, t);
|
||||
}
|
||||
@@ -980,7 +985,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
async createClass(className: string, schema: SchemaType, conn: ?any) {
|
||||
conn = conn || this._client;
|
||||
return conn
|
||||
.tx('create-class', async t => {
|
||||
.tx('create-class', async (t) => {
|
||||
const q1 = this.createTable(className, schema, t);
|
||||
const q2 = t.none(
|
||||
'INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)',
|
||||
@@ -1000,7 +1005,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
.then(() => {
|
||||
return toParseSchema(schema);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
if (err.data[0].result.code === PostgresTransactionAbortedError) {
|
||||
err = err.data[1].result;
|
||||
}
|
||||
@@ -1037,7 +1042,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
let index = 2;
|
||||
const relations = [];
|
||||
Object.keys(fields).forEach(fieldName => {
|
||||
Object.keys(fields).forEach((fieldName) => {
|
||||
const parseType = fields[fieldName];
|
||||
// Skip when it's a relation
|
||||
// We'll create the tables later
|
||||
@@ -1060,7 +1065,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const values = [className, ...valuesArray];
|
||||
|
||||
debug(qs, values);
|
||||
return conn.task('create-table', async t => {
|
||||
return conn.task('create-table', async (t) => {
|
||||
try {
|
||||
await self._ensureSchemaCollectionExists(t);
|
||||
await t.none(qs, values);
|
||||
@@ -1070,9 +1075,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
// ELSE: Table already exists, must have been created by a different request. Ignore the error.
|
||||
}
|
||||
await t.tx('create-table-tx', tx => {
|
||||
await t.tx('create-table-tx', (tx) => {
|
||||
return tx.batch(
|
||||
relations.map(fieldName => {
|
||||
relations.map((fieldName) => {
|
||||
return tx.none(
|
||||
'CREATE TABLE IF NOT EXISTS $<joinTable:name> ("relatedId" varChar(120), "owningId" varChar(120), PRIMARY KEY("relatedId", "owningId") )',
|
||||
{ joinTable: `_Join:${fieldName}:${className}` }
|
||||
@@ -1088,15 +1093,15 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
conn = conn || this._client;
|
||||
const self = this;
|
||||
|
||||
await conn.tx('schema-upgrade', async t => {
|
||||
await conn.tx('schema-upgrade', async (t) => {
|
||||
const columns = await t.map(
|
||||
'SELECT column_name FROM information_schema.columns WHERE table_name = $<className>',
|
||||
{ className },
|
||||
a => a.column_name
|
||||
(a) => a.column_name
|
||||
);
|
||||
const newColumns = Object.keys(schema.fields)
|
||||
.filter(item => columns.indexOf(item) === -1)
|
||||
.map(fieldName =>
|
||||
.filter((item) => columns.indexOf(item) === -1)
|
||||
.map((fieldName) =>
|
||||
self.addFieldIfNotExists(
|
||||
className,
|
||||
fieldName,
|
||||
@@ -1119,7 +1124,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
debug('addFieldIfNotExists', { className, fieldName, type });
|
||||
conn = conn || this._client;
|
||||
const self = this;
|
||||
await conn.tx('add-field-if-not-exists', async t => {
|
||||
await conn.tx('add-field-if-not-exists', async (t) => {
|
||||
if (type.type !== 'Relation') {
|
||||
try {
|
||||
await t.none(
|
||||
@@ -1178,7 +1183,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
},
|
||||
];
|
||||
return this._client
|
||||
.tx(t => t.none(this._pgp.helpers.concat(operations)))
|
||||
.tx((t) => t.none(this._pgp.helpers.concat(operations)))
|
||||
.then(() => className.indexOf('_Join:') != 0); // resolves with false when _Join table
|
||||
}
|
||||
|
||||
@@ -1189,7 +1194,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
debug('deleteAllClasses');
|
||||
|
||||
await this._client
|
||||
.task('delete-all-classes', async t => {
|
||||
.task('delete-all-classes', async (t) => {
|
||||
try {
|
||||
const results = await t.any('SELECT * FROM "_SCHEMA"');
|
||||
const joins = results.reduce((list: Array<string>, schema: any) => {
|
||||
@@ -1204,14 +1209,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
'_GlobalConfig',
|
||||
'_GraphQLConfig',
|
||||
'_Audience',
|
||||
...results.map(result => result.className),
|
||||
...results.map((result) => result.className),
|
||||
...joins,
|
||||
];
|
||||
const queries = classes.map(className => ({
|
||||
const queries = classes.map((className) => ({
|
||||
query: 'DROP TABLE IF EXISTS $<className:name>',
|
||||
values: { className },
|
||||
}));
|
||||
await t.tx(tx => tx.none(helpers.concat(queries)));
|
||||
await t.tx((tx) => tx.none(helpers.concat(queries)));
|
||||
} catch (error) {
|
||||
if (error.code !== PostgresRelationDoesNotExistError) {
|
||||
throw error;
|
||||
@@ -1259,7 +1264,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
})
|
||||
.join(', DROP COLUMN');
|
||||
|
||||
await this._client.tx('delete-fields', async t => {
|
||||
await this._client.tx('delete-fields', async (t) => {
|
||||
await t.none(
|
||||
'UPDATE "_SCHEMA" SET "schema" = $<schema> WHERE "className" = $<className>',
|
||||
{ schema, className }
|
||||
@@ -1275,9 +1280,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
// rejection reason are TBD.
|
||||
async getAllClasses() {
|
||||
const self = this;
|
||||
return this._client.task('get-all-classes', async t => {
|
||||
return this._client.task('get-all-classes', async (t) => {
|
||||
await self._ensureSchemaCollectionExists(t);
|
||||
return await t.map('SELECT * FROM "_SCHEMA"', null, row =>
|
||||
return await t.map('SELECT * FROM "_SCHEMA"', null, (row) =>
|
||||
toParseSchema({ className: row.className, ...row.schema })
|
||||
);
|
||||
});
|
||||
@@ -1292,7 +1297,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
.any('SELECT * FROM "_SCHEMA" WHERE "className" = $<className>', {
|
||||
className,
|
||||
})
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
if (result.length !== 1) {
|
||||
throw undefined;
|
||||
}
|
||||
@@ -1318,7 +1323,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
|
||||
validateKeys(object);
|
||||
|
||||
Object.keys(object).forEach(fieldName => {
|
||||
Object.keys(object).forEach((fieldName) => {
|
||||
if (object[fieldName] === null) {
|
||||
return;
|
||||
}
|
||||
@@ -1420,7 +1425,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
return `$${index + 2 + columnsArray.length}${termination}`;
|
||||
});
|
||||
const geoPointsInjects = Object.keys(geoPoints).map(key => {
|
||||
const geoPointsInjects = Object.keys(geoPoints).map((key) => {
|
||||
const value = geoPoints[key];
|
||||
valuesArray.push(value.longitude, value.latitude);
|
||||
const l = valuesArray.length + columnsArray.length;
|
||||
@@ -1441,7 +1446,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
)
|
||||
.none(qs, values)
|
||||
.then(() => ({ ops: [object] }))
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error.code === PostgresUniqueIndexViolationError) {
|
||||
const err = new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
@@ -1492,8 +1497,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
? transactionalSession.t
|
||||
: this._client
|
||||
)
|
||||
.one(qs, values, a => +a.count)
|
||||
.then(count => {
|
||||
.one(qs, values, (a) => +a.count)
|
||||
.then((count) => {
|
||||
if (count === 0) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
@@ -1503,7 +1508,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
return count;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error.code !== PostgresRelationDoesNotExistError) {
|
||||
throw error;
|
||||
}
|
||||
@@ -1529,7 +1534,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
query,
|
||||
update,
|
||||
transactionalSession
|
||||
).then(val => val[0]);
|
||||
).then((val) => val[0]);
|
||||
}
|
||||
|
||||
// Apply the update to all objects that match the given Parse Query.
|
||||
@@ -1550,7 +1555,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
|
||||
// Set flag for dot notation fields
|
||||
const dotNotationOptions = {};
|
||||
Object.keys(update).forEach(fieldName => {
|
||||
Object.keys(update).forEach((fieldName) => {
|
||||
if (fieldName.indexOf('.') > -1) {
|
||||
const components = fieldName.split('.');
|
||||
const first = components.shift();
|
||||
@@ -1622,8 +1627,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
index += 2;
|
||||
} else if (fieldValue.__op === 'Add') {
|
||||
updatePatterns.push(
|
||||
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${index +
|
||||
1}::jsonb)`
|
||||
`$${index}:name = array_add(COALESCE($${index}:name, '[]'::jsonb), $${
|
||||
index + 1
|
||||
}::jsonb)`
|
||||
);
|
||||
values.push(fieldName, JSON.stringify(fieldValue.objects));
|
||||
index += 2;
|
||||
@@ -1633,15 +1639,17 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
index += 2;
|
||||
} else if (fieldValue.__op === 'Remove') {
|
||||
updatePatterns.push(
|
||||
`$${index}:name = array_remove(COALESCE($${index}:name, '[]'::jsonb), $${index +
|
||||
1}::jsonb)`
|
||||
`$${index}:name = array_remove(COALESCE($${index}:name, '[]'::jsonb), $${
|
||||
index + 1
|
||||
}::jsonb)`
|
||||
);
|
||||
values.push(fieldName, JSON.stringify(fieldValue.objects));
|
||||
index += 2;
|
||||
} else if (fieldValue.__op === 'AddUnique') {
|
||||
updatePatterns.push(
|
||||
`$${index}:name = array_add_unique(COALESCE($${index}:name, '[]'::jsonb), $${index +
|
||||
1}::jsonb)`
|
||||
`$${index}:name = array_add_unique(COALESCE($${index}:name, '[]'::jsonb), $${
|
||||
index + 1
|
||||
}::jsonb)`
|
||||
);
|
||||
values.push(fieldName, JSON.stringify(fieldValue.objects));
|
||||
index += 2;
|
||||
@@ -1698,7 +1706,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
) {
|
||||
// Gather keys to increment
|
||||
const keysToIncrement = Object.keys(originalUpdate)
|
||||
.filter(k => {
|
||||
.filter((k) => {
|
||||
// choose top level fields that have a delete operation set
|
||||
// Note that Object.keys is iterating over the **original** update object
|
||||
// and that some of the keys of the original update could be null or undefined:
|
||||
@@ -1711,26 +1719,26 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
k.split('.')[0] === fieldName
|
||||
);
|
||||
})
|
||||
.map(k => k.split('.')[1]);
|
||||
.map((k) => k.split('.')[1]);
|
||||
|
||||
let incrementPatterns = '';
|
||||
if (keysToIncrement.length > 0) {
|
||||
incrementPatterns =
|
||||
' || ' +
|
||||
keysToIncrement
|
||||
.map(c => {
|
||||
.map((c) => {
|
||||
const amount = fieldValue[c].amount;
|
||||
return `CONCAT('{"${c}":', COALESCE($${index}:name->>'${c}','0')::int + ${amount}, '}')::jsonb`;
|
||||
})
|
||||
.join(' || ');
|
||||
// Strip the keys
|
||||
keysToIncrement.forEach(key => {
|
||||
keysToIncrement.forEach((key) => {
|
||||
delete fieldValue[key];
|
||||
});
|
||||
}
|
||||
|
||||
const keysToDelete: Array<string> = Object.keys(originalUpdate)
|
||||
.filter(k => {
|
||||
.filter((k) => {
|
||||
// choose top level fields that have a delete operation set.
|
||||
const value = originalUpdate[k];
|
||||
return (
|
||||
@@ -1740,7 +1748,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
k.split('.')[0] === fieldName
|
||||
);
|
||||
})
|
||||
.map(k => k.split('.')[1]);
|
||||
.map((k) => k.split('.')[1]);
|
||||
|
||||
const deletePatterns = keysToDelete.reduce(
|
||||
(p: string, c: string, i: number) => {
|
||||
@@ -1756,9 +1764,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
updateObject = `COALESCE($${index}:name, '{}'::jsonb)`;
|
||||
}
|
||||
updatePatterns.push(
|
||||
`$${index}:name = (${updateObject} ${deletePatterns} ${incrementPatterns} || $${index +
|
||||
1 +
|
||||
keysToDelete.length}::jsonb )`
|
||||
`$${index}:name = (${updateObject} ${deletePatterns} ${incrementPatterns} || $${
|
||||
index + 1 + keysToDelete.length
|
||||
}::jsonb )`
|
||||
);
|
||||
values.push(fieldName, ...keysToDelete, JSON.stringify(fieldValue));
|
||||
index += 2 + keysToDelete.length;
|
||||
@@ -1825,7 +1833,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
schema,
|
||||
createValue,
|
||||
transactionalSession
|
||||
).catch(error => {
|
||||
).catch((error) => {
|
||||
// ignore duplicate value errors as it's upsert
|
||||
if (error.code !== Parse.Error.DUPLICATE_VALUE) {
|
||||
throw error;
|
||||
@@ -1880,7 +1888,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
if (sort) {
|
||||
const sortCopy: any = sort;
|
||||
const sorting = Object.keys(sort)
|
||||
.map(key => {
|
||||
.map((key) => {
|
||||
const transformKey = transformDotFieldToComponents(key).join('->');
|
||||
// Using $idx pattern gives: non-integer constant in ORDER BY
|
||||
if (sortCopy[key] === 1) {
|
||||
@@ -1923,31 +1931,33 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
const originalQuery = `SELECT ${columns} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern}`;
|
||||
const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
|
||||
const qs = explain
|
||||
? this.createExplainableQuery(originalQuery)
|
||||
: originalQuery;
|
||||
debug(qs, values);
|
||||
return this._client
|
||||
.any(qs, values)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
// Query on non existing table, don't crash
|
||||
if (error.code !== PostgresRelationDoesNotExistError) {
|
||||
throw error;
|
||||
}
|
||||
return [];
|
||||
})
|
||||
.then(results => {
|
||||
if (explain){
|
||||
.then((results) => {
|
||||
if (explain) {
|
||||
return results;
|
||||
}
|
||||
return results.map(object =>
|
||||
return results.map((object) =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
);}
|
||||
);
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Converts from a postgres-format object to a REST-format object.
|
||||
// Does not strip out anything based on a lack of authentication.
|
||||
postgresObjectToParseObject(className: string, object: any, schema: any) {
|
||||
Object.keys(schema.fields).forEach(fieldName => {
|
||||
Object.keys(schema.fields).forEach((fieldName) => {
|
||||
if (schema.fields[fieldName].type === 'Pointer' && object[fieldName]) {
|
||||
object[fieldName] = {
|
||||
objectId: object[fieldName],
|
||||
@@ -1971,7 +1981,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
if (object[fieldName] && schema.fields[fieldName].type === 'Polygon') {
|
||||
let coords = object[fieldName];
|
||||
coords = coords.substr(2, coords.length - 4).split('),(');
|
||||
coords = coords.map(point => {
|
||||
coords = coords.map((point) => {
|
||||
return [
|
||||
parseFloat(point.split(',')[1]),
|
||||
parseFloat(point.split(',')[0]),
|
||||
@@ -2061,7 +2071,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const qs = `ALTER TABLE $1:name ADD CONSTRAINT $2:name UNIQUE (${constraintPatterns.join()})`;
|
||||
return this._client
|
||||
.none(qs, [className, constraintName, ...fieldNames])
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (
|
||||
error.code === PostgresDuplicateRelationError &&
|
||||
error.message.includes(constraintName)
|
||||
@@ -2112,14 +2122,14 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
return this._client
|
||||
.one(qs, values, a => {
|
||||
.one(qs, values, (a) => {
|
||||
if (a.approximate_row_count != null) {
|
||||
return +a.approximate_row_count;
|
||||
} else {
|
||||
return +a.count;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error.code !== PostgresRelationDoesNotExistError) {
|
||||
throw error;
|
||||
}
|
||||
@@ -2168,16 +2178,16 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
debug(qs, values);
|
||||
return this._client
|
||||
.any(qs, values)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error.code === PostgresMissingColumnError) {
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.then(results => {
|
||||
.then((results) => {
|
||||
if (!isNested) {
|
||||
results = results.filter(object => object[field] !== null);
|
||||
return results.map(object => {
|
||||
results = results.filter((object) => object[field] !== null);
|
||||
return results.map((object) => {
|
||||
if (!isPointerField) {
|
||||
return object[field];
|
||||
}
|
||||
@@ -2189,10 +2199,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
}
|
||||
const child = fieldName.split('.')[1];
|
||||
return results.map(object => object[column][child]);
|
||||
return results.map((object) => object[column][child]);
|
||||
})
|
||||
.then(results =>
|
||||
results.map(object =>
|
||||
.then((results) =>
|
||||
results.map((object) =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
)
|
||||
);
|
||||
@@ -2204,7 +2214,8 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
pipeline: any,
|
||||
readPreference: ?string,
|
||||
hint: ?mixed,
|
||||
explain?: boolean) {
|
||||
explain?: boolean
|
||||
) {
|
||||
debug('aggregate', className, pipeline, readPreference, hint, explain);
|
||||
const values = [className];
|
||||
let index: number = 2;
|
||||
@@ -2257,8 +2268,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
columns.push(
|
||||
`EXTRACT(${
|
||||
mongoAggregateToPostgres[operation]
|
||||
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${index +
|
||||
1}:name`
|
||||
} FROM $${index}:name AT TIME ZONE 'UTC') AS $${
|
||||
index + 1
|
||||
}:name`
|
||||
);
|
||||
values.push(source, alias);
|
||||
index += 2;
|
||||
@@ -2327,7 +2339,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
|
||||
if (stage.$match.$or) {
|
||||
const collapse = {};
|
||||
stage.$match.$or.forEach(element => {
|
||||
stage.$match.$or.forEach((element) => {
|
||||
for (const key in element) {
|
||||
collapse[key] = element[key];
|
||||
}
|
||||
@@ -2337,7 +2349,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
for (const field in stage.$match) {
|
||||
const value = stage.$match[field];
|
||||
const matchPatterns = [];
|
||||
Object.keys(ParseToPosgresComparator).forEach(cmp => {
|
||||
Object.keys(ParseToPosgresComparator).forEach((cmp) => {
|
||||
if (value[cmp]) {
|
||||
const pgComparator = ParseToPosgresComparator[cmp];
|
||||
matchPatterns.push(
|
||||
@@ -2377,7 +2389,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
const sort = stage.$sort;
|
||||
const keys = Object.keys(sort);
|
||||
const sorting = keys
|
||||
.map(key => {
|
||||
.map((key) => {
|
||||
const transformer = sort[key] === 1 ? 'ASC' : 'DESC';
|
||||
const order = `$${index}:name ${transformer}`;
|
||||
index += 1;
|
||||
@@ -2389,43 +2401,54 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
sort !== undefined && sorting.length > 0 ? `ORDER BY ${sorting}` : '';
|
||||
}
|
||||
}
|
||||
const originalQuery = `SELECT ${columns.join()} FROM $1:name ${wherePattern} ${sortPattern} ${limitPattern} ${skipPattern} ${groupPattern}`;
|
||||
const qs = explain ? this.createExplainableQuery(originalQuery) : originalQuery;
|
||||
debug(qs, values);
|
||||
return this._client
|
||||
.any(qs, values)
|
||||
.then(a => {
|
||||
if (explain){
|
||||
return a;
|
||||
|
||||
if (groupPattern) {
|
||||
columns.forEach((e, i, a) => {
|
||||
if (e && e.trim() === '*') {
|
||||
a[i] = '';
|
||||
}
|
||||
const results = a.map(object =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
);
|
||||
results.forEach(result => {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) {
|
||||
result.objectId = null;
|
||||
}
|
||||
if (groupValues) {
|
||||
result.objectId = {};
|
||||
for (const key in groupValues) {
|
||||
result.objectId[key] = result[key];
|
||||
delete result[key];
|
||||
}
|
||||
}
|
||||
if (countField) {
|
||||
result[countField] = parseInt(result[countField], 10);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
const originalQuery = `SELECT ${columns
|
||||
.filter(Boolean)
|
||||
.join()} FROM $1:name ${wherePattern} ${skipPattern} ${groupPattern} ${sortPattern} ${limitPattern}`;
|
||||
const qs = explain
|
||||
? this.createExplainableQuery(originalQuery)
|
||||
: originalQuery;
|
||||
debug(qs, values);
|
||||
return this._client.any(qs, values).then((a) => {
|
||||
if (explain) {
|
||||
return a;
|
||||
}
|
||||
const results = a.map((object) =>
|
||||
this.postgresObjectToParseObject(className, object, schema)
|
||||
);
|
||||
results.forEach((result) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(result, 'objectId')) {
|
||||
result.objectId = null;
|
||||
}
|
||||
if (groupValues) {
|
||||
result.objectId = {};
|
||||
for (const key in groupValues) {
|
||||
result.objectId[key] = result[key];
|
||||
delete result[key];
|
||||
}
|
||||
}
|
||||
if (countField) {
|
||||
result[countField] = parseInt(result[countField], 10);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
async performInitialization({ VolatileClassesSchemas }: any) {
|
||||
// TODO: This method needs to be rewritten to make proper use of connections (@vitaly-t)
|
||||
debug('performInitialization');
|
||||
const promises = VolatileClassesSchemas.map(schema => {
|
||||
const promises = VolatileClassesSchemas.map((schema) => {
|
||||
return this.createTable(schema.className, schema)
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
if (
|
||||
err.code === PostgresDuplicateRelationError ||
|
||||
err.code === Parse.Error.INVALID_CLASS_NAME
|
||||
@@ -2438,7 +2461,7 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
});
|
||||
return Promise.all(promises)
|
||||
.then(() => {
|
||||
return this._client.tx('perform-initialization', t => {
|
||||
return this._client.tx('perform-initialization', (t) => {
|
||||
return t.batch([
|
||||
t.none(sql.misc.jsonObjectSetKeys),
|
||||
t.none(sql.array.add),
|
||||
@@ -2450,10 +2473,10 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
]);
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
debug(`initializationDone in ${data.duration}`);
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
/* eslint-disable no-console */
|
||||
console.error(error);
|
||||
});
|
||||
@@ -2464,9 +2487,9 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
indexes: any,
|
||||
conn: ?any
|
||||
): Promise<void> {
|
||||
return (conn || this._client).tx(t =>
|
||||
return (conn || this._client).tx((t) =>
|
||||
t.batch(
|
||||
indexes.map(i => {
|
||||
indexes.map((i) => {
|
||||
return t.none('CREATE INDEX $1:name ON $2:name ($3:name)', [
|
||||
i.name,
|
||||
className,
|
||||
@@ -2493,11 +2516,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
async dropIndexes(className: string, indexes: any, conn: any): Promise<void> {
|
||||
const queries = indexes.map(i => ({
|
||||
const queries = indexes.map((i) => ({
|
||||
query: 'DROP INDEX $1:name',
|
||||
values: i,
|
||||
}));
|
||||
await (conn || this._client).tx(t =>
|
||||
await (conn || this._client).tx((t) =>
|
||||
t.none(this._pgp.helpers.concat(queries))
|
||||
);
|
||||
}
|
||||
@@ -2517,11 +2540,11 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
}
|
||||
|
||||
async createTransactionalSession(): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
const transactionalSession = {};
|
||||
transactionalSession.result = this._client.tx(t => {
|
||||
transactionalSession.result = this._client.tx((t) => {
|
||||
transactionalSession.t = t;
|
||||
transactionalSession.promise = new Promise(resolve => {
|
||||
transactionalSession.promise = new Promise((resolve) => {
|
||||
transactionalSession.resolve = resolve;
|
||||
});
|
||||
transactionalSession.batch = [];
|
||||
@@ -2555,15 +2578,19 @@ export class PostgresStorageAdapter implements StorageAdapter {
|
||||
caseInsensitive: boolean = false,
|
||||
conn: ?any = null
|
||||
): Promise<any> {
|
||||
|
||||
conn = conn != null ? conn : this._client;
|
||||
const defaultIndexName = `parse_default_${fieldNames.sort().join('_')}`;
|
||||
const indexNameOptions: Object = indexName != null ? { name: indexName } : { name: defaultIndexName };
|
||||
const constraintPatterns = caseInsensitive ? fieldNames.map((fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`) :
|
||||
fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
||||
const indexNameOptions: Object =
|
||||
indexName != null ? { name: indexName } : { name: defaultIndexName };
|
||||
const constraintPatterns = caseInsensitive
|
||||
? fieldNames.map(
|
||||
(fieldName, index) => `lower($${index + 3}:name) varchar_pattern_ops`
|
||||
)
|
||||
: fieldNames.map((fieldName, index) => `$${index + 3}:name`);
|
||||
const qs = `CREATE INDEX $1:name ON $2:name (${constraintPatterns.join()})`;
|
||||
await conn.none(qs, [indexNameOptions.name, className, ...fieldNames])
|
||||
.catch(error => {
|
||||
await conn
|
||||
.none(qs, [indexNameOptions.name, className, ...fieldNames])
|
||||
.catch((error) => {
|
||||
if (
|
||||
error.code === PostgresDuplicateRelationError &&
|
||||
error.message.includes(indexNameOptions.name)
|
||||
@@ -2616,7 +2643,7 @@ function convertPolygonToSQL(polygon) {
|
||||
);
|
||||
}
|
||||
const points = polygon
|
||||
.map(point => {
|
||||
.map((point) => {
|
||||
Parse.GeoPoint._validate(parseFloat(point[1]), parseFloat(point[0]));
|
||||
return `(${point[1]}, ${point[0]})`;
|
||||
})
|
||||
@@ -2685,7 +2712,7 @@ function isAllValuesRegexOrNone(values) {
|
||||
}
|
||||
|
||||
function isAnyValueRegexStartsWith(values) {
|
||||
return values.some(function(value) {
|
||||
return values.some(function (value) {
|
||||
return isStartsWithRegex(value.$regex);
|
||||
});
|
||||
}
|
||||
@@ -2693,7 +2720,7 @@ function isAnyValueRegexStartsWith(values) {
|
||||
function createLiteralRegex(remaining) {
|
||||
return remaining
|
||||
.split('')
|
||||
.map(c => {
|
||||
.map((c) => {
|
||||
const regex = RegExp('[0-9 ]|\\p{L}', 'u'); // Support all unicode letter chars
|
||||
if (c.match(regex) !== null) {
|
||||
// don't escape alphanumeric characters
|
||||
|
||||
@@ -65,7 +65,7 @@ const specialQuerykeys = [
|
||||
'_failed_login_count',
|
||||
];
|
||||
|
||||
const isSpecialQueryKey = key => {
|
||||
const isSpecialQueryKey = (key) => {
|
||||
return specialQuerykeys.indexOf(key) >= 0;
|
||||
};
|
||||
|
||||
@@ -107,7 +107,7 @@ const validateQuery = (query: any): void => {
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(query).forEach(key => {
|
||||
Object.keys(query).forEach((key) => {
|
||||
if (query && query[key] && query[key].$regex) {
|
||||
if (typeof query[key].$options === 'string') {
|
||||
if (!query[key].$options.match(/^[imxs]+$/)) {
|
||||
@@ -149,8 +149,8 @@ const filterSensitiveData = (
|
||||
if (isReadOperation && perms.protectedFields) {
|
||||
// extract protectedFields added with the pointer-permission prefix
|
||||
const protectedFieldsPointerPerm = Object.keys(perms.protectedFields)
|
||||
.filter(key => key.startsWith('userField:'))
|
||||
.map(key => {
|
||||
.filter((key) => key.startsWith('userField:'))
|
||||
.map((key) => {
|
||||
return { key: key.substring(10), value: perms.protectedFields[key] };
|
||||
});
|
||||
|
||||
@@ -158,13 +158,13 @@ const filterSensitiveData = (
|
||||
let overrideProtectedFields = false;
|
||||
|
||||
// check if the object grants the current user access based on the extracted fields
|
||||
protectedFieldsPointerPerm.forEach(pointerPerm => {
|
||||
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
|
||||
(user) => user.objectId && user.objectId === userId
|
||||
);
|
||||
} else {
|
||||
pointerPermIncludesUser =
|
||||
@@ -186,14 +186,14 @@ const filterSensitiveData = (
|
||||
newProtectedFields.push(protectedFields);
|
||||
}
|
||||
// intersect all sets of protectedFields
|
||||
newProtectedFields.forEach(fields => {
|
||||
newProtectedFields.forEach((fields) => {
|
||||
if (fields) {
|
||||
// if there're no protctedFields by other criteria ( id / role / auth)
|
||||
// then we must intersect each set (per userField)
|
||||
if (!protectedFields) {
|
||||
protectedFields = fields;
|
||||
} else {
|
||||
protectedFields = protectedFields.filter(v => fields.includes(v));
|
||||
protectedFields = protectedFields.filter((v) => fields.includes(v));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -205,13 +205,13 @@ const filterSensitiveData = (
|
||||
/* special treat for the user class: don't filter protectedFields if currently loggedin user is
|
||||
the retrieved user */
|
||||
if (!(isUserClass && userId && object.objectId === userId)) {
|
||||
protectedFields && protectedFields.forEach(k => delete object[k]);
|
||||
protectedFields && protectedFields.forEach((k) => delete object[k]);
|
||||
|
||||
// fields not requested by client (excluded),
|
||||
//but were needed to apply protecttedFields
|
||||
perms.protectedFields &&
|
||||
perms.protectedFields.temporaryKeys &&
|
||||
perms.protectedFields.temporaryKeys.forEach(k => delete object[k]);
|
||||
perms.protectedFields.temporaryKeys.forEach((k) => delete object[k]);
|
||||
}
|
||||
|
||||
if (!isUserClass) {
|
||||
@@ -265,7 +265,7 @@ const specialKeysForUpdate = [
|
||||
'_password_history',
|
||||
];
|
||||
|
||||
const isSpecialUpdateKey = key => {
|
||||
const isSpecialUpdateKey = (key) => {
|
||||
return specialKeysForUpdate.indexOf(key) >= 0;
|
||||
};
|
||||
|
||||
@@ -291,7 +291,7 @@ function sanitizeDatabaseResult(originalObject, result): Promise<any> {
|
||||
if (!result) {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
Object.keys(originalObject).forEach(key => {
|
||||
Object.keys(originalObject).forEach((key) => {
|
||||
const keyUpdate = originalObject[key];
|
||||
// determine if that was an op
|
||||
if (
|
||||
@@ -312,7 +312,7 @@ function joinTableName(className, key) {
|
||||
return `_Join:${key}:${className}`;
|
||||
}
|
||||
|
||||
const flattenUpdateOperatorsForCreate = object => {
|
||||
const flattenUpdateOperatorsForCreate = (object) => {
|
||||
for (const key in object) {
|
||||
if (object[key] && object[key].__op) {
|
||||
switch (object[key].__op) {
|
||||
@@ -367,7 +367,7 @@ const flattenUpdateOperatorsForCreate = object => {
|
||||
|
||||
const transformAuthData = (className, object, schema) => {
|
||||
if (object.authData && className === '_User') {
|
||||
Object.keys(object.authData).forEach(provider => {
|
||||
Object.keys(object.authData).forEach((provider) => {
|
||||
const providerData = object.authData[provider];
|
||||
const fieldName = `_auth_data_${provider}`;
|
||||
if (providerData == null) {
|
||||
@@ -387,7 +387,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => {
|
||||
if (_rperm || _wperm) {
|
||||
output.ACL = {};
|
||||
|
||||
(_rperm || []).forEach(entry => {
|
||||
(_rperm || []).forEach((entry) => {
|
||||
if (!output.ACL[entry]) {
|
||||
output.ACL[entry] = { read: true };
|
||||
} else {
|
||||
@@ -395,7 +395,7 @@ const untransformObjectACL = ({ _rperm, _wperm, ...output }) => {
|
||||
}
|
||||
});
|
||||
|
||||
(_wperm || []).forEach(entry => {
|
||||
(_wperm || []).forEach((entry) => {
|
||||
if (!output.ACL[entry]) {
|
||||
output.ACL[entry] = { write: true };
|
||||
} else {
|
||||
@@ -442,8 +442,10 @@ class DatabaseController {
|
||||
|
||||
purgeCollection(className: string): Promise<void> {
|
||||
return this.loadSchema()
|
||||
.then(schemaController => schemaController.getOneSchema(className))
|
||||
.then(schema => this.adapter.deleteObjectsByQuery(className, schema, {}));
|
||||
.then((schemaController) => schemaController.getOneSchema(className))
|
||||
.then((schema) =>
|
||||
this.adapter.deleteObjectsByQuery(className, schema, {})
|
||||
);
|
||||
}
|
||||
|
||||
validateClassName(className: string): Promise<void> {
|
||||
@@ -490,7 +492,7 @@ class DatabaseController {
|
||||
// classname through the key.
|
||||
// TODO: make this not in the DatabaseController interface
|
||||
redirectClassNameForKey(className: string, key: string): Promise<?string> {
|
||||
return this.loadSchema().then(schema => {
|
||||
return this.loadSchema().then((schema) => {
|
||||
var t = schema.getExpectedType(className, key);
|
||||
if (t != null && typeof t !== 'string' && t.type === 'Relation') {
|
||||
return t.targetClass;
|
||||
@@ -514,7 +516,7 @@ class DatabaseController {
|
||||
const isMaster = acl === undefined;
|
||||
var aclGroup: string[] = acl || [];
|
||||
return this.loadSchema()
|
||||
.then(s => {
|
||||
.then((s) => {
|
||||
schema = s;
|
||||
if (isMaster) {
|
||||
return Promise.resolve();
|
||||
@@ -550,7 +552,7 @@ class DatabaseController {
|
||||
var aclGroup = acl || [];
|
||||
|
||||
return this.loadSchemaIfNeeded(validSchemaController).then(
|
||||
schemaController => {
|
||||
(schemaController) => {
|
||||
return (isMaster
|
||||
? Promise.resolve()
|
||||
: schemaController.validatePermission(className, aclGroup, 'update')
|
||||
@@ -594,7 +596,7 @@ class DatabaseController {
|
||||
validateQuery(query);
|
||||
return schemaController
|
||||
.getOneSchema(className, true)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
// If the schema doesn't exist, pretend it exists with no fields. This behavior
|
||||
// will likely need revisiting.
|
||||
if (error === undefined) {
|
||||
@@ -602,8 +604,8 @@ class DatabaseController {
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.then(schema => {
|
||||
Object.keys(update).forEach(fieldName => {
|
||||
.then((schema) => {
|
||||
Object.keys(update).forEach((fieldName) => {
|
||||
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_KEY_NAME,
|
||||
@@ -626,7 +628,7 @@ class DatabaseController {
|
||||
update[updateOperation] &&
|
||||
typeof update[updateOperation] === 'object' &&
|
||||
Object.keys(update[updateOperation]).some(
|
||||
innerKey =>
|
||||
(innerKey) =>
|
||||
innerKey.includes('$') || innerKey.includes('.')
|
||||
)
|
||||
) {
|
||||
@@ -641,7 +643,7 @@ class DatabaseController {
|
||||
if (validateOnly) {
|
||||
return this.adapter
|
||||
.find(className, schema, query, {})
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
if (!result || !result.length) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
@@ -697,7 +699,7 @@ class DatabaseController {
|
||||
return result;
|
||||
});
|
||||
})
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
if (skipSanitization) {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
@@ -820,7 +822,7 @@ class DatabaseController {
|
||||
doc,
|
||||
this._transactionalSession
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
// We don't care if they try to delete a non-existent relation.
|
||||
if (error.code == Parse.Error.OBJECT_NOT_FOUND) {
|
||||
return;
|
||||
@@ -846,7 +848,7 @@ class DatabaseController {
|
||||
const aclGroup = acl || [];
|
||||
|
||||
return this.loadSchemaIfNeeded(validSchemaController).then(
|
||||
schemaController => {
|
||||
(schemaController) => {
|
||||
return (isMaster
|
||||
? Promise.resolve()
|
||||
: schemaController.validatePermission(className, aclGroup, 'delete')
|
||||
@@ -873,7 +875,7 @@ class DatabaseController {
|
||||
validateQuery(query);
|
||||
return schemaController
|
||||
.getOneSchema(className)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
// If the schema doesn't exist, pretend it exists with no fields. This behavior
|
||||
// will likely need revisiting.
|
||||
if (error === undefined) {
|
||||
@@ -881,7 +883,7 @@ class DatabaseController {
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.then(parseFormatSchema =>
|
||||
.then((parseFormatSchema) =>
|
||||
this.adapter.deleteObjectsByQuery(
|
||||
className,
|
||||
parseFormatSchema,
|
||||
@@ -889,7 +891,7 @@ class DatabaseController {
|
||||
this._transactionalSession
|
||||
)
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
// When deleting sessions while changing passwords, don't throw an error if they don't have any sessions.
|
||||
if (
|
||||
className === '_Session' &&
|
||||
@@ -930,14 +932,14 @@ class DatabaseController {
|
||||
|
||||
return this.validateClassName(className)
|
||||
.then(() => this.loadSchemaIfNeeded(validSchemaController))
|
||||
.then(schemaController => {
|
||||
.then((schemaController) => {
|
||||
return (isMaster
|
||||
? Promise.resolve()
|
||||
: schemaController.validatePermission(className, aclGroup, 'create')
|
||||
)
|
||||
.then(() => schemaController.enforceClassExists(className))
|
||||
.then(() => schemaController.getOneSchema(className, true))
|
||||
.then(schema => {
|
||||
.then((schema) => {
|
||||
transformAuthData(className, object, schema);
|
||||
flattenUpdateOperatorsForCreate(object);
|
||||
if (validateOnly) {
|
||||
@@ -950,7 +952,7 @@ class DatabaseController {
|
||||
this._transactionalSession
|
||||
);
|
||||
})
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
if (validateOnly) {
|
||||
return originalObject;
|
||||
}
|
||||
@@ -979,7 +981,7 @@ class DatabaseController {
|
||||
}
|
||||
const fields = Object.keys(object);
|
||||
const schemaFields = Object.keys(classSchema.fields);
|
||||
const newKeys = fields.filter(field => {
|
||||
const newKeys = fields.filter((field) => {
|
||||
// Skip fields that are unset
|
||||
if (
|
||||
object[field] &&
|
||||
@@ -1038,7 +1040,7 @@ class DatabaseController {
|
||||
{ owningId },
|
||||
findOptions
|
||||
)
|
||||
.then(results => results.map(result => result.relatedId));
|
||||
.then((results) => results.map((result) => result.relatedId));
|
||||
}
|
||||
|
||||
// Returns a promise for a list of owning ids given some related ids.
|
||||
@@ -1053,9 +1055,9 @@ class DatabaseController {
|
||||
joinTableName(className, key),
|
||||
relationSchema,
|
||||
{ relatedId: { $in: relatedIds } },
|
||||
{}
|
||||
{ keys: ['owningId'] }
|
||||
)
|
||||
.then(results => results.map(result => result.owningId));
|
||||
.then((results) => results.map((result) => result.owningId));
|
||||
}
|
||||
|
||||
// Modifies query so that it no longer has $in on relation fields, or
|
||||
@@ -1069,7 +1071,7 @@ class DatabaseController {
|
||||
return Promise.all(
|
||||
ors.map((aQuery, index) => {
|
||||
return this.reduceInRelation(className, aQuery, schema).then(
|
||||
aQuery => {
|
||||
(aQuery) => {
|
||||
query['$or'][index] = aQuery;
|
||||
}
|
||||
);
|
||||
@@ -1079,7 +1081,7 @@ class DatabaseController {
|
||||
});
|
||||
}
|
||||
|
||||
const promises = Object.keys(query).map(key => {
|
||||
const promises = Object.keys(query).map((key) => {
|
||||
const t = schema.getExpectedType(className, key);
|
||||
if (!t || t.type !== 'Relation') {
|
||||
return Promise.resolve(query);
|
||||
@@ -1093,16 +1095,16 @@ class DatabaseController {
|
||||
query[key].__type == 'Pointer')
|
||||
) {
|
||||
// Build the list of queries
|
||||
queries = Object.keys(query[key]).map(constraintKey => {
|
||||
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);
|
||||
relatedIds = query[key]['$in'].map((r) => r.objectId);
|
||||
} else if (constraintKey == '$nin') {
|
||||
isNegation = true;
|
||||
relatedIds = query[key]['$nin'].map(r => r.objectId);
|
||||
relatedIds = query[key]['$nin'].map((r) => r.objectId);
|
||||
} else if (constraintKey == '$ne') {
|
||||
isNegation = true;
|
||||
relatedIds = [query[key]['$ne'].objectId];
|
||||
@@ -1122,11 +1124,11 @@ class DatabaseController {
|
||||
delete query[key];
|
||||
// execute each query independently to build the list of
|
||||
// $in / $nin
|
||||
const promises = queries.map(q => {
|
||||
const promises = queries.map((q) => {
|
||||
if (!q) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.owningIds(className, key, q.relatedIds).then(ids => {
|
||||
return this.owningIds(className, key, q.relatedIds).then((ids) => {
|
||||
if (q.isNegation) {
|
||||
this.addNotInObjectIdsIds(ids, query);
|
||||
} else {
|
||||
@@ -1155,7 +1157,7 @@ class DatabaseController {
|
||||
): ?Promise<void> {
|
||||
if (query['$or']) {
|
||||
return Promise.all(
|
||||
query['$or'].map(aQuery => {
|
||||
query['$or'].map((aQuery) => {
|
||||
return this.reduceRelationKeys(className, aQuery, queryOptions);
|
||||
})
|
||||
);
|
||||
@@ -1169,7 +1171,7 @@ class DatabaseController {
|
||||
relatedTo.object.objectId,
|
||||
queryOptions
|
||||
)
|
||||
.then(ids => {
|
||||
.then((ids) => {
|
||||
delete query['$relatedTo'];
|
||||
this.addInObjectIdsIds(ids, query);
|
||||
return this.reduceRelationKeys(className, query, queryOptions);
|
||||
@@ -1192,7 +1194,7 @@ class DatabaseController {
|
||||
idsFromEq,
|
||||
idsFromIn,
|
||||
ids,
|
||||
].filter(list => list !== null);
|
||||
].filter((list) => list !== null);
|
||||
const totalLength = allIds.reduce((memo, list) => memo + list.length, 0);
|
||||
|
||||
let idsIntersection = [];
|
||||
@@ -1221,7 +1223,7 @@ class DatabaseController {
|
||||
addNotInObjectIdsIds(ids: string[] = [], query: any) {
|
||||
const idsFromNin =
|
||||
query.objectId && query.objectId['$nin'] ? query.objectId['$nin'] : [];
|
||||
let allIds = [...idsFromNin, ...ids].filter(list => list !== null);
|
||||
let allIds = [...idsFromNin, ...ids].filter((list) => list !== null);
|
||||
|
||||
// make a set and spread to remove duplicates
|
||||
allIds = [...new Set(allIds)];
|
||||
@@ -1290,13 +1292,13 @@ class DatabaseController {
|
||||
|
||||
let classExists = true;
|
||||
return this.loadSchemaIfNeeded(validSchemaController).then(
|
||||
schemaController => {
|
||||
(schemaController) => {
|
||||
//Allow volatile classes if querying with Master (for _PushStatus)
|
||||
//TODO: Move volatile classes concept into mongo adapter, postgres adapter shouldn't care
|
||||
//that api.parse.com breaks when _PushStatus exists in mongo.
|
||||
return schemaController
|
||||
.getOneSchema(className, isMaster)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
// Behavior for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much.
|
||||
// For now, pretend the class exists but has no objects,
|
||||
if (error === undefined) {
|
||||
@@ -1305,7 +1307,7 @@ class DatabaseController {
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.then(schema => {
|
||||
.then((schema) => {
|
||||
// Parse.com treats queries on _created_at and _updated_at as if they were queries on createdAt and updatedAt,
|
||||
// so duplicate that behavior here. If both are specified, the correct behavior to match Parse.com is to
|
||||
// use the one that appears first in the sort list.
|
||||
@@ -1327,7 +1329,7 @@ class DatabaseController {
|
||||
caseInsensitive,
|
||||
explain,
|
||||
};
|
||||
Object.keys(sort).forEach(fieldName => {
|
||||
Object.keys(sort).forEach((fieldName) => {
|
||||
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_KEY_NAME,
|
||||
@@ -1439,8 +1441,8 @@ class DatabaseController {
|
||||
} else {
|
||||
return this.adapter
|
||||
.find(className, schema, query, queryOptions)
|
||||
.then(objects =>
|
||||
objects.map(object => {
|
||||
.then((objects) =>
|
||||
objects.map((object) => {
|
||||
object = untransformObjectACL(object);
|
||||
return filterSensitiveData(
|
||||
isMaster,
|
||||
@@ -1454,7 +1456,7 @@ class DatabaseController {
|
||||
);
|
||||
})
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INTERNAL_SERVER_ERROR,
|
||||
error
|
||||
@@ -1469,8 +1471,10 @@ class DatabaseController {
|
||||
|
||||
deleteSchema(className: string): Promise<void> {
|
||||
return this.loadSchema({ clearCache: true })
|
||||
.then(schemaController => schemaController.getOneSchema(className, true))
|
||||
.catch(error => {
|
||||
.then((schemaController) =>
|
||||
schemaController.getOneSchema(className, true)
|
||||
)
|
||||
.catch((error) => {
|
||||
if (error === undefined) {
|
||||
return { fields: {} };
|
||||
} else {
|
||||
@@ -1482,7 +1486,7 @@ class DatabaseController {
|
||||
.then(() =>
|
||||
this.adapter.count(className, { fields: {} }, null, '', false)
|
||||
)
|
||||
.then(count => {
|
||||
.then((count) => {
|
||||
if (count > 0) {
|
||||
throw new Parse.Error(
|
||||
255,
|
||||
@@ -1491,13 +1495,13 @@ class DatabaseController {
|
||||
}
|
||||
return this.adapter.deleteClass(className);
|
||||
})
|
||||
.then(wasParseCollection => {
|
||||
.then((wasParseCollection) => {
|
||||
if (wasParseCollection) {
|
||||
const relationFieldNames = Object.keys(schema.fields).filter(
|
||||
fieldName => schema.fields[fieldName].type === 'Relation'
|
||||
(fieldName) => schema.fields[fieldName].type === 'Relation'
|
||||
);
|
||||
return Promise.all(
|
||||
relationFieldNames.map(name =>
|
||||
relationFieldNames.map((name) =>
|
||||
this.adapter.deleteClass(joinTableName(className, name))
|
||||
)
|
||||
).then(() => {
|
||||
@@ -1529,7 +1533,7 @@ class DatabaseController {
|
||||
}
|
||||
const perms = schema.getClassLevelPermissions(className);
|
||||
|
||||
const userACL = aclGroup.filter(acl => {
|
||||
const userACL = aclGroup.filter((acl) => {
|
||||
return acl.indexOf('role:') != 0 && acl != '*';
|
||||
});
|
||||
|
||||
@@ -1566,7 +1570,7 @@ class DatabaseController {
|
||||
objectId: userId,
|
||||
};
|
||||
|
||||
const ors = permFields.flatMap(key => {
|
||||
const ors = permFields.flatMap((key) => {
|
||||
// constraint for single pointer setup
|
||||
const q = {
|
||||
[key]: userPointer,
|
||||
@@ -1682,9 +1686,9 @@ class DatabaseController {
|
||||
}, []);
|
||||
|
||||
// intersect all sets of protectedFields
|
||||
protectedKeysSets.forEach(fields => {
|
||||
protectedKeysSets.forEach((fields) => {
|
||||
if (fields) {
|
||||
protectedKeys = protectedKeys.filter(v => fields.includes(v));
|
||||
protectedKeys = protectedKeys.filter((v) => fields.includes(v));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1694,7 +1698,7 @@ class DatabaseController {
|
||||
createTransactionalSession() {
|
||||
return this.adapter
|
||||
.createTransactionalSession()
|
||||
.then(transactionalSession => {
|
||||
.then((transactionalSession) => {
|
||||
this._transactionalSession = transactionalSession;
|
||||
});
|
||||
}
|
||||
@@ -1737,10 +1741,10 @@ class DatabaseController {
|
||||
},
|
||||
};
|
||||
|
||||
const userClassPromise = this.loadSchema().then(schema =>
|
||||
const userClassPromise = this.loadSchema().then((schema) =>
|
||||
schema.enforceClassExists('_User')
|
||||
);
|
||||
const roleClassPromise = this.loadSchema().then(schema =>
|
||||
const roleClassPromise = this.loadSchema().then((schema) =>
|
||||
schema.enforceClassExists('_Role')
|
||||
);
|
||||
|
||||
@@ -1748,7 +1752,7 @@ class DatabaseController {
|
||||
.then(() =>
|
||||
this.adapter.ensureUniqueness('_User', requiredUserFields, ['username'])
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
logger.warn('Unable to ensure uniqueness for usernames: ', error);
|
||||
throw error;
|
||||
});
|
||||
@@ -1763,7 +1767,7 @@ class DatabaseController {
|
||||
true
|
||||
)
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
logger.warn(
|
||||
'Unable to create case insensitive username index: ',
|
||||
error
|
||||
@@ -1775,7 +1779,7 @@ class DatabaseController {
|
||||
.then(() =>
|
||||
this.adapter.ensureUniqueness('_User', requiredUserFields, ['email'])
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
logger.warn(
|
||||
'Unable to ensure uniqueness for user email addresses: ',
|
||||
error
|
||||
@@ -1793,7 +1797,7 @@ class DatabaseController {
|
||||
true
|
||||
)
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
logger.warn('Unable to create case insensitive email index: ', error);
|
||||
throw error;
|
||||
});
|
||||
@@ -1802,7 +1806,7 @@ class DatabaseController {
|
||||
.then(() =>
|
||||
this.adapter.ensureUniqueness('_Role', requiredRoleFields, ['name'])
|
||||
)
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
logger.warn('Unable to ensure uniqueness for role name: ', error);
|
||||
throw error;
|
||||
});
|
||||
@@ -1824,7 +1828,7 @@ class DatabaseController {
|
||||
]);
|
||||
}
|
||||
|
||||
static _validateQuery: any => void;
|
||||
static _validateQuery: (any) => void;
|
||||
}
|
||||
|
||||
module.exports = DatabaseController;
|
||||
|
||||
@@ -200,7 +200,7 @@ class ParseGraphQLSchema {
|
||||
if (typeof this.graphQLCustomTypeDefs.getTypeMap === 'function') {
|
||||
const customGraphQLSchemaTypeMap = this.graphQLCustomTypeDefs.getTypeMap();
|
||||
Object.values(customGraphQLSchemaTypeMap).forEach(
|
||||
customGraphQLSchemaType => {
|
||||
(customGraphQLSchemaType) => {
|
||||
if (
|
||||
!customGraphQLSchemaType ||
|
||||
!customGraphQLSchemaType.name ||
|
||||
@@ -215,50 +215,45 @@ class ParseGraphQLSchema {
|
||||
autoGraphQLSchemaType &&
|
||||
typeof customGraphQLSchemaType.getFields === 'function'
|
||||
) {
|
||||
const findAndAddLastType = type => {
|
||||
if (type.name) {
|
||||
if (!this.graphQLAutoSchema.getType(type)) {
|
||||
// To avoid schema stitching (Unknow type) bug on variables
|
||||
// transfer the final type to the Auto Schema
|
||||
this.graphQLAutoSchema._typeMap[type.name] = type;
|
||||
const findAndReplaceLastType = (parent, key) => {
|
||||
if (parent[key].name) {
|
||||
if (
|
||||
this.graphQLAutoSchema.getType(parent[key].name) &&
|
||||
this.graphQLAutoSchema.getType(parent[key].name) !==
|
||||
parent[key]
|
||||
) {
|
||||
// To avoid unresolved field on overloaded schema
|
||||
// replace the final type with the auto schema one
|
||||
parent[key] = this.graphQLAutoSchema.getType(
|
||||
parent[key].name
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (type.ofType) {
|
||||
findAndAddLastType(type.ofType);
|
||||
if (parent[key].ofType) {
|
||||
findAndReplaceLastType(parent[key], 'ofType');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Object.values(customGraphQLSchemaType.getFields()).forEach(
|
||||
field => {
|
||||
findAndAddLastType(field.type);
|
||||
if (field.args) {
|
||||
field.args.forEach(arg => {
|
||||
findAndAddLastType(arg.type);
|
||||
});
|
||||
}
|
||||
(field) => {
|
||||
findAndReplaceLastType(field, 'type');
|
||||
}
|
||||
);
|
||||
autoGraphQLSchemaType._fields = {
|
||||
...autoGraphQLSchemaType._fields,
|
||||
...customGraphQLSchemaType._fields,
|
||||
...autoGraphQLSchemaType.getFields(),
|
||||
...customGraphQLSchemaType.getFields(),
|
||||
};
|
||||
if (
|
||||
customGraphQLSchemaType.name !== 'Query' &&
|
||||
customGraphQLSchemaType.name !== 'Mutation' &&
|
||||
customGraphQLSchemaType.name !== 'Subscription'
|
||||
) {
|
||||
customGraphQLSchemaType._fields = {
|
||||
...autoGraphQLSchemaType._fields,
|
||||
...customGraphQLSchemaType._fields,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this.graphQLAutoSchema._typeMap[
|
||||
customGraphQLSchemaType.name
|
||||
] = customGraphQLSchemaType;
|
||||
}
|
||||
}
|
||||
);
|
||||
this.graphQLSchema = mergeSchemas({
|
||||
schemas: [
|
||||
this.graphQLSchemaDirectivesDefinitions,
|
||||
this.graphQLCustomTypeDefs,
|
||||
this.graphQLAutoSchema,
|
||||
],
|
||||
mergeDirectives: true,
|
||||
@@ -281,24 +276,24 @@ class ParseGraphQLSchema {
|
||||
}
|
||||
|
||||
const graphQLSchemaTypeMap = this.graphQLSchema.getTypeMap();
|
||||
Object.keys(graphQLSchemaTypeMap).forEach(graphQLSchemaTypeName => {
|
||||
Object.keys(graphQLSchemaTypeMap).forEach((graphQLSchemaTypeName) => {
|
||||
const graphQLSchemaType = graphQLSchemaTypeMap[graphQLSchemaTypeName];
|
||||
if (
|
||||
typeof graphQLSchemaType.getFields === 'function' &&
|
||||
this.graphQLCustomTypeDefs.definitions
|
||||
) {
|
||||
const graphQLCustomTypeDef = this.graphQLCustomTypeDefs.definitions.find(
|
||||
definition => definition.name.value === graphQLSchemaTypeName
|
||||
(definition) => definition.name.value === graphQLSchemaTypeName
|
||||
);
|
||||
if (graphQLCustomTypeDef) {
|
||||
const graphQLSchemaTypeFieldMap = graphQLSchemaType.getFields();
|
||||
Object.keys(graphQLSchemaTypeFieldMap).forEach(
|
||||
graphQLSchemaTypeFieldName => {
|
||||
(graphQLSchemaTypeFieldName) => {
|
||||
const graphQLSchemaTypeField =
|
||||
graphQLSchemaTypeFieldMap[graphQLSchemaTypeFieldName];
|
||||
if (!graphQLSchemaTypeField.astNode) {
|
||||
const astNode = graphQLCustomTypeDef.fields.find(
|
||||
field => field.name.value === graphQLSchemaTypeFieldName
|
||||
(field) => field.name.value === graphQLSchemaTypeFieldName
|
||||
);
|
||||
if (astNode) {
|
||||
graphQLSchemaTypeField.astNode = astNode;
|
||||
@@ -329,7 +324,9 @@ class ParseGraphQLSchema {
|
||||
) {
|
||||
if (
|
||||
(!ignoreReserved && RESERVED_GRAPHQL_TYPE_NAMES.includes(type.name)) ||
|
||||
this.graphQLTypes.find(existingType => existingType.name === type.name) ||
|
||||
this.graphQLTypes.find(
|
||||
(existingType) => existingType.name === type.name
|
||||
) ||
|
||||
(!ignoreConnection && type.name.endsWith('Connection'))
|
||||
) {
|
||||
const message = `Type ${type.name} could not be added to the auto schema because it collided with an existing type.`;
|
||||
@@ -419,7 +416,7 @@ class ParseGraphQLSchema {
|
||||
if (Array.isArray(enabledForClasses) || Array.isArray(disabledForClasses)) {
|
||||
let includedClasses = allClasses;
|
||||
if (enabledForClasses) {
|
||||
includedClasses = allClasses.filter(clazz => {
|
||||
includedClasses = allClasses.filter((clazz) => {
|
||||
return enabledForClasses.includes(clazz.className);
|
||||
});
|
||||
}
|
||||
@@ -427,12 +424,12 @@ class ParseGraphQLSchema {
|
||||
// Classes included in `enabledForClasses` that
|
||||
// are also present in `disabledForClasses` will
|
||||
// still be filtered out
|
||||
includedClasses = includedClasses.filter(clazz => {
|
||||
includedClasses = includedClasses.filter((clazz) => {
|
||||
return !disabledForClasses.includes(clazz.className);
|
||||
});
|
||||
}
|
||||
|
||||
this.isUsersClassDisabled = !includedClasses.some(clazz => {
|
||||
this.isUsersClassDisabled = !includedClasses.some((clazz) => {
|
||||
return clazz.className === '_User';
|
||||
});
|
||||
|
||||
@@ -477,11 +474,11 @@ class ParseGraphQLSchema {
|
||||
}
|
||||
};
|
||||
|
||||
return parseClasses.sort(sortClasses).map(parseClass => {
|
||||
return parseClasses.sort(sortClasses).map((parseClass) => {
|
||||
let parseClassConfig;
|
||||
if (classConfigs) {
|
||||
parseClassConfig = classConfigs.find(
|
||||
c => c.className === parseClass.className
|
||||
(c) => c.className === parseClass.className
|
||||
);
|
||||
}
|
||||
return [parseClass, parseClassConfig];
|
||||
@@ -489,7 +486,7 @@ class ParseGraphQLSchema {
|
||||
}
|
||||
|
||||
async _getFunctionNames() {
|
||||
return await getFunctionNames(this.appId).filter(functionName => {
|
||||
return await getFunctionNames(this.appId).filter((functionName) => {
|
||||
if (/^[_a-zA-Z][_a-zA-Z0-9]*$/.test(functionName)) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -183,6 +183,17 @@ export function handleParseHeaders(req, res, next) {
|
||||
delete info.sessionToken;
|
||||
}
|
||||
|
||||
if (req.userFromJWT) {
|
||||
req.auth = new auth.Auth({
|
||||
config: req.config,
|
||||
installationId: info.installationId,
|
||||
isMaster: false,
|
||||
user: req.userFromJWT,
|
||||
});
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.sessionToken) {
|
||||
req.auth = new auth.Auth({
|
||||
config: req.config,
|
||||
|
||||
Reference in New Issue
Block a user