Refactor pushStatusHandler to use Parse instead of direct access (#4173)
* Refactors pushStatusHandler to use HTTP interface so we can bind CloudCode hooks * Handle correctly nested dot atomic operations * Better handling of restricted class names, add support for afterSave _PushStatus * Adds simple testing for afterSave(PushStatus) * Reverts jobStatusHandler * Addresses fixes * adds delays to all methods
This commit is contained in:
@@ -1627,4 +1627,19 @@ describe('afterFind hooks', () => {
|
|||||||
})
|
})
|
||||||
.then(() => done());
|
.then(() => done());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should validate triggers correctly', () => {
|
||||||
|
expect(() => {
|
||||||
|
Parse.Cloud.beforeSave('_Session', () => {});
|
||||||
|
}).toThrow('Triggers are not supported for _Session class.');
|
||||||
|
expect(() => {
|
||||||
|
Parse.Cloud.afterSave('_Session', () => {});
|
||||||
|
}).toThrow('Triggers are not supported for _Session class.');
|
||||||
|
expect(() => {
|
||||||
|
Parse.Cloud.beforeSave('_PushStatus', () => {});
|
||||||
|
}).toThrow('Only afterSave is allowed on _PushStatus');
|
||||||
|
expect(() => {
|
||||||
|
Parse.Cloud.afterSave('_PushStatus', () => {});
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ describe('Parse.Push', () => {
|
|||||||
alert: 'Hello world!'
|
alert: 'Hello world!'
|
||||||
}
|
}
|
||||||
}, {useMasterKey: true}))
|
}, {useMasterKey: true}))
|
||||||
|
.then(() => delayPromise(500))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
request.get({
|
request.get({
|
||||||
url: 'http://localhost:8378/1/classes/_PushStatus',
|
url: 'http://localhost:8378/1/classes/_PushStatus',
|
||||||
@@ -155,6 +156,7 @@ describe('Parse.Push', () => {
|
|||||||
alert: 'Hello world!'
|
alert: 'Hello world!'
|
||||||
}
|
}
|
||||||
}, {useMasterKey: true}))
|
}, {useMasterKey: true}))
|
||||||
|
.then(() => delayPromise(500)) // put a delay as we keep writing
|
||||||
.then(() => {
|
.then(() => {
|
||||||
request.get({
|
request.get({
|
||||||
url: 'http://localhost:8378/1/classes/_PushStatus',
|
url: 'http://localhost:8378/1/classes/_PushStatus',
|
||||||
|
|||||||
@@ -388,6 +388,11 @@ describe('PushController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('properly creates _PushStatus', (done) => {
|
it('properly creates _PushStatus', (done) => {
|
||||||
|
const pushStatusAfterSave = {
|
||||||
|
handler: function() {}
|
||||||
|
};
|
||||||
|
const spy = spyOn(pushStatusAfterSave, 'handler').and.callThrough();
|
||||||
|
Parse.Cloud.afterSave('_PushStatus', pushStatusAfterSave.handler);
|
||||||
var installations = [];
|
var installations = [];
|
||||||
while(installations.length != 10) {
|
while(installations.length != 10) {
|
||||||
const installation = new Parse.Object("_Installation");
|
const installation = new Parse.Object("_Installation");
|
||||||
@@ -466,8 +471,36 @@ describe('PushController', () => {
|
|||||||
return query.find();
|
return query.find();
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
expect(error.code).toBe(119);
|
expect(error.code).toBe(119);
|
||||||
done();
|
})
|
||||||
});
|
.then(() => {
|
||||||
|
function getPushStatus(callIndex) {
|
||||||
|
return spy.calls.all()[callIndex].args[0].object;
|
||||||
|
}
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
expect(spy.calls.count()).toBe(4);
|
||||||
|
const allCalls = spy.calls.all();
|
||||||
|
allCalls.forEach((call) => {
|
||||||
|
expect(call.args.length).toBe(2);
|
||||||
|
const object = call.args[0].object;
|
||||||
|
expect(object instanceof Parse.Object).toBe(true);
|
||||||
|
});
|
||||||
|
expect(getPushStatus(0).get('status')).toBe('pending');
|
||||||
|
expect(getPushStatus(1).get('status')).toBe('running');
|
||||||
|
expect(getPushStatus(1).get('numSent')).toBe(0);
|
||||||
|
expect(getPushStatus(2).get('status')).toBe('running');
|
||||||
|
expect(getPushStatus(2).get('numSent')).toBe(10);
|
||||||
|
expect(getPushStatus(2).get('numFailed')).toBe(5);
|
||||||
|
// Those are updated from a nested . operation, this would
|
||||||
|
// not render correctly before
|
||||||
|
expect(getPushStatus(2).get('failedPerType')).toEqual({
|
||||||
|
android: 5
|
||||||
|
});
|
||||||
|
expect(getPushStatus(2).get('sentPerType')).toEqual({
|
||||||
|
ios: 10
|
||||||
|
});
|
||||||
|
expect(getPushStatus(3).get('status')).toBe('succeeded');
|
||||||
|
})
|
||||||
|
.then(done).catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly report failures in _PushStatus', (done) => {
|
it('should properly report failures in _PushStatus', (done) => {
|
||||||
@@ -710,7 +743,7 @@ describe('PushController', () => {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
return Parse.Object.saveAll(installations).then(() => {
|
return Parse.Object.saveAll(installations).then(() => {
|
||||||
return pushController.sendPush(payload, {}, config, auth);
|
return pushController.sendPush(payload, {}, config, auth);
|
||||||
});
|
}).then(() => new Promise(resolve => setTimeout(resolve, 300)));
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
const query = new Parse.Query('_PushStatus');
|
const query = new Parse.Query('_PushStatus');
|
||||||
return query.find({useMasterKey: true}).then((results) => {
|
return query.find({useMasterKey: true}).then((results) => {
|
||||||
@@ -776,20 +809,15 @@ describe('PushController', () => {
|
|||||||
var config = new Config(Parse.applicationId);
|
var config = new Config(Parse.applicationId);
|
||||||
return Parse.Object.saveAll(installations).then(() => {
|
return Parse.Object.saveAll(installations).then(() => {
|
||||||
return pushController.sendPush(payload, {}, config, auth);
|
return pushController.sendPush(payload, {}, config, auth);
|
||||||
}).then(() => new Promise(resolve => setTimeout(resolve, 100)));
|
}).then(() => new Promise(resolve => setTimeout(resolve, 300)));
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
const query = new Parse.Query('_PushStatus');
|
const query = new Parse.Query('_PushStatus');
|
||||||
return query.find({useMasterKey: true}).then((results) => {
|
return query.find({useMasterKey: true}).then((results) => {
|
||||||
expect(results.length).toBe(1);
|
expect(results.length).toBe(1);
|
||||||
const pushStatus = results[0];
|
const pushStatus = results[0];
|
||||||
expect(pushStatus.get('status')).toBe('scheduled');
|
expect(pushStatus.get('status')).toBe('scheduled');
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
}).then(done).catch(done.err);
|
||||||
console.error(err);
|
|
||||||
fail('should not fail');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not enqueue push when device token is not set', (done) => {
|
it('should not enqueue push when device token is not set', (done) => {
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ describe('PushWorker', () => {
|
|||||||
const spy = spyOn(config.database, "update").and.callFake(() => {
|
const spy = spyOn(config.database, "update").and.callFake(() => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
handler.trackSent([
|
const toAwait = handler.trackSent([
|
||||||
{
|
{
|
||||||
transmitted: false,
|
transmitted: false,
|
||||||
device: {
|
device: {
|
||||||
@@ -239,13 +239,13 @@ describe('PushWorker', () => {
|
|||||||
expect(lastCall.args[2]).toEqual({
|
expect(lastCall.args[2]).toEqual({
|
||||||
deviceToken: { '__op': "Delete" }
|
deviceToken: { '__op': "Delete" }
|
||||||
});
|
});
|
||||||
done();
|
toAwait.then(done).catch(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('tracks push status per UTC offsets', (done) => {
|
it('tracks push status per UTC offsets', (done) => {
|
||||||
const config = new Config('test');
|
const config = new Config('test');
|
||||||
const handler = pushStatusHandler(config, 'ABCDEF1234');
|
const handler = pushStatusHandler(config);
|
||||||
const spy = spyOn(config.database, "update").and.callThrough();
|
const spy = spyOn(Parse, "_request").and.callThrough();
|
||||||
const UTCOffset = 1;
|
const UTCOffset = 1;
|
||||||
handler.setInitial().then(() => {
|
handler.setInitial().then(() => {
|
||||||
return handler.trackSent([
|
return handler.trackSent([
|
||||||
@@ -266,14 +266,9 @@ describe('PushWorker', () => {
|
|||||||
], UTCOffset)
|
], UTCOffset)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
expect(spy.calls.count()).toBe(1);
|
|
||||||
const lastCall = spy.calls.mostRecent();
|
const lastCall = spy.calls.mostRecent();
|
||||||
expect(lastCall.args[0]).toBe('_PushStatus');
|
expect(lastCall.args[0]).toBe('PUT');
|
||||||
const updatePayload = lastCall.args[2];
|
expect(lastCall.args[1]).toBe(`classes/_PushStatus/${handler.objectId}`);
|
||||||
expect(updatePayload.updatedAt instanceof Date).toBeTruthy();
|
|
||||||
// remove the updatedAt as not testable
|
|
||||||
delete updatePayload.updatedAt;
|
|
||||||
|
|
||||||
expect(lastCall.args[2]).toEqual({
|
expect(lastCall.args[2]).toEqual({
|
||||||
numSent: { __op: 'Increment', amount: 1 },
|
numSent: { __op: 'Increment', amount: 1 },
|
||||||
numFailed: { __op: 'Increment', amount: 1 },
|
numFailed: { __op: 'Increment', amount: 1 },
|
||||||
@@ -284,7 +279,7 @@ describe('PushWorker', () => {
|
|||||||
count: { __op: 'Increment', amount: -2 },
|
count: { __op: 'Increment', amount: -2 },
|
||||||
});
|
});
|
||||||
const query = new Parse.Query('_PushStatus');
|
const query = new Parse.Query('_PushStatus');
|
||||||
return query.get('ABCDEF1234', { useMasterKey: true });
|
return query.get(handler.objectId, { useMasterKey: true });
|
||||||
}).then((pushStatus) => {
|
}).then((pushStatus) => {
|
||||||
const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset');
|
const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset');
|
||||||
expect(sentPerUTCOffset['1']).toBe(1);
|
expect(sentPerUTCOffset['1']).toBe(1);
|
||||||
@@ -315,7 +310,7 @@ describe('PushWorker', () => {
|
|||||||
], UTCOffset)
|
], UTCOffset)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
const query = new Parse.Query('_PushStatus');
|
const query = new Parse.Query('_PushStatus');
|
||||||
return query.get('ABCDEF1234', { useMasterKey: true });
|
return query.get(handler.objectId, { useMasterKey: true });
|
||||||
}).then((pushStatus) => {
|
}).then((pushStatus) => {
|
||||||
const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset');
|
const sentPerUTCOffset = pushStatus.get('sentPerUTCOffset');
|
||||||
expect(sentPerUTCOffset['1']).toBe(3);
|
expect(sentPerUTCOffset['1']).toBe(3);
|
||||||
@@ -330,7 +325,7 @@ describe('PushWorker', () => {
|
|||||||
spyOn(config.database, "create").and.callFake(() => {
|
spyOn(config.database, "create").and.callFake(() => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
const spy = spyOn(config.database, "update").and.callFake(() => {
|
const spy = spyOn(Parse, "_request").and.callFake(() => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
const UTCOffset = -6;
|
const UTCOffset = -6;
|
||||||
@@ -353,14 +348,8 @@ describe('PushWorker', () => {
|
|||||||
},
|
},
|
||||||
], UTCOffset).then(() => {
|
], UTCOffset).then(() => {
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
expect(spy.calls.count()).toBe(1);
|
|
||||||
const lastCall = spy.calls.mostRecent();
|
const lastCall = spy.calls.mostRecent();
|
||||||
expect(lastCall.args[0]).toBe('_PushStatus');
|
expect(lastCall.args[1]).toBe(`classes/_PushStatus/${handler.objectId}`);
|
||||||
const updatePayload = lastCall.args[2];
|
|
||||||
expect(updatePayload.updatedAt instanceof Date).toBeTruthy();
|
|
||||||
// remove the updatedAt as not testable
|
|
||||||
delete updatePayload.updatedAt;
|
|
||||||
|
|
||||||
expect(lastCall.args[2]).toEqual({
|
expect(lastCall.args[2]).toEqual({
|
||||||
numSent: { __op: 'Increment', amount: 1 },
|
numSent: { __op: 'Increment', amount: 1 },
|
||||||
numFailed: { __op: 'Increment', amount: 1 },
|
numFailed: { __op: 'Increment', amount: 1 },
|
||||||
|
|||||||
@@ -308,6 +308,19 @@ DatabaseController.prototype.update = function(className, query, update, {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function expandResultOnKeyPath(object, key, value) {
|
||||||
|
if (key.indexOf('.') < 0) {
|
||||||
|
object[key] = value[key];
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
const path = key.split('.');
|
||||||
|
const firstKey = path[0];
|
||||||
|
const nextPath = path.slice(1).join('.');
|
||||||
|
object[firstKey] = expandResultOnKeyPath(object[firstKey] || {}, nextPath, value[firstKey]);
|
||||||
|
delete object[key];
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
function sanitizeDatabaseResult(originalObject, result) {
|
function sanitizeDatabaseResult(originalObject, result) {
|
||||||
const response = {};
|
const response = {};
|
||||||
if (!result) {
|
if (!result) {
|
||||||
@@ -319,7 +332,8 @@ function sanitizeDatabaseResult(originalObject, result) {
|
|||||||
if (keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op
|
if (keyUpdate && typeof keyUpdate === 'object' && keyUpdate.__op
|
||||||
&& ['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1) {
|
&& ['Add', 'AddUnique', 'Remove', 'Increment'].indexOf(keyUpdate.__op) > -1) {
|
||||||
// only valid ops that produce an actionable result
|
// only valid ops that produce an actionable result
|
||||||
response[key] = result[key];
|
// the op may have happend on a keypath
|
||||||
|
expandResultOnKeyPath(response, key, result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export class PushWorker {
|
|||||||
const auth = master(config);
|
const auth = master(config);
|
||||||
const where = utils.applyDeviceTokenExists(query.where);
|
const where = utils.applyDeviceTokenExists(query.where);
|
||||||
delete query.where;
|
delete query.where;
|
||||||
|
pushStatus = pushStatusHandler(config, pushStatus.objectId);
|
||||||
return rest.find(config, auth, '_Installation', where, query).then(({results}) => {
|
return rest.find(config, auth, '_Installation', where, query).then(({results}) => {
|
||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -63,7 +64,6 @@ export class PushWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendToAdapter(body: any, installations: any[], pushStatus: any, config: Config, UTCOffset: ?any): Promise<*> {
|
sendToAdapter(body: any, installations: any[], pushStatus: any, config: Config, UTCOffset: ?any): Promise<*> {
|
||||||
pushStatus = pushStatusHandler(config, pushStatus.objectId);
|
|
||||||
// Check if we have locales in the push body
|
// Check if we have locales in the push body
|
||||||
const locales = utils.getLocalesFromPush(body);
|
const locales = utils.getLocalesFromPush(body);
|
||||||
if (locales.length > 0) {
|
if (locales.length > 0) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { md5Hash, newObjectId } from './cryptoUtils';
|
import { md5Hash, newObjectId } from './cryptoUtils';
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
|
import Parse from 'parse/node';
|
||||||
|
|
||||||
const PUSH_STATUS_COLLECTION = '_PushStatus';
|
const PUSH_STATUS_COLLECTION = '_PushStatus';
|
||||||
const JOB_STATUS_COLLECTION = '_JobStatus';
|
const JOB_STATUS_COLLECTION = '_JobStatus';
|
||||||
@@ -50,6 +51,47 @@ function statusHandler(className, database) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function restStatusHandler(className) {
|
||||||
|
let lastPromise = Promise.resolve();
|
||||||
|
|
||||||
|
function create(object) {
|
||||||
|
lastPromise = lastPromise.then(() => {
|
||||||
|
return Parse._request(
|
||||||
|
'POST',
|
||||||
|
`classes/${className}`,
|
||||||
|
object,
|
||||||
|
{ useMasterKey: true }
|
||||||
|
).then((result) => {
|
||||||
|
// merge the objects
|
||||||
|
const response = Object.assign({}, object, result);
|
||||||
|
return Promise.resolve(response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return lastPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(where, object) {
|
||||||
|
// TODO: when we have updateWhere, use that for proper interfacing
|
||||||
|
lastPromise = lastPromise.then(() => {
|
||||||
|
return Parse._request(
|
||||||
|
'PUT',
|
||||||
|
`classes/${className}/${where.objectId}`,
|
||||||
|
object,
|
||||||
|
{ useMasterKey: true }).then((result) => {
|
||||||
|
// merge the objects
|
||||||
|
const response = Object.assign({}, object, result);
|
||||||
|
return Promise.resolve(response);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return lastPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.freeze({
|
||||||
|
create,
|
||||||
|
update
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function jobStatusHandler(config) {
|
export function jobStatusHandler(config) {
|
||||||
let jobStatus;
|
let jobStatus;
|
||||||
const objectId = newObjectId(config.objectIdSize);
|
const objectId = newObjectId(config.objectIdSize);
|
||||||
@@ -103,11 +145,12 @@ export function jobStatusHandler(config) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pushStatusHandler(config, objectId = newObjectId(config.objectIdSize)) {
|
export function pushStatusHandler(config, existingObjectId) {
|
||||||
|
|
||||||
let pushStatus;
|
let pushStatus;
|
||||||
const database = config.database;
|
const database = config.database;
|
||||||
const handler = statusHandler(PUSH_STATUS_COLLECTION, database);
|
const handler = restStatusHandler(PUSH_STATUS_COLLECTION);
|
||||||
|
let objectId = existingObjectId;
|
||||||
const setInitial = function(body = {}, where, options = {source: 'rest'}) {
|
const setInitial = function(body = {}, where, options = {source: 'rest'}) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let pushTime = now.toISOString();
|
let pushTime = now.toISOString();
|
||||||
@@ -133,8 +176,6 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
|
|||||||
pushHash = 'd41d8cd98f00b204e9800998ecf8427e';
|
pushHash = 'd41d8cd98f00b204e9800998ecf8427e';
|
||||||
}
|
}
|
||||||
const object = {
|
const object = {
|
||||||
objectId,
|
|
||||||
createdAt: now,
|
|
||||||
pushTime,
|
pushTime,
|
||||||
query: JSON.stringify(where),
|
query: JSON.stringify(where),
|
||||||
payload: payloadString,
|
payload: payloadString,
|
||||||
@@ -148,7 +189,8 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
|
|||||||
ACL: {}
|
ACL: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.create(object).then(() => {
|
return handler.create(object).then((result) => {
|
||||||
|
objectId = result.objectId;
|
||||||
pushStatus = {
|
pushStatus = {
|
||||||
objectId
|
objectId
|
||||||
};
|
};
|
||||||
@@ -159,12 +201,11 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
|
|||||||
const setRunning = function(count) {
|
const setRunning = function(count) {
|
||||||
logger.verbose(`_PushStatus ${objectId}: sending push to %d installations`, count);
|
logger.verbose(`_PushStatus ${objectId}: sending push to %d installations`, count);
|
||||||
return handler.update({status:"pending", objectId: objectId},
|
return handler.update({status:"pending", objectId: objectId},
|
||||||
{status: "running", updatedAt: new Date(), count });
|
{status: "running", count });
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackSent = function(results, UTCOffset, cleanupInstallations = process.env.PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS) {
|
const trackSent = function(results, UTCOffset, cleanupInstallations = process.env.PARSE_SERVER_CLEANUP_INVALID_INSTALLATIONS) {
|
||||||
const update = {
|
const update = {
|
||||||
updatedAt: new Date(),
|
|
||||||
numSent: 0,
|
numSent: 0,
|
||||||
numFailed: 0
|
numFailed: 0
|
||||||
};
|
};
|
||||||
@@ -236,26 +277,33 @@ export function pushStatusHandler(config, objectId = newObjectId(config.objectId
|
|||||||
const complete = function() {
|
const complete = function() {
|
||||||
return handler.update({ objectId }, {
|
return handler.update({ objectId }, {
|
||||||
status: 'succeeded',
|
status: 'succeeded',
|
||||||
count: {__op: 'Delete'},
|
count: {__op: 'Delete'}
|
||||||
updatedAt: new Date()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fail = function(err) {
|
const fail = function(err) {
|
||||||
|
if (typeof err === 'string') {
|
||||||
|
err = { message: err };
|
||||||
|
}
|
||||||
const update = {
|
const update = {
|
||||||
errorMessage: JSON.stringify(err),
|
errorMessage: err,
|
||||||
status: 'failed',
|
status: 'failed'
|
||||||
updatedAt: new Date()
|
|
||||||
}
|
}
|
||||||
return handler.update({ objectId }, update);
|
return handler.update({ objectId }, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.freeze({
|
const rval = {
|
||||||
objectId,
|
|
||||||
setInitial,
|
setInitial,
|
||||||
setRunning,
|
setRunning,
|
||||||
trackSent,
|
trackSent,
|
||||||
complete,
|
complete,
|
||||||
fail
|
fail
|
||||||
})
|
};
|
||||||
|
|
||||||
|
// define objectId to be dynamic
|
||||||
|
Object.defineProperty(rval, "objectId", {
|
||||||
|
get: () => objectId
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.freeze(rval);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
import { Parse } from 'parse/node';
|
import { Parse } from 'parse/node';
|
||||||
import * as triggers from '../triggers';
|
import * as triggers from '../triggers';
|
||||||
|
|
||||||
function validateClassNameForTriggers(className) {
|
|
||||||
const restrictedClassNames = [ '_Session' ];
|
|
||||||
if (restrictedClassNames.indexOf(className) != -1) {
|
|
||||||
throw `Triggers are not supported for ${className} class.`;
|
|
||||||
}
|
|
||||||
return className;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getClassName(parseClass) {
|
function getClassName(parseClass) {
|
||||||
if (parseClass && parseClass.className) {
|
if (parseClass && parseClass.className) {
|
||||||
return validateClassNameForTriggers(parseClass.className);
|
return parseClass.className;
|
||||||
}
|
}
|
||||||
return validateClassNameForTriggers(parseClass);
|
return parseClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ParseCloud = {};
|
var ParseCloud = {};
|
||||||
|
|||||||
@@ -30,6 +30,20 @@ const baseStore = function() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function validateClassNameForTriggers(className, type) {
|
||||||
|
const restrictedClassNames = [ '_Session' ];
|
||||||
|
if (restrictedClassNames.indexOf(className) != -1) {
|
||||||
|
throw `Triggers are not supported for ${className} class.`;
|
||||||
|
}
|
||||||
|
if (type == Types.beforeSave && className === '_PushStatus') {
|
||||||
|
// _PushStatus uses undocumented nested key increment ops
|
||||||
|
// allowing beforeSave would mess up the objects big time
|
||||||
|
// TODO: Allow proper documented way of using nested increment ops
|
||||||
|
throw 'Only afterSave is allowed on _PushStatus';
|
||||||
|
}
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
const _triggerStore = {};
|
const _triggerStore = {};
|
||||||
|
|
||||||
export function addFunction(functionName, handler, validationHandler, applicationId) {
|
export function addFunction(functionName, handler, validationHandler, applicationId) {
|
||||||
@@ -46,6 +60,7 @@ export function addJob(jobName, handler, applicationId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function addTrigger(type, className, handler, applicationId) {
|
export function addTrigger(type, className, handler, applicationId) {
|
||||||
|
validateClassNameForTriggers(className, type);
|
||||||
applicationId = applicationId || Parse.applicationId;
|
applicationId = applicationId || Parse.applicationId;
|
||||||
_triggerStore[applicationId] = _triggerStore[applicationId] || baseStore();
|
_triggerStore[applicationId] = _triggerStore[applicationId] || baseStore();
|
||||||
_triggerStore[applicationId].Triggers[type][className] = handler;
|
_triggerStore[applicationId].Triggers[type][className] = handler;
|
||||||
|
|||||||
Reference in New Issue
Block a user