Browse Source

feat: hook apis

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5444/head
Pranav C 2 years ago
parent
commit
122f9b69c9
  1. 3
      packages/nocodb-nest/src/app.module.ts
  2. 20
      packages/nocodb-nest/src/modules/hooks/hooks.controller.spec.ts
  3. 112
      packages/nocodb-nest/src/modules/hooks/hooks.controller.ts
  4. 9
      packages/nocodb-nest/src/modules/hooks/hooks.module.ts
  5. 18
      packages/nocodb-nest/src/modules/hooks/hooks.service.spec.ts
  6. 119
      packages/nocodb-nest/src/modules/hooks/hooks.service.ts

3
packages/nocodb-nest/src/app.module.ts

@ -16,9 +16,10 @@ import { SortsModule } from './modules/sorts/sorts.module';
import { ColumnsModule } from './modules/columns/columns.module'; import { ColumnsModule } from './modules/columns/columns.module';
import { ViewColumnsModule } from './modules/view-columns/view-columns.module'; import { ViewColumnsModule } from './modules/view-columns/view-columns.module';
import { BasesModule } from './modules/bases/bases.module'; import { BasesModule } from './modules/bases/bases.module';
import { HooksModule } from './modules/hooks/hooks.module';
@Module({ @Module({
imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule], imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule],
controllers: [], controllers: [],
providers: [Connection, MetaService, JwtStrategy, ExtractProjectIdMiddleware], providers: [Connection, MetaService, JwtStrategy, ExtractProjectIdMiddleware],
exports: [Connection, MetaService], exports: [Connection, MetaService],

20
packages/nocodb-nest/src/modules/hooks/hooks.controller.spec.ts

@ -0,0 +1,20 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HooksController } from './hooks.controller';
import { HooksService } from './hooks.service';
describe('HooksController', () => {
let controller: HooksController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [HooksController],
providers: [HooksService],
}).compile();
controller = module.get<HooksController>(HooksController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

112
packages/nocodb-nest/src/modules/hooks/hooks.controller.ts

@ -0,0 +1,112 @@
import {
Body,
Controller,
Request,
Delete,
Get,
Param,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { HookReqType, HookTestReqType, HookType } from 'nocodb-sdk';
import { PagedResponseImpl } from '../../helpers/PagedResponse';
import {
Acl,
ExtractProjectIdMiddleware,
} from '../../middlewares/extract-project-id/extract-project-id.middleware';
import { HooksService } from './hooks.service';
import { AuthGuard } from '@nestjs/passport';
@Controller('hooks')
@UseGuards(ExtractProjectIdMiddleware, AuthGuard('jwt'))
export class HooksController {
constructor(private readonly hooksService: HooksService) {}
@Get('/api/v1/db/meta/tables/:tableId/hooks')
@Acl('hookList')
async hookList(@Param('tableId') tableId: string) {
return new PagedResponseImpl(await this.hooksService.hookList({ tableId }));
}
@Post('/api/v1/db/meta/tables/:tableId/hooks')
@Acl('hookCreate')
async hookCreate(
@Param('tableId') tableId: string,
@Body() body: HookReqType,
) {
const hook = await this.hooksService.hookCreate({
hook: body,
tableId,
});
return hook;
}
@Delete('/api/v1/db/meta/hooks/:hookId')
@Acl('hookDelete')
async hookDelete(@Param('hookId') hookId: string) {
return await this.hooksService.hookDelete({ hookId });
}
@Patch('/api/v1/db/meta/hooks/:hookId')
@Acl('hookUpdate')
async hookUpdate(@Param('hookId') hookId: string, @Body() body: HookReqType) {
return;
await this.hooksService.hookUpdate({ hookId, hook: body });
}
@Post('/api/v1/db/meta/tables/:tableId/hooks/test')
@Acl('hookTest')
async hookTest(@Body() body: HookTestReqType, @Request() req: any) {
try {
await this.hooksService.hookTest({
hookTest: {
...body,
payload: {
...body.payload,
user: (req as any)?.user,
},
},
tableId: req.params.tableId,
});
return { msg: 'The hook has been tested successfully' };
} catch (e) {
console.error(e);
throw e;
}
}
@Get(
'/api/v1/db/meta/tables/:tableId/hooks/samplePayload/:operation/:version',
)
@Acl('tableSampleData')
async tableSampleData(
@Param('tableId') tableId: string,
@Param('operation') operation: HookType['operation'],
@Param('version') version: HookType['version'],
) {
return;
await this.hooksService.tableSampleData({
tableId,
operation,
version,
});
}
@Get('/api/v1/db/meta/hooks/:hookId/logs')
@Acl('hookLogList')
async hookLogList(@Param('hookId') hookId: string, @Request() req: any) {
return new PagedResponseImpl(
await this.hooksService.hookLogList({
query: req.query,
hookId,
}),
{
...req.query,
count: await this.hooksService.hookLogCount({
hookId,
}),
},
);
}
}

9
packages/nocodb-nest/src/modules/hooks/hooks.module.ts

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { HooksService } from './hooks.service';
import { HooksController } from './hooks.controller';
@Module({
controllers: [HooksController],
providers: [HooksService]
})
export class HooksModule {}

18
packages/nocodb-nest/src/modules/hooks/hooks.service.spec.ts

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HooksService } from './hooks.service';
describe('HooksService', () => {
let service: HooksService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [HooksService],
}).compile();
service = module.get<HooksService>(HooksService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

119
packages/nocodb-nest/src/modules/hooks/hooks.service.ts

@ -0,0 +1,119 @@
import { Injectable } from '@nestjs/common';
import { HookReqType, HookTestReqType, HookType } from 'nocodb-sdk';
import { validatePayload } from '../../helpers';
import { NcError } from '../../helpers/catchError';
import {
populateSamplePayload,
populateSamplePayloadV2,
} from '../../helpers/populateSamplePayload';
import { invokeWebhook } from '../../helpers/webhookHelpers';
import { Hook, HookLog, Model } from '../../models';
import { T } from 'nc-help';
@Injectable()
export class HooksService {
validateHookPayload(notificationJsonOrObject: string | Record<string, any>) {
let notification: { type?: string } = {};
try {
notification =
typeof notificationJsonOrObject === 'string'
? JSON.parse(notificationJsonOrObject)
: notificationJsonOrObject;
} catch {}
if (notification.type !== 'URL' && process.env.NC_CLOUD === 'true') {
NcError.badRequest('Only URL notification is supported');
}
}
async hookList(param: { tableId: string }) {
return await Hook.list({ fk_model_id: param.tableId });
}
async hookLogList(param: { query: any; hookId: string }) {
return await HookLog.list({ fk_hook_id: param.hookId }, param.query);
}
async hookCreate(param: { tableId: string; hook: HookReqType }) {
validatePayload('swagger.json#/components/schemas/HookReq', param.hook);
this.validateHookPayload(param.hook.notification);
const hook = await Hook.insert({
...param.hook,
fk_model_id: param.tableId,
} as any);
T.emit('evt', { evt_type: 'webhooks:created' });
return hook;
}
async hookDelete(param: { hookId: string }) {
T.emit('evt', { evt_type: 'webhooks:deleted' });
await Hook.delete(param.hookId);
return true;
}
async hookUpdate(param: { hookId: string; hook: HookReqType }) {
validatePayload('swagger.json#/components/schemas/HookReq', param.hook);
T.emit('evt', { evt_type: 'webhooks:updated' });
this.validateHookPayload(param.hook.notification);
return await Hook.update(param.hookId, param.hook);
}
async hookTest(param: { tableId: string; hookTest: HookTestReqType }) {
validatePayload(
'swagger.json#/components/schemas/HookTestReq',
param.hookTest,
);
this.validateHookPayload(param.hookTest.hook?.notification);
const model = await Model.getByIdOrName({ id: param.tableId });
T.emit('evt', { evt_type: 'webhooks:tested' });
const {
hook,
payload: { data, user },
} = param.hookTest;
try {
await invokeWebhook(
new Hook(hook),
model,
null,
null,
data,
user,
(hook as any)?.filters,
true,
true,
);
} catch (e) {
throw e;
}
return true;
}
async tableSampleData(param: {
tableId: string;
operation: HookType['operation'];
version: HookType['version'];
}) {
const model = await Model.getByIdOrName({ id: param.tableId });
if (param.version === 'v1') {
return await populateSamplePayload(model, false, param.operation);
}
return await populateSamplePayloadV2(model, false, param.operation);
}
async hookLogCount(param: { hookId: string }) {
return await HookLog.count({ hookId: param.hookId });
}
}
Loading…
Cancel
Save