diff --git a/packages/nocodb-nest/src/app.module.ts b/packages/nocodb-nest/src/app.module.ts index 7792c95a4d..1b6fda987d 100644 --- a/packages/nocodb-nest/src/app.module.ts +++ b/packages/nocodb-nest/src/app.module.ts @@ -36,9 +36,10 @@ import { OrgUsersModule } from './modules/org-users/org-users.module'; import { MetaDiffsModule } from './modules/meta-diffs/meta-diffs.module'; import { AuditsModule } from './modules/audits/audits.module'; import { DatasModule } from './modules/datas/datas.module'; +import { DataAliasModule } from './modules/data-alias/data-alias.module'; @Module({ - imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule, SharedBasesModule, FormsModule, GridsModule, KanbansModule, GalleriesModule, FormColumnsModule, GridColumnsModule, MapsModule, ProjectUsersModule, ModelVisibilitiesModule, HookFiltersModule, ApiTokensModule, AttachmentsModule, OrgLcenseModule, OrgTokensModule, OrgUsersModule, MetaDiffsModule, AuditsModule, DatasModule], + imports: [AuthModule, UsersModule, UtilsModule, ProjectsModule, TablesModule, ViewsModule, FiltersModule, SortsModule, ColumnsModule, ViewColumnsModule, BasesModule, HooksModule, SharedBasesModule, FormsModule, GridsModule, KanbansModule, GalleriesModule, FormColumnsModule, GridColumnsModule, MapsModule, ProjectUsersModule, ModelVisibilitiesModule, HookFiltersModule, ApiTokensModule, AttachmentsModule, OrgLcenseModule, OrgTokensModule, OrgUsersModule, MetaDiffsModule, AuditsModule, DatasModule, DataAliasModule], controllers: [], providers: [Connection, MetaService, JwtStrategy, ExtractProjectIdMiddleware], exports: [Connection, MetaService], diff --git a/packages/nocodb-nest/src/modules/datas/data-alias.controller.spec.ts b/packages/nocodb-nest/src/modules/datas/data-alias.controller.spec.ts new file mode 100644 index 0000000000..77e68818a2 --- /dev/null +++ b/packages/nocodb-nest/src/modules/datas/data-alias.controller.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { DataAliasController } from './data-alias.controller'; +import { DataAliasService } from '../data-alias/data-alias.service'; + +describe('DataAliasController', () => { + let controller: DataAliasController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [DataAliasController], + providers: [DataAliasService], + }).compile(); + + controller = module.get(DataAliasController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/packages/nocodb-nest/src/modules/datas/data-alias.controller.ts b/packages/nocodb-nest/src/modules/datas/data-alias.controller.ts new file mode 100644 index 0000000000..9b01664a00 --- /dev/null +++ b/packages/nocodb-nest/src/modules/datas/data-alias.controller.ts @@ -0,0 +1,239 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Request, + Response, +} from '@nestjs/common'; +import { parseHrtimeToSeconds } from '../../helpers'; +import { Acl } from '../../middlewares/extract-project-id/extract-project-id.middleware'; +import { DatasService } from './datas.service'; + +@Controller('data-alias') +export class DataAliasController { + constructor(private readonly datasService: DatasService) {} + + // todo: Handle the error case where view doesnt belong to model + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', + ]) + @Acl('dataList') + async dataList( + @Request() req, + @Response() res, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + ) { + const startTime = process.hrtime(); + const responseData = await this.datasService.dataList({ + query: req.query, + projectName: projectName, + tableName: tableName, + viewName: viewName, + }); + const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); + + res.setHeader('xc-db-response', elapsedSeconds); + return responseData; + } + + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName/find-one', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/find-one', + ]) + @Acl('dataFindOne') + async dataFindOne( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + ) { + return await this.datasService.dataFindOne({ + query: req.query, + projectName: projectName, + tableName: tableName, + viewName: viewName, + }); + } + + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName/groupby', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/groupby', + ]) + @Acl('dataGroupBy') + async dataGroupBy( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + ) { + return await this.datasService.dataGroupBy({ + query: req.query, + projectName: projectName, + tableName: tableName, + viewName: viewName, + }); + } + + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName/count', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/count', + ]) + @Acl('dataCount') + async dataCount( + @Request() req, + @Response() res, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + ) { + const countResult = await this.datasService.dataCount({ + query: req.query, + projectName: projectName, + tableName: tableName, + viewName: viewName, + }); + + res.json(countResult); + } + + @Post([ + '/api/v1/db/data/:orgs/:projectName/:tableName', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName', + ]) + @Acl('dataInsert') + async dataInsert( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + @Body() body: any, + ) { + return await this.datasService.dataInsert({ + projectName: projectName, + tableName: tableName, + viewName: viewName, + body: body, + cookie: req, + }); + } + + @Patch([ + '/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', + ]) + @Acl('dataUpdate') + async dataUpdate( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + @Param('rowId') rowId: string, + ) { + return await this.datasService.dataUpdate({ + projectName: projectName, + tableName: tableName, + viewName: viewName, + body: req.body, + cookie: req, + rowId: rowId, + }); + } + + @Delete([ + '/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', + ]) + @Acl('dataDelete') + async dataDelete( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + @Param('rowId') rowId: string, + ) { + return await this.datasService.dataDelete({ + projectName: projectName, + tableName: tableName, + viewName: viewName, + cookie: req, + rowId: rowId, + }); + } + + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName/:rowId', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId', + ]) + @Acl('dataRead') + async dataRead( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + @Param('rowId') rowId: string, + ) { + return await this.datasService.dataRead({ + projectName: projectName, + tableName: tableName, + viewName: viewName, + rowId: rowId, + query: req.query, + }); + } + + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName/:rowId/exist', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/:rowId/exist', + ]) + @Acl('dataExist') + async dataExist( + @Request() req, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + @Param('rowId') rowId: string, + ) { + return await this.datasService.dataExist({ + projectName: projectName, + tableName: tableName, + viewName: viewName, + rowId: rowId, + query: req.query, + }); + } + + // todo: Handle the error case where view doesnt belong to model + + @Get([ + '/api/v1/db/data/:orgs/:projectName/:tableName/group/:columnId', + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/group/:columnId', + ]) + @Acl('groupedDataList') + async groupedDataList( + @Request() req, + @Response() res, + @Param('projectName') projectName: string, + @Param('tableName') tableName: string, + @Param('viewName') viewName: string, + @Param('columnId') columnId: string, + ) { + const startTime = process.hrtime(); + const groupedData = await this.datasService.groupedDataList({ + projectName: projectName, + tableName: tableName, + viewName: viewName, + query: req.query, + columnId: columnId, + }); + const elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime)); + res.setHeader('xc-db-response', elapsedSeconds); + return groupedData; + } +}