Compare commits

..
24 Commits
Author SHA1 Message Date
Antoine Cormouls 370f5f0eaa build 2020-04-10 13:57:54 +02:00
Antoine Cormouls 94bbc8e7d0 Merge branch 'resolve-type-from-auto-schema' into package 2020-04-10 13:57:16 +02:00
Antoine Cormouls 1469aaad17 fix + improve 2020-04-10 13:57:00 +02:00
Antoine Cormouls 40af386021 fix merge 2020-04-10 12:49:23 +02:00
Antoine Cormouls 9a3774dc1c Merge branch 'upstream/master' into package 2020-04-10 12:44:08 +02:00
Antoine Cormouls ec5337b07a Merge branch 'resolve-type-from-auto-schema' into package 2020-04-10 12:43:53 +02:00
Antoine Cormouls 75d97522cd Simple merge 2020-04-10 12:43:37 +02:00
greenkeeper[bot] 0698c20997 Update apollo-link to the latest version 🚀 (#6591)
* chore(package): update apollo-link-http to version 1.5.17

* chore(package): update apollo-link-ws to version 1.0.20

* chore(package): update apollo-link to version 1.2.14

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-09 22:00:24 +00:00
greenkeeper[bot] 3c13d8015c Update husky to the latest version 🚀 (#6587)
* chore(package): update husky to version 4.2.4

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-09 18:54:55 +00:00
greenkeeper[bot] f2fb6bd610 Update lint-staged to the latest version 🚀 (#6584)
* chore(package): update lint-staged to version 10.1.3

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-09 16:52:42 +00:00
Noah Silas 19086a8112 perf: Allow covering relation queries with minimal index (#6581)
* Apply linter changes on files I'm about to update

My actual changes were quite difficult to find when buried in this sea
of style changes, which were getting automatically applied during a
pre-commit hook. Here I just run the hooks against the files I'm going
to be touching in the following commit, so that a reviewer can ignore
these automatically generated diffs and just view the meaningful commit.

* perf: Allow covering relation queries with minimal index

When finding objects through a relation, we're sending Mongo queries
that look like this:
```
db.getCollection('_Join:foo:bar').find({ relatedId: { $in: [...] } });
```

From the result of that query, we're only reading the `owningId` field,
so we can start by adding it as a projection:
```
db.getCollection('_Join:foo:bar')
  .find({ relatedId: { $in: [...] } })
  .project({ owningId: 1 });
```

This seems like the perfect example of a query that could be satisfied
with an index scan: we are querying on one field, and only need one
field from the matching document.

For example, this can allow users to speed up the fetching of user roles
in authentication, because they query a `roles` relation on the `_Role`
collection. To add a covering index on that, you could now add an index
like the following:
```
db.getCollection('_Join:roles:_Role').createIndex(
  { relatedId: 1, owningId: 1 },
  { background: true }
);
```

One caveat there is that the index I propose above doesn't include the
`_id` column. For the query in question, we don't actually care about
the ID of the row in the join table, just the `owningId` field, so we
can avoid some overhead of putting the `_id` column into the index if we
can also drop it from the projection. This requires adding a small
special case to the MongoStorageAdapter, because the `_id` field is
special: you have to opt-out of using it by projecting `{ _id: 0 }`.
2020-04-08 11:43:45 -07:00
greenkeeper[bot] fc805fc5da Update semver to the latest version 🚀 (#6576)
* fix(package): update semver to version 7.2.0

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-08 04:12:17 +00:00
greenkeeper[bot] b791e13dfd Update babel7 to the latest version 🚀 (#6579)
* chore(package): update @babel/plugin-proposal-object-rest-spread to version 7.9.5

* chore(package): update @babel/preset-env to version 7.9.5

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-07 22:35:27 +00:00
greenkeeper[bot] c2fa25d846 Update apollo-server to the latest version 🚀 (#6578)
* fix(package): update apollo-server-express to version 2.12.0

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-07 19:16:24 +00:00
Antoine Cormouls 12432f6455 Fix 2020-04-07 12:26:43 +02:00
Antoine Cormouls 1760dedf99 Merge branch 'downgrade-graphql' into package 2020-04-07 12:25:24 +02:00
Antoine Cormouls 51e21d36a2 Downgrade graphql 2020-04-07 12:11:00 +02:00
Siddharth RameshandDiamond Lewis cc5f14e11a fix: Postrgres group aggregation (#6522)
* Postrgres group aggregation

* convert tabs to spaces

Co-authored-by: Diamond Lewis <findlewis@gmail.com>
2020-04-06 12:20:33 -05:00
greenkeeper[bot] bfdcba8d5c Update lint-staged to the latest version 🚀 (#6566)
* chore(package): update lint-staged to version 10.1.2

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-06 17:05:08 +00:00
greenkeeper[bot] bcd5f1717e Update prettier to the latest version 🚀 (#6568)
* chore(package): update prettier to version 2.0.3

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-06 16:45:39 +00:00
Antoine Cormouls 48beb2e4b5 Remove beta (#6565) 2020-04-05 20:21:56 +01:00
Gordon SunandGordon Sun b08571774f Allow set user mapped from JWT directly on request (#6411)
* Use user mapped from JWT for Auth

* Add a test for userFromJWT bypass

Co-authored-by: Gordon Sun <gordon.sun@pipe17.com>
2020-04-03 17:09:37 -05:00
greenkeeper[bot] 81b22b21b5 Update nyc to the latest version 🚀 (#6563)
* chore(package): update nyc to version 15.0.1

* chore(package): update lockfile package-lock.json

Co-authored-by: greenkeeper[bot] <23040076+greenkeeper[bot]@users.noreply.github.com>
2020-04-03 17:46:11 +00:00
Arthur CinaderandTom Fox 1045eeb700 Prep release 4.2 (#6560)
* Prep release 4.2

* fix links, consistent formatting

* remove unnecessary credit

* add one more commit

* Fix link, remove double spaces

* add a few more commits
run npm audit fix

* little fixes

Co-authored-by: Tom Fox <13188249+TomWFox@users.noreply.github.com>
2020-04-03 09:53:08 -07:00
15 changed files with 1047 additions and 949 deletions
+33 -7
View File
@@ -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)
+2 -4
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
+515 -524
View File
File diff suppressed because it is too large Load Diff
+15 -14
View File
@@ -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"
]
}
+11
View File
@@ -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();
});
});
});
+111 -99
View File
@@ -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
+80 -76
View File
@@ -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;
+37 -40
View File
@@ -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 {
+11
View File
@@ -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,