Skip to content

fix: Parse.Hooks requests have double forward slash in URL #2441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/ParseHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function remove(hook: HookDeleteArg) {

const DefaultController = {
get(type: string, functionName?: string, triggerName?: string) {
let url = '/hooks/' + type;
let url = 'hooks/' + type;
if (functionName) {
url += '/' + functionName;
if (triggerName) {
Expand All @@ -74,9 +74,9 @@ const DefaultController = {
create(hook) {
let url;
if (hook.functionName && hook.url) {
url = '/hooks/functions';
url = 'hooks/functions';
} else if (hook.className && hook.triggerName && hook.url) {
url = '/hooks/triggers';
url = 'hooks/triggers';
} else {
return Promise.reject({ error: 'invalid hook declaration', code: 143 });
}
Expand All @@ -86,10 +86,10 @@ const DefaultController = {
remove(hook) {
let url;
if (hook.functionName) {
url = '/hooks/functions/' + hook.functionName;
url = 'hooks/functions/' + hook.functionName;
delete hook.functionName;
} else if (hook.className && hook.triggerName) {
url = '/hooks/triggers/' + hook.className + '/' + hook.triggerName;
url = 'hooks/triggers/' + hook.className + '/' + hook.triggerName;
delete hook.className;
delete hook.triggerName;
} else {
Expand All @@ -101,10 +101,10 @@ const DefaultController = {
update(hook) {
let url;
if (hook.functionName && hook.url) {
url = '/hooks/functions/' + hook.functionName;
url = 'hooks/functions/' + hook.functionName;
delete hook.functionName;
} else if (hook.className && hook.triggerName && hook.url) {
url = '/hooks/triggers/' + hook.className + '/' + hook.triggerName;
url = 'hooks/triggers/' + hook.className + '/' + hook.triggerName;
delete hook.className;
delete hook.triggerName;
} else {
Expand Down
135 changes: 100 additions & 35 deletions src/__tests__/Hooks-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,27 @@ jest.dontMock('../decode');
jest.dontMock('../encode');
jest.dontMock('../ParseError');
jest.dontMock('../ParseObject');
jest.dontMock('../RESTController');

const Hooks = require('../ParseHooks');
const CoreManager = require('../CoreManager');
const RESTController = require('../RESTController');

const defaultController = CoreManager.getHooksController();
const { sendRequest } = defaultController;

CoreManager.setInstallationController({
currentInstallationId() {
return Promise.resolve('iid');
},
currentInstallation() {},
updateInstallationOnDisk() {},
});
CoreManager.set('APPLICATION_ID', 'A');
CoreManager.set('JAVASCRIPT_KEY', 'B');
CoreManager.set('MASTER_KEY', 'C');
CoreManager.set('VERSION', 'V');

describe('Hooks', () => {
beforeEach(() => {
const run = jest.fn();
Expand All @@ -19,125 +33,176 @@ describe('Hooks', () => {
result: {},
})
);
const ajax = jest.fn();
ajax.mockReturnValue(
Promise.resolve({
response: {},
})
);
RESTController.ajax = ajax;
defaultController.sendRequest = run;
CoreManager.setHooksController(defaultController);
CoreManager.setRESTController(RESTController);
});

it('shoud properly build GET functions', () => {
it('should properly build GET functions', async () => {
Hooks.getFunctions();

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'GET',
'/hooks/functions',
'hooks/functions',
]);
defaultController.sendRequest = sendRequest;
await Hooks.getFunctions();
expect(RESTController.ajax.mock.calls[0][1]).toBe('https://api.parse.com/1/hooks/functions');
});

it('shoud properly build GET triggers', () => {
it('should properly build GET triggers', async () => {
Hooks.getTriggers();

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'GET',
'/hooks/triggers',
'hooks/triggers',
]);
defaultController.sendRequest = sendRequest;
await Hooks.getTriggers();
expect(RESTController.ajax.mock.calls[0][1]).toBe('https://api.parse.com/1/hooks/triggers');
});

it('shoud properly build GET function', () => {
it('should properly build GET function', async () => {
Hooks.getFunction('functionName');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'GET',
'/hooks/functions/functionName',
'hooks/functions/functionName',
]);
defaultController.sendRequest = sendRequest;
await Hooks.getFunction('functionName');
expect(RESTController.ajax.mock.calls[0][1]).toBe(
'https://api.parse.com/1/hooks/functions/functionName'
);
});

it('shoud properly build GET trigger', () => {
it('should properly build GET trigger', async () => {
Hooks.getTrigger('MyClass', 'beforeSave');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'GET',
'/hooks/triggers/MyClass/beforeSave',
'hooks/triggers/MyClass/beforeSave',
]);
defaultController.sendRequest = sendRequest;
await Hooks.getTrigger('MyClass', 'beforeSave');
expect(RESTController.ajax.mock.calls[0][1]).toBe(
'https://api.parse.com/1/hooks/triggers/MyClass/beforeSave'
);
});

it('shoud properly build POST function', () => {
Hooks.createFunction('myFunction', 'https://dummy.com');
it('should properly build POST function', async () => {
Hooks.createFunction('myFunction', 'https://example.com');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'POST',
'/hooks/functions',
'hooks/functions',
{
functionName: 'myFunction',
url: 'https://dummy.com',
url: 'https://example.com',
},
]);
defaultController.sendRequest = sendRequest;
await Hooks.createFunction('myFunction', 'https://example.com');
expect(RESTController.ajax.mock.calls[0][1]).toBe('https://api.parse.com/1/hooks/functions');
});

it('shoud properly build POST trigger', () => {
Hooks.createTrigger('MyClass', 'beforeSave', 'https://dummy.com');
it('should properly build POST trigger', async () => {
Hooks.createTrigger('MyClass', 'beforeSave', 'https://example.com');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'POST',
'/hooks/triggers',
'hooks/triggers',
{
className: 'MyClass',
triggerName: 'beforeSave',
url: 'https://dummy.com',
url: 'https://example.com',
},
]);
defaultController.sendRequest = sendRequest;
await Hooks.createTrigger('MyClass', 'beforeSave', 'https://example.com');
expect(RESTController.ajax.mock.calls[0][1]).toBe('https://api.parse.com/1/hooks/triggers');
});

it('shoud properly build PUT function', () => {
Hooks.updateFunction('myFunction', 'https://dummy.com');
it('should properly build PUT function', async () => {
Hooks.updateFunction('myFunction', 'https://example.com');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'PUT',
'/hooks/functions/myFunction',
'hooks/functions/myFunction',
{
url: 'https://dummy.com',
url: 'https://example.com',
},
]);
defaultController.sendRequest = sendRequest;
await Hooks.updateFunction('myFunction', 'https://example.com');
expect(RESTController.ajax.mock.calls[0][1]).toBe(
'https://api.parse.com/1/hooks/functions/myFunction'
);
});

it('shoud properly build PUT trigger', () => {
Hooks.updateTrigger('MyClass', 'beforeSave', 'https://dummy.com');
it('should properly build PUT trigger', async () => {
Hooks.updateTrigger('MyClass', 'beforeSave', 'https://example.com');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'PUT',
'/hooks/triggers/MyClass/beforeSave',
'hooks/triggers/MyClass/beforeSave',
{
url: 'https://dummy.com',
url: 'https://example.com',
},
]);
defaultController.sendRequest = sendRequest;
await Hooks.updateTrigger('MyClass', 'beforeSave', 'https://example.com');
expect(RESTController.ajax.mock.calls[0][1]).toBe(
'https://api.parse.com/1/hooks/triggers/MyClass/beforeSave'
);
});

it('shoud properly build removeFunction', () => {
it('should properly build removeFunction', async () => {
Hooks.removeFunction('myFunction');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'PUT',
'/hooks/functions/myFunction',
'hooks/functions/myFunction',
{ __op: 'Delete' },
]);

defaultController.sendRequest = sendRequest;
await Hooks.removeFunction('myFunction');
expect(RESTController.ajax.mock.calls[0][1]).toBe(
'https://api.parse.com/1/hooks/functions/myFunction'
);
});

it('shoud properly build removeTrigger', () => {
it('should properly build removeTrigger', async () => {
Hooks.removeTrigger('MyClass', 'beforeSave');

expect(CoreManager.getHooksController().sendRequest.mock.calls[0]).toEqual([
'PUT',
'/hooks/triggers/MyClass/beforeSave',
'hooks/triggers/MyClass/beforeSave',
{ __op: 'Delete' },
]);
defaultController.sendRequest = sendRequest;
await Hooks.removeTrigger('MyClass', 'beforeSave');
expect(RESTController.ajax.mock.calls[0][1]).toBe(
'https://api.parse.com/1/hooks/triggers/MyClass/beforeSave'
);
});

it('shoud throw invalid create', async () => {
it('should throw invalid create', async () => {
expect.assertions(10);
const p1 = Hooks.create({ functionName: 'myFunction' }).catch(err => {
expect(err.code).toBe(143);
expect(err.error).toBe('invalid hook declaration');
});

const p2 = Hooks.create({ url: 'http://dummy.com' }).catch(err => {
const p2 = Hooks.create({ url: 'http://example.com' }).catch(err => {
expect(err.code).toBe(143);
expect(err.error).toBe('invalid hook declaration');
});
Expand All @@ -147,7 +212,7 @@ describe('Hooks', () => {
expect(err.error).toBe('invalid hook declaration');
});

const p4 = Hooks.create({ className: 'MyClass', url: 'http://dummy.com' }).catch(err => {
const p4 = Hooks.create({ className: 'MyClass', url: 'http://example.com' }).catch(err => {
expect(err.code).toBe(143);
expect(err.error).toBe('invalid hook declaration');
});
Expand All @@ -160,7 +225,7 @@ describe('Hooks', () => {
await Promise.all([p1, p2, p3, p4, p5]);
});

it('shoud throw invalid update', async () => {
it('should throw invalid update', async () => {
expect.assertions(6);
const p1 = Hooks.update({ functionssName: 'myFunction' }).catch(err => {
expect(err.code).toBe(143);
Expand All @@ -172,14 +237,14 @@ describe('Hooks', () => {
expect(err.error).toBe('invalid hook declaration');
});

const p3 = Hooks.update({ className: 'MyClass', url: 'http://dummy.com' }).catch(err => {
const p3 = Hooks.update({ className: 'MyClass', url: 'http://example.com' }).catch(err => {
expect(err.code).toBe(143);
expect(err.error).toBe('invalid hook declaration');
});
await Promise.all([p1, p2, p3]);
});

it('shoud throw invalid remove', async () => {
it('should throw invalid remove', async () => {
expect.assertions(6);
const p1 = Hooks.remove({ functionssName: 'myFunction' }).catch(err => {
expect(err.code).toBe(143);
Expand All @@ -191,7 +256,7 @@ describe('Hooks', () => {
expect(err.error).toBe('invalid hook declaration');
});

const p3 = Hooks.remove({ className: 'MyClass', url: 'http://dummy.com' }).catch(err => {
const p3 = Hooks.remove({ className: 'MyClass', url: 'http://example.com' }).catch(err => {
expect(err.code).toBe(143);
expect(err.error).toBe('invalid hook declaration');
});
Expand Down