Endpoint for purging all objects in class (#2032)
* Endpoint for purging all objects in class * Use deleteObjectsByQuery * Standalone handling function and purge cache * Change endpoint url
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||||
const MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
|
const MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
|
||||||
var request = require('request');
|
var request = require('request');
|
||||||
|
const rp = require('request-promise');
|
||||||
const Parse = require("parse/node");
|
const Parse = require("parse/node");
|
||||||
let Config = require('../src/Config');
|
let Config = require('../src/Config');
|
||||||
let defaultColumns = require('../src/Controllers/SchemaController').defaultColumns;
|
let defaultColumns = require('../src/Controllers/SchemaController').defaultColumns;
|
||||||
@@ -1347,4 +1348,111 @@ describe('miscellaneous', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('purge all objects in class', (done) => {
|
||||||
|
let object = new Parse.Object('TestObject');
|
||||||
|
object.set('foo', 'bar');
|
||||||
|
let object2 = new Parse.Object('TestObject');
|
||||||
|
object2.set('alice', 'wonderland');
|
||||||
|
Parse.Object.saveAll([object, object2])
|
||||||
|
.then(() => {
|
||||||
|
let query = new Parse.Query(TestObject);
|
||||||
|
return query.count()
|
||||||
|
}).then((count) => {
|
||||||
|
expect(count).toBe(2);
|
||||||
|
let headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Parse-Application-Id': 'test',
|
||||||
|
'X-Parse-Master-Key': 'test'
|
||||||
|
};
|
||||||
|
request.del({
|
||||||
|
headers: headers,
|
||||||
|
url: 'http://localhost:8378/1/purge/TestObject',
|
||||||
|
json: true
|
||||||
|
}, (err, res, body) => {
|
||||||
|
expect(err).toBe(null);
|
||||||
|
let query = new Parse.Query(TestObject);
|
||||||
|
return query.count().then((count) => {
|
||||||
|
expect(count).toBe(0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fail on purge all objects in class without master key', (done) => {
|
||||||
|
let headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Parse-Application-Id': 'test',
|
||||||
|
'X-Parse-REST-API-Key': 'rest'
|
||||||
|
};
|
||||||
|
rp({
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: headers,
|
||||||
|
uri: 'http://localhost:8378/1/purge/TestObject',
|
||||||
|
json: true
|
||||||
|
}).then(body => {
|
||||||
|
fail('Should not succeed');
|
||||||
|
}).catch(err => {
|
||||||
|
expect(err.error.error).toEqual('unauthorized: master key is required');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('purge all objects in _Role also purge cache', (done) => {
|
||||||
|
let headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Parse-Application-Id': 'test',
|
||||||
|
'X-Parse-Master-Key': 'test'
|
||||||
|
};
|
||||||
|
var user, object;
|
||||||
|
createTestUser().then((x) => {
|
||||||
|
user = x;
|
||||||
|
let acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(false);
|
||||||
|
let role = new Parse.Object('_Role');
|
||||||
|
role.set('name', 'TestRole');
|
||||||
|
role.setACL(acl);
|
||||||
|
let users = role.relation('users');
|
||||||
|
users.add(user);
|
||||||
|
return role.save({}, { useMasterKey: true });
|
||||||
|
}).then((x) => {
|
||||||
|
let query = new Parse.Query('_Role');
|
||||||
|
return query.find({ useMasterKey: true });
|
||||||
|
}).then((x) => {
|
||||||
|
expect(x.length).toEqual(1);
|
||||||
|
let relation = x[0].relation('users').query();
|
||||||
|
return relation.first({ useMasterKey: true });
|
||||||
|
}).then((x) => {
|
||||||
|
expect(x.id).toEqual(user.id);
|
||||||
|
object = new Parse.Object('TestObject');
|
||||||
|
let acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(false);
|
||||||
|
acl.setPublicWriteAccess(false);
|
||||||
|
acl.setRoleReadAccess('TestRole', true);
|
||||||
|
acl.setRoleWriteAccess('TestRole', true);
|
||||||
|
object.setACL(acl);
|
||||||
|
return object.save();
|
||||||
|
}).then((x) => {
|
||||||
|
let query = new Parse.Query('TestObject');
|
||||||
|
return query.find({ sessionToken: user.getSessionToken() });
|
||||||
|
}).then((x) => {
|
||||||
|
expect(x.length).toEqual(1);
|
||||||
|
return rp({
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: headers,
|
||||||
|
uri: 'http://localhost:8378/1/purge/_Role',
|
||||||
|
json: true
|
||||||
|
});
|
||||||
|
}).then((x) => {
|
||||||
|
let query = new Parse.Query('TestObject');
|
||||||
|
return query.get(object.id, { sessionToken: user.getSessionToken() });
|
||||||
|
}).then((x) => {
|
||||||
|
fail('Should not succeed');
|
||||||
|
}, (e) => {
|
||||||
|
expect(e.code).toEqual(Parse.Error.OBJECT_NOT_FOUND);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -91,6 +91,16 @@ DatabaseController.prototype.collectionExists = function(className) {
|
|||||||
return this.adapter.classExists(className);
|
return this.adapter.classExists(className);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DatabaseController.prototype.purgeCollection = function(className) {
|
||||||
|
return this.loadSchema()
|
||||||
|
.then((schema) => {
|
||||||
|
schema.getOneSchema(className)
|
||||||
|
})
|
||||||
|
.then((schema) => {
|
||||||
|
this.adapter.deleteObjectsByQuery(className, {}, schema);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
DatabaseController.prototype.validateClassName = function(className) {
|
DatabaseController.prototype.validateClassName = function(className) {
|
||||||
if (this.skipValidation) {
|
if (this.skipValidation) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { SchemasRouter } from './Routers/SchemasRouter';
|
|||||||
import { SessionsRouter } from './Routers/SessionsRouter';
|
import { SessionsRouter } from './Routers/SessionsRouter';
|
||||||
import { UserController } from './Controllers/UserController';
|
import { UserController } from './Controllers/UserController';
|
||||||
import { UsersRouter } from './Routers/UsersRouter';
|
import { UsersRouter } from './Routers/UsersRouter';
|
||||||
|
import { PurgeRouter } from './Routers/PurgeRouter';
|
||||||
|
|
||||||
import DatabaseController from './Controllers/DatabaseController';
|
import DatabaseController from './Controllers/DatabaseController';
|
||||||
const SchemaController = require('./Controllers/SchemaController');
|
const SchemaController = require('./Controllers/SchemaController');
|
||||||
@@ -291,6 +292,7 @@ class ParseServer {
|
|||||||
new IAPValidationRouter(),
|
new IAPValidationRouter(),
|
||||||
new FeaturesRouter(),
|
new FeaturesRouter(),
|
||||||
new GlobalConfigRouter(),
|
new GlobalConfigRouter(),
|
||||||
|
new PurgeRouter(),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) {
|
if (process.env.PARSE_EXPERIMENTAL_HOOKS_ENABLED || process.env.TESTING) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class FeaturesRouter extends PromiseRouter {
|
|||||||
removeField: true,
|
removeField: true,
|
||||||
addClass: true,
|
addClass: true,
|
||||||
removeClass: true,
|
removeClass: true,
|
||||||
clearAllDataFromClass: false,
|
clearAllDataFromClass: true,
|
||||||
exportClass: false,
|
exportClass: false,
|
||||||
editClassLevelPermissions: true,
|
editClassLevelPermissions: true,
|
||||||
editPointerPermissions: true,
|
editPointerPermissions: true,
|
||||||
|
|||||||
24
src/Routers/PurgeRouter.js
Normal file
24
src/Routers/PurgeRouter.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import PromiseRouter from '../PromiseRouter';
|
||||||
|
import * as middleware from '../middlewares';
|
||||||
|
|
||||||
|
export class PurgeRouter extends PromiseRouter {
|
||||||
|
|
||||||
|
handlePurge(req) {
|
||||||
|
return req.config.database.purgeCollection(req.params.className)
|
||||||
|
.then(() => {
|
||||||
|
var cacheAdapter = req.config.cacheController;
|
||||||
|
if (req.params.className == '_Session') {
|
||||||
|
cacheAdapter.user.clear();
|
||||||
|
} else if (req.params.className == '_Role') {
|
||||||
|
cacheAdapter.role.clear();
|
||||||
|
}
|
||||||
|
return {response: {}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mountRoutes() {
|
||||||
|
this.route('DELETE', '/purge/:className', middleware.promiseEnforceMasterKeyAccess, (req) => { return this.handlePurge(req); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PurgeRouter;
|
||||||
Reference in New Issue
Block a user