Skip to content

Commit 31c5f1f

Browse files
committed
Adds Hooks API on parse/node
1 parent f17befd commit 31c5f1f

File tree

4 files changed

+351
-0
lines changed

4 files changed

+351
-0
lines changed

src/CoreManager.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ type UserController = {
108108
upgradeToRevocableSession: (user: ParseUser, options: RequestOptions) => ParsePromise;
109109
linkWith: (user: ParseUser, authData: AuthData) => ParsePromise;
110110
};
111+
type HooksController = {
112+
get: (type: string, functionName?: string, triggerName?: string) => ParsePromise;
113+
create: (hook: mixed) => ParsePromise;
114+
delete: (hook: mixed) => ParsePromise;
115+
update: (hook: mixed) => ParsePromise;
116+
send: (method: string, path: string, body?: mixed) => ParsePromise;
117+
}
111118

112119
var config: { [key: string]: mixed } = {
113120
// Defaults
@@ -476,5 +483,20 @@ module.exports = {
476483

477484
getLiveQueryController(): any {
478485
return config['LiveQueryController'];
486+
},
487+
488+
setHooksController(controller: HooksController) {
489+
['create', 'get', 'update', 'remove'].forEach((func) => {
490+
if (typeof controller[func] !== 'function') {
491+
throw new Error(
492+
`A HooksController must implement ${func}()`
493+
);
494+
}
495+
})
496+
config['HooksController'] = controller;
497+
},
498+
499+
getHooksController(): HooksController {
500+
return config['HooksController'];
479501
}
480502
}

src/Parse.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ if (process.env.PARSE_BUILD === 'node') {
147147
Parse.Cloud.useMasterKey = function() {
148148
CoreManager.set('USE_MASTER_KEY', true);
149149
}
150+
Parse.Hooks = require('./ParseHooks');
150151
}
151152

152153
// For legacy requires, of the form `var Parse = require('parse').Parse`

src/ParseHooks.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import CoreManager from './CoreManager';
2+
import decode from './decode';
3+
import encode from './encode';
4+
import ParseError from './ParseError';
5+
import ParsePromise from './ParsePromise';
6+
7+
export function getFunctions() {
8+
return CoreManager.getHooksController().get("functions");
9+
}
10+
11+
export function getTriggers() {
12+
return CoreManager.getHooksController().get("triggers");
13+
}
14+
15+
export function getFunction(name) {
16+
return CoreManager.getHooksController().get("functions", name);
17+
}
18+
19+
export function getTrigger(className, triggerName) {
20+
return CoreManager.getHooksController().get("triggers", className, triggerName);
21+
}
22+
23+
export function createFunction(functionName, url) {
24+
return create({functionName: functionName, url: url});
25+
}
26+
27+
export function createTrigger(className, triggerName, url) {
28+
return create({className: className, triggerName: triggerName, url: url});
29+
}
30+
31+
export function create(hook) {
32+
return CoreManager.getHooksController().create(hook);
33+
}
34+
35+
export function updateFunction(functionName, url) {
36+
return update({functionName: functionName, url: url});
37+
}
38+
39+
export function updateTrigger(className, triggerName, url) {
40+
return update({className: className, triggerName: triggerName, url: url});
41+
}
42+
43+
export function update(hook) {
44+
return CoreManager.getHooksController().update(hook);
45+
}
46+
47+
export function removeFunction(functionName) {
48+
return remove({functionName: functionName});
49+
}
50+
51+
export function removeTrigger(className, triggerName) {
52+
return remove({className: className, triggerName: triggerName});
53+
}
54+
55+
export function remove(hook) {
56+
return CoreManager.getHooksController().remove(hook);
57+
}
58+
59+
var DefaultController = {
60+
61+
get(type, functionName, triggerName) {
62+
var url = "/hooks/"+type;
63+
if(functionName) {
64+
url += "/"+functionName;
65+
if (triggerName) {
66+
url += "/"+triggerName;
67+
}
68+
}
69+
return this.sendRequest("GET", url);
70+
},
71+
72+
create(hook) {
73+
var url;
74+
if (hook.functionName && hook.url) {
75+
url = "/hooks/functions";
76+
} else if (hook.className && hook.triggerName && hook.url) {
77+
url = "/hooks/triggers";
78+
} else {
79+
return Promise.reject({error: 'invalid hook declaration', code: 143});
80+
}
81+
return this.sendRequest("POST", url, hook);
82+
},
83+
84+
remove(hook) {
85+
var url;
86+
if (hook.functionName) {
87+
url = "/hooks/functions/"+hook.functionName;
88+
delete hook.functionName;
89+
} else if (hook.className && hook.triggerName) {
90+
url = "/hooks/triggers/"+hook.className+"/"+hook.triggerName;
91+
delete hook.className;
92+
delete hook.triggerName;
93+
} else {
94+
return Promise.reject({error: 'invalid hook declaration', code: 143});
95+
}
96+
return this.sendRequest("PUT", url, { "__op": "Delete" });
97+
},
98+
99+
update(hook) {
100+
var url;
101+
if (hook.functionName && hook.url) {
102+
url = "/hooks/functions/"+hook.functionName;
103+
delete hook.functionName;
104+
} else if (hook.className && hook.triggerName && hook.url) {
105+
url = "/hooks/triggers/"+hook.className+"/"+hook.triggerName;
106+
delete hook.className;
107+
delete hook.triggerName;
108+
} else {
109+
return Promise.reject({error: 'invalid hook declaration', code: 143});
110+
}
111+
return this.sendRequest('PUT', url, hook);
112+
},
113+
114+
sendRequest(method, url, body) {
115+
return CoreManager.getRESTController().request(method, url, body, {useMasterKey: true}).then((res) => {
116+
var decoded = decode(res);
117+
if (decoded) {
118+
return ParsePromise.as(decoded);
119+
}
120+
return ParsePromise.error(
121+
new ParseError(
122+
ParseError.INVALID_JSON,
123+
'The server returned an invalid response.'
124+
)
125+
);
126+
})
127+
}
128+
};
129+
130+
CoreManager.setHooksController(DefaultController);

src/__tests__/Hooks-test.js

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/**
2+
* Copyright (c) 2015-present, Parse, LLC.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
jest.dontMock('../ParseHooks');
11+
jest.dontMock('../CoreManager');
12+
jest.dontMock('../decode');
13+
jest.dontMock('../encode');
14+
jest.dontMock('../ParsePromise');
15+
16+
var Hooks = require('../ParseHooks');
17+
var CoreManager = require('../CoreManager');
18+
var ParsePromise = require('../ParsePromise');
19+
20+
var defaultController = CoreManager.getHooksController();
21+
22+
describe('Hooks', () => {
23+
beforeEach(() => {
24+
var run = jest.genMockFunction();
25+
run.mockReturnValue(ParsePromise.as({
26+
result: {}
27+
}));
28+
defaultController.sendRequest = run;
29+
CoreManager.setHooksController(defaultController);
30+
});
31+
32+
it('shoud properly build GET functions', () => {
33+
Hooks.getFunctions();
34+
35+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
36+
.toEqual(['GET', '/hooks/functions']);
37+
});
38+
39+
it('shoud properly build GET triggers', () => {
40+
Hooks.getTriggers();
41+
42+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
43+
.toEqual(['GET', '/hooks/triggers']);
44+
})
45+
46+
it('shoud properly build GET function', () => {
47+
Hooks.getFunction('functionName');
48+
49+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
50+
.toEqual(['GET', '/hooks/functions/functionName']);
51+
})
52+
53+
it('shoud properly build GET trigger', () => {
54+
Hooks.getTrigger('MyClass', 'beforeSave');
55+
56+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
57+
.toEqual(['GET', '/hooks/triggers/MyClass/beforeSave']);
58+
})
59+
60+
it('shoud properly build POST function', () => {
61+
Hooks.createFunction('myFunction', 'https://dummy.com');
62+
63+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
64+
.toEqual(['POST', '/hooks/functions', {
65+
functionName: 'myFunction',
66+
url: 'https://dummy.com'
67+
}]);
68+
})
69+
70+
it('shoud properly build POST trigger', () => {
71+
Hooks.createTrigger('MyClass', 'beforeSave', 'https://dummy.com');
72+
73+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
74+
.toEqual(['POST', '/hooks/triggers', {
75+
className: 'MyClass',
76+
triggerName: 'beforeSave',
77+
url: 'https://dummy.com'
78+
}]);
79+
})
80+
81+
it('shoud properly build PUT function', () => {
82+
Hooks.updateFunction('myFunction', 'https://dummy.com');
83+
84+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
85+
.toEqual(['PUT', '/hooks/functions/myFunction', {
86+
url: 'https://dummy.com'
87+
}]);
88+
})
89+
90+
it('shoud properly build PUT trigger', () => {
91+
Hooks.updateTrigger('MyClass', 'beforeSave', 'https://dummy.com');
92+
93+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
94+
.toEqual(['PUT', '/hooks/triggers/MyClass/beforeSave', {
95+
url: 'https://dummy.com'
96+
}]);
97+
})
98+
99+
100+
it('shoud properly build removeFunction', () => {
101+
Hooks.removeFunction('myFunction');
102+
103+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
104+
.toEqual(['PUT', '/hooks/functions/myFunction', { "__op": "Delete" }]);
105+
})
106+
107+
it('shoud properly build removeTrigger', () => {
108+
Hooks.removeTrigger('MyClass', 'beforeSave');
109+
110+
expect(CoreManager.getHooksController().sendRequest.mock.calls[0])
111+
.toEqual(['PUT', '/hooks/triggers/MyClass/beforeSave', { "__op": "Delete" }]);
112+
})
113+
114+
it('shoud throw invalid create', () => {
115+
Hooks.create({functionName: 'myFunction'}).then(() => {
116+
fail('should not succeed')
117+
}).catch((err) => {
118+
expect(err.code).toBe(143);
119+
expect(err.error).toBe('invalid hook declaration');
120+
});
121+
122+
Hooks.create({url: 'http://dummy.com'}).then(() => {
123+
fail('should not succeed')
124+
}).catch((err) => {
125+
expect(err.code).toBe(143);
126+
expect(err.error).toBe('invalid hook declaration');
127+
});
128+
129+
Hooks.create({className: 'MyClass'}).then(() => {
130+
fail('should not succeed')
131+
}).catch((err) => {
132+
expect(err.code).toBe(143);
133+
expect(err.error).toBe('invalid hook declaration');
134+
});
135+
136+
Hooks.create({className: 'MyClass', url: 'http://dummy.com'}).then(() => {
137+
fail('should not succeed')
138+
}).catch((err) => {
139+
expect(err.code).toBe(143);
140+
expect(err.error).toBe('invalid hook declaration');
141+
});
142+
143+
Hooks.create({className: 'MyClass', triggerName: 'beforeSave'}).then(() => {
144+
fail('should not succeed')
145+
}).catch((err) => {
146+
expect(err.code).toBe(143);
147+
expect(err.error).toBe('invalid hook declaration');
148+
});
149+
})
150+
151+
it('shoud throw invalid update', () => {
152+
Hooks.update({functionssName: 'myFunction'}).then(() => {
153+
fail('should not succeed')
154+
}).catch((err) => {
155+
expect(err.code).toBe(143);
156+
expect(err.error).toBe('invalid hook declaration');
157+
});
158+
159+
Hooks.update({className: 'MyClass'}).then(() => {
160+
fail('should not succeed')
161+
}).catch((err) => {
162+
expect(err.code).toBe(143);
163+
expect(err.error).toBe('invalid hook declaration');
164+
});
165+
166+
Hooks.update({className: 'MyClass', url: 'http://dummy.com'}).then(() => {
167+
fail('should not succeed')
168+
}).catch((err) => {
169+
expect(err.code).toBe(143);
170+
expect(err.error).toBe('invalid hook declaration');
171+
});
172+
})
173+
174+
it('shoud throw invalid remove', () => {
175+
Hooks.remove({functionssName: 'myFunction'}).then(() => {
176+
fail('should not succeed')
177+
}).catch((err) => {
178+
expect(err.code).toBe(143);
179+
expect(err.error).toBe('invalid hook declaration');
180+
});
181+
182+
Hooks.remove({className: 'MyClass'}).then(() => {
183+
fail('should not succeed')
184+
}).catch((err) => {
185+
expect(err.code).toBe(143);
186+
expect(err.error).toBe('invalid hook declaration');
187+
});
188+
189+
Hooks.remove({className: 'MyClass', url: 'http://dummy.com'}).then(() => {
190+
fail('should not succeed')
191+
}).catch((err) => {
192+
expect(err.code).toBe(143);
193+
expect(err.error).toBe('invalid hook declaration');
194+
});
195+
})
196+
197+
198+
});

0 commit comments

Comments
 (0)