Handle mongodb auth errors that may occur after running the server for a while (#4576)
This commit is contained in:
@@ -23,6 +23,7 @@ import Parse from 'parse/node';
|
|||||||
// @flow-disable-next
|
// @flow-disable-next
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import defaults from '../../../defaults';
|
import defaults from '../../../defaults';
|
||||||
|
import logger from '../../../logger';
|
||||||
|
|
||||||
// @flow-disable-next
|
// @flow-disable-next
|
||||||
const mongodb = require('mongodb');
|
const mongodb = require('mongodb');
|
||||||
@@ -160,6 +161,16 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
return this.connectionPromise;
|
return this.connectionPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleError<T>(error: ?Error): Promise<T> {
|
||||||
|
if (error && error.code === 13) { // Unauthorized error
|
||||||
|
delete this.client;
|
||||||
|
delete this.database;
|
||||||
|
delete this.connectionPromise;
|
||||||
|
logger.error('Received unauthorized error', { error: error });
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
handleShutdown() {
|
handleShutdown() {
|
||||||
if (!this.client) {
|
if (!this.client) {
|
||||||
return;
|
return;
|
||||||
@@ -170,7 +181,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
_adaptiveCollection(name: string) {
|
_adaptiveCollection(name: string) {
|
||||||
return this.connect()
|
return this.connect()
|
||||||
.then(() => this.database.collection(this._collectionPrefix + name))
|
.then(() => this.database.collection(this._collectionPrefix + name))
|
||||||
.then(rawCollection => new MongoCollection(rawCollection));
|
.then(rawCollection => new MongoCollection(rawCollection))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
_schemaCollection(): Promise<MongoSchemaCollection> {
|
_schemaCollection(): Promise<MongoSchemaCollection> {
|
||||||
@@ -184,14 +196,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
|
return this.database.listCollections({ name: this._collectionPrefix + name }).toArray();
|
||||||
}).then(collections => {
|
}).then(collections => {
|
||||||
return collections.length > 0;
|
return collections.length > 0;
|
||||||
});
|
}).catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
setClassLevelPermissions(className: string, CLPs: any): Promise<void> {
|
setClassLevelPermissions(className: string, CLPs: any): Promise<void> {
|
||||||
return this._schemaCollection()
|
return this._schemaCollection()
|
||||||
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
||||||
$set: { '_metadata.class_permissions': CLPs }
|
$set: { '_metadata.class_permissions': CLPs }
|
||||||
}));
|
})).catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
setIndexesWithSchemaFormat(className: string, submittedIndexes: any, existingIndexes: any = {}, fields: any): Promise<void> {
|
setIndexesWithSchemaFormat(className: string, submittedIndexes: any, existingIndexes: any = {}, fields: any): Promise<void> {
|
||||||
@@ -237,7 +249,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
||||||
$set: { '_metadata.indexes': existingIndexes }
|
$set: { '_metadata.indexes': existingIndexes }
|
||||||
}));
|
}))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
setIndexesFromMongo(className: string) {
|
setIndexesFromMongo(className: string) {
|
||||||
@@ -257,7 +270,9 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
||||||
$set: { '_metadata.indexes': indexes }
|
$set: { '_metadata.indexes': indexes }
|
||||||
}));
|
}));
|
||||||
}).catch(() => {
|
})
|
||||||
|
.catch(err => this.handleError(err))
|
||||||
|
.catch(() => {
|
||||||
// Ignore if collection not found
|
// Ignore if collection not found
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
@@ -269,13 +284,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
mongoObject._id = className;
|
mongoObject._id = className;
|
||||||
return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields)
|
return this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields)
|
||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection => schemaCollection.insertSchema(mongoObject));
|
.then(schemaCollection => schemaCollection.insertSchema(mongoObject))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> {
|
addFieldIfNotExists(className: string, fieldName: string, type: any): Promise<void> {
|
||||||
return this._schemaCollection()
|
return this._schemaCollection()
|
||||||
.then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type))
|
.then(schemaCollection => schemaCollection.addFieldIfNotExists(className, fieldName, type))
|
||||||
.then(() => this.createIndexesIfNeeded(className, fieldName, type));
|
.then(() => this.createIndexesIfNeeded(className, fieldName, type))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.)
|
// Drops a collection. Resolves with true if it was a Parse Schema (eg. _User, Custom, etc.)
|
||||||
@@ -293,12 +310,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
// We've dropped the collection, now remove the _SCHEMA document
|
// We've dropped the collection, now remove the _SCHEMA document
|
||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection => schemaCollection.findAndDeleteSchema(className))
|
.then(schemaCollection => schemaCollection.findAndDeleteSchema(className))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all data known to this adapter. Used for testing.
|
// Delete all data known to this adapter. Used for testing.
|
||||||
deleteAllClasses() {
|
deleteAllClasses() {
|
||||||
return storageAdapterAllCollections(this)
|
return storageAdapterAllCollections(this)
|
||||||
.then(collections => Promise.all(collections.map(collection => collection.drop())));
|
.then(collections => Promise.all(collections.map(collection => collection.drop())))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the column and all the data. For Relations, the _Join collection is handled
|
// Remove the column and all the data. For Relations, the _Join collection is handled
|
||||||
@@ -342,14 +361,16 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection.updateMany({}, collectionUpdate))
|
.then(collection => collection.updateMany({}, collectionUpdate))
|
||||||
.then(() => this._schemaCollection())
|
.then(() => this._schemaCollection())
|
||||||
.then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate));
|
.then(schemaCollection => schemaCollection.updateSchema(className, schemaUpdate))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a promise for all schemas known to this adapter, in Parse format. In case the
|
// Return a promise for all schemas known to this adapter, in Parse format. In case the
|
||||||
// schemas cannot be retrieved, returns a promise that rejects. Requirements for the
|
// schemas cannot be retrieved, returns a promise that rejects. Requirements for the
|
||||||
// rejection reason are TBD.
|
// rejection reason are TBD.
|
||||||
getAllClasses(): Promise<StorageClass[]> {
|
getAllClasses(): Promise<StorageClass[]> {
|
||||||
return this._schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA());
|
return this._schemaCollection().then(schemasCollection => schemasCollection._fetchAllSchemasFrom_SCHEMA())
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a promise for the schema with the given name, in Parse format. If
|
// Return a promise for the schema with the given name, in Parse format. If
|
||||||
@@ -358,6 +379,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
getClass(className: string): Promise<StorageClass> {
|
getClass(className: string): Promise<StorageClass> {
|
||||||
return this._schemaCollection()
|
return this._schemaCollection()
|
||||||
.then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className))
|
.then(schemasCollection => schemasCollection._fetchOneSchemaFrom_SCHEMA(className))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
|
// TODO: As yet not particularly well specified. Creates an object. Maybe shouldn't even need the schema,
|
||||||
@@ -381,7 +403,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
});
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all objects that match the given Parse Query.
|
// Remove all objects that match the given Parse Query.
|
||||||
@@ -394,6 +417,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
return collection.deleteMany(mongoWhere)
|
return collection.deleteMany(mongoWhere)
|
||||||
})
|
})
|
||||||
|
.catch(err => this.handleError(err))
|
||||||
.then(({ result }) => {
|
.then(({ result }) => {
|
||||||
if (result.n === 0) {
|
if (result.n === 0) {
|
||||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||||
@@ -410,7 +434,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
const mongoUpdate = transformUpdate(className, update, schema);
|
const mongoUpdate = transformUpdate(className, update, schema);
|
||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection.updateMany(mongoWhere, mongoUpdate));
|
.then(collection => collection.updateMany(mongoWhere, mongoUpdate))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atomically finds and updates an object based on query.
|
// Atomically finds and updates an object based on query.
|
||||||
@@ -427,7 +452,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'A duplicate value for a field with unique values was provided');
|
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'A duplicate value for a field with unique values was provided');
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
});
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hopefully we can get rid of this. It's only used for config and hooks.
|
// Hopefully we can get rid of this. It's only used for config and hooks.
|
||||||
@@ -436,7 +462,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
const mongoUpdate = transformUpdate(className, update, schema);
|
const mongoUpdate = transformUpdate(className, update, schema);
|
||||||
const mongoWhere = transformWhere(className, query, schema);
|
const mongoWhere = transformWhere(className, query, schema);
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection.upsertOne(mongoWhere, mongoUpdate));
|
.then(collection => collection.upsertOne(mongoWhere, mongoUpdate))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
|
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
|
||||||
@@ -461,6 +488,7 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
readPreference,
|
readPreference,
|
||||||
}))
|
}))
|
||||||
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
|
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
|
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
|
||||||
@@ -482,14 +510,15 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.');
|
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Tried to ensure field uniqueness for a class that already has duplicates.');
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
});
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used in tests
|
// Used in tests
|
||||||
_rawFind(className: string, query: QueryType) {
|
_rawFind(className: string, query: QueryType) {
|
||||||
return this._adaptiveCollection(className).then(collection => collection.find(query, {
|
return this._adaptiveCollection(className).then(collection => collection.find(query, {
|
||||||
maxTimeMS: this._maxTimeMS,
|
maxTimeMS: this._maxTimeMS,
|
||||||
}));
|
})).catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes a count.
|
// Executes a count.
|
||||||
@@ -500,7 +529,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
.then(collection => collection.count(transformWhere(className, query, schema), {
|
.then(collection => collection.count(transformWhere(className, query, schema), {
|
||||||
maxTimeMS: this._maxTimeMS,
|
maxTimeMS: this._maxTimeMS,
|
||||||
readPreference,
|
readPreference,
|
||||||
}));
|
}))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
|
distinct(className: string, schema: SchemaType, query: QueryType, fieldName: string) {
|
||||||
@@ -520,7 +550,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
}
|
}
|
||||||
return mongoObjectToParseObject(className, object, schema);
|
return mongoObjectToParseObject(className, object, schema);
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
aggregate(className: string, schema: any, pipeline: any, readPreference: ?string) {
|
aggregate(className: string, schema: any, pipeline: any, readPreference: ?string) {
|
||||||
@@ -573,7 +604,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
})
|
})
|
||||||
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
|
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
_parseReadPreference(readPreference: ?string): ?string {
|
_parseReadPreference(readPreference: ?string): ?string {
|
||||||
@@ -609,12 +641,14 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
|
|
||||||
createIndex(className: string, index: any) {
|
createIndex(className: string, index: any) {
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection._mongoCollection.createIndex(index));
|
.then(collection => collection._mongoCollection.createIndex(index))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
createIndexes(className: string, indexes: any) {
|
createIndexes(className: string, indexes: any) {
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection._mongoCollection.createIndexes(indexes));
|
.then(collection => collection._mongoCollection.createIndexes(indexes))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
createIndexesIfNeeded(className: string, fieldName: string, type: any) {
|
createIndexesIfNeeded(className: string, fieldName: string, type: any) {
|
||||||
@@ -656,17 +690,20 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
|
|
||||||
getIndexes(className: string) {
|
getIndexes(className: string) {
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection._mongoCollection.indexes());
|
.then(collection => collection._mongoCollection.indexes())
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
dropIndex(className: string, index: any) {
|
dropIndex(className: string, index: any) {
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection._mongoCollection.dropIndex(index));
|
.then(collection => collection._mongoCollection.dropIndex(index))
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
dropAllIndexes(className: string) {
|
dropAllIndexes(className: string) {
|
||||||
return this._adaptiveCollection(className)
|
return this._adaptiveCollection(className)
|
||||||
.then(collection => collection._mongoCollection.dropIndexes());
|
.then(collection => collection._mongoCollection.dropIndexes())
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSchemaWithIndexes(): Promise<any> {
|
updateSchemaWithIndexes(): Promise<any> {
|
||||||
@@ -676,7 +713,8 @@ export class MongoStorageAdapter implements StorageAdapter {
|
|||||||
return this.setIndexesFromMongo(schema.className);
|
return this.setIndexesFromMongo(schema.className);
|
||||||
});
|
});
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
});
|
})
|
||||||
|
.catch(err => this.handleError(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user