Skip to content

Commit

Permalink
Merge pull request #844 from ParsePlatform/nlutsenko.hooks
Browse files Browse the repository at this point in the history
Move HooksController to use MongoCollection instead of direct Mongo access.
  • Loading branch information
nlutsenko committed Mar 7, 2016
2 parents 75ae958 + 172da3a commit f1f9bde
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 99 deletions.
15 changes: 12 additions & 3 deletions src/Adapters/Storage/Mongo/MongoCollection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

let mongodb = require('mongodb');
let Collection = mongodb.Collection;

Expand All @@ -18,8 +17,7 @@ export default class MongoCollection {
return this._rawFind(query, { skip, limit, sort })
.catch(error => {
// Check for "no geoindex" error
if (error.code != 17007 ||
!error.message.match(/unable to find index for .geoNear/)) {
if (error.code != 17007 || !error.message.match(/unable to find index for .geoNear/)) {
throw error;
}
// Figure out what key needs an index
Expand Down Expand Up @@ -59,6 +57,13 @@ export default class MongoCollection {
})
}

// Atomically updates data in the database for a single (first) object that matched the query
// If there is nothing that matches the query - does insert
// Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5.
upsertOne(query, update) {
return this._mongoCollection.update(query, update, { upsert: true });
}

// Atomically find and delete an object based on query.
// The result is the promise with an object that was in the database before deleting.
// Postgres Note: Translates directly to `DELETE * FROM ... RETURNING *`, which will return data after delete is done.
Expand All @@ -70,6 +75,10 @@ export default class MongoCollection {
});
}

remove(query) {
return this._mongoCollection.remove(query);
}

drop() {
return this._mongoCollection.drop();
}
Expand Down
170 changes: 74 additions & 96 deletions src/Controllers/HooksController.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,104 +8,91 @@ import * as request from "request";
const DefaultHooksCollectionName = "_Hooks";

export class HooksController {
_applicationId: string;
_collectionPrefix: string;
_applicationId:string;
_collectionPrefix:string;
_collection;

constructor(applicationId: string, collectionPrefix: string = '') {
constructor(applicationId:string, collectionPrefix:string = '') {
this._applicationId = applicationId;
this._collectionPrefix = collectionPrefix;
}

database() {
return DatabaseAdapter.getDatabaseConnection(this._applicationId, this._collectionPrefix);

load() {
return this._getHooks().then(hooks => {
hooks = hooks || [];
hooks.forEach((hook) => {
this.addHookToTriggers(hook);
});
});
}
collection() {

getCollection() {
if (this._collection) {
return Promise.resolve(this._collection)
}
return this.database().rawCollection(DefaultHooksCollectionName).then((collection) => {

let database = DatabaseAdapter.getDatabaseConnection(this._applicationId, this._collectionPrefix);
return database.adaptiveCollection(DefaultHooksCollectionName).then(collection => {
this._collection = collection;
return collection;
});
}

getFunction(functionName) {
return this.getOne({functionName: functionName})
return this._getHooks({ functionName: functionName }, 1).then(results => results[0]);
}

getFunctions() {
return this.get({functionName: { $exists: true }})
return this._getHooks({ functionName: { $exists: true } });
}

getTrigger(className, triggerName) {
return this.getOne({className: className, triggerName: triggerName })
return this._getHooks({ className: className, triggerName: triggerName }, 1).then(results => results[0]);
}

getTriggers() {
return this.get({className: { $exists: true }, triggerName: { $exists: true }})
return this._getHooks({ className: { $exists: true }, triggerName: { $exists: true } });
}

deleteFunction(functionName) {
triggers.removeFunction(functionName, this._applicationId);
return this.delete({functionName: functionName});
return this._removeHooks({ functionName: functionName });
}

deleteTrigger(className, triggerName) {
triggers.removeTrigger(triggerName, className, this._applicationId);
return this.delete({className: className, triggerName: triggerName});
}

delete(query) {
return this.collection().then((collection) => {
return collection.remove(query)
}).then( (res) => {
return {};
}, 1);
return this._removeHooks({ className: className, triggerName: triggerName });
}

getOne(query) {
return this.collection()
.then(coll => coll.findOne(query, {_id: 0}))
.then(hook => {
return hook;
});

_getHooks(query, limit) {
let options = limit ? { limit: limit } : undefined;
return this.getCollection().then(collection => collection.find(query, options));
}
get(query) {
return this.collection()
.then(coll => coll.find(query, {_id: 0}).toArray())
.then(hooks => {
return hooks;

_removeHooks(query) {
return this.getCollection().then(collection => {
return collection.remove(query);
}).then(() => {
return {};
});
}

getHooks() {
return this.collection()
.then(coll => coll.find({}, {_id: 0}).toArray())
.then(hooks => {
return hooks;
}, () => ([]))
}


saveHook(hook) {

var query;
if (hook.functionName && hook.url) {
query = {functionName: hook.functionName }
query = { functionName: hook.functionName }
} else if (hook.triggerName && hook.className && hook.url) {
query = { className: hook.className, triggerName: hook.triggerName }
} else {
throw new Parse.Error(143, "invalid hook declaration");
}
return this.collection().then((collection) => {
return collection.update(query, hook, {upsert: true})
}).then(function(res){
return hook;
})
return this.getCollection()
.then(collection => collection.upsertOne(query, hook))
.then(() => {
return hook;
});
}

addHookToTriggers(hook) {
var wrappedFunction = wrapToHTTPRequest(hook);
wrappedFunction.url = hook.url;
Expand All @@ -114,13 +101,13 @@ export class HooksController {
} else {
triggers.addFunction(hook.functionName, wrappedFunction, null, this._applicationId);
}
}
}

addHook(hook) {
this.addHookToTriggers(hook);
return this.saveHook(hook);
}

createOrUpdateHook(aHook) {
var hook;
if (aHook && aHook.functionName && aHook.url) {
Expand All @@ -132,69 +119,59 @@ export class HooksController {
hook.className = aHook.className;
hook.url = aHook.url;
hook.triggerName = aHook.triggerName;

} else {
throw new Parse.Error(143, "invalid hook declaration");
}
}

return this.addHook(hook);
};

createHook(aHook) {
if (aHook.functionName) {
return this.getFunction(aHook.functionName).then((result) => {
if (result) {
throw new Parse.Error(143,`function name: ${aHook.functionName} already exits`);
throw new Parse.Error(143, `function name: ${aHook.functionName} already exits`);
} else {
return this.createOrUpdateHook(aHook);
}
});
} else if (aHook.className && aHook.triggerName) {
return this.getTrigger(aHook.className, aHook.triggerName).then((result) => {
if (result) {
throw new Parse.Error(143,`class ${aHook.className} already has trigger ${aHook.triggerName}`);
throw new Parse.Error(143, `class ${aHook.className} already has trigger ${aHook.triggerName}`);
}
return this.createOrUpdateHook(aHook);
});
}

throw new Parse.Error(143, "invalid hook declaration");
};

updateHook(aHook) {
if (aHook.functionName) {
return this.getFunction(aHook.functionName).then((result) => {
if (result) {
return this.createOrUpdateHook(aHook);
}
throw new Parse.Error(143,`no function named: ${aHook.functionName} is defined`);
throw new Parse.Error(143, `no function named: ${aHook.functionName} is defined`);
});
} else if (aHook.className && aHook.triggerName) {
return this.getTrigger(aHook.className, aHook.triggerName).then((result) => {
if (result) {
return this.createOrUpdateHook(aHook);
}
throw new Parse.Error(143,`class ${aHook.className} does not exist`);
throw new Parse.Error(143, `class ${aHook.className} does not exist`);
});
}
throw new Parse.Error(143, "invalid hook declaration");
};

load() {
return this.getHooks().then((hooks) => {
hooks = hooks || [];
hooks.forEach((hook) => {
this.addHookToTriggers(hook);
});
});
}

}

function wrapToHTTPRequest(hook) {
return function(req, res) {
var jsonBody = {};
for(var i in req) {
return (req, res) => {
let jsonBody = {};
for (var i in req) {
jsonBody[i] = req[i];
}
if (req.object) {
Expand All @@ -205,30 +182,31 @@ function wrapToHTTPRequest(hook) {
jsonBody.original = req.original.toJSON();
jsonBody.original.className = req.original.className;
}
var jsonRequest = {};
jsonRequest.headers = {
'Content-Type': 'application/json'
}
jsonRequest.body = JSON.stringify(jsonBody);

request.post(hook.url, jsonRequest, function(err, httpResponse, body){
let jsonRequest = {
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(jsonBody)
};

request.post(hook.url, jsonRequest, function (err, httpResponse, body) {
var result;
if (body) {
if (typeof body == "string") {
try {
body = JSON.parse(body);
} catch(e) {
err = {error: "Malformed response", code: -1};
} catch (e) {
err = { error: "Malformed response", code: -1 };
}
}
if (!err) {
result = body.success;
err = body.error;
err = body.error;
}
}
if (err) {
return res.error(err);
} else {
} else {
return res.success(result);
}
});
Expand Down

0 comments on commit f1f9bde

Please sign in to comment.