Browse Source

feat: query isolation (#8666)

* feat: query isolation

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: query isolation

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: notifications acl

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: import type

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: baseModel constructor & extractProps undefined handling

Signed-off-by: mertmit <mertmit99@gmail.com>

* feat: query isolation migration

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: query isolation migration

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: mssql query issue

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: handle not found cases on extract id middleware

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: expect not found properly

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: data migration call

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: extract id for public

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: mysql list indexes

Signed-off-by: mertmit <mertmit99@gmail.com>

* fix: duplicate base context

Signed-off-by: mertmit <mertmit99@gmail.com>

---------

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/8678/head
Mert E 6 months ago committed by GitHub
parent
commit
c876b49467
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      packages/nocodb-sdk/src/lib/Api.ts
  2. 1
      packages/nocodb-sdk/src/lib/globals.ts
  3. 52
      packages/nocodb/src/Noco.ts
  4. 18
      packages/nocodb/src/controllers/api-docs/api-docs.controller.ts
  5. 8
      packages/nocodb/src/controllers/api-tokens.controller.ts
  6. 6
      packages/nocodb/src/controllers/attachments-secure.controller.ts
  7. 7
      packages/nocodb/src/controllers/attachments.controller.ts
  8. 15
      packages/nocodb/src/controllers/audits.controller.ts
  9. 38
      packages/nocodb/src/controllers/base-users.controller.ts
  10. 44
      packages/nocodb/src/controllers/bases.controller.ts
  11. 29
      packages/nocodb/src/controllers/bulk-data-alias.controller.ts
  12. 52
      packages/nocodb/src/controllers/calendars-datas.controller.ts
  13. 20
      packages/nocodb/src/controllers/calendars.controller.ts
  14. 47
      packages/nocodb/src/controllers/columns.controller.ts
  15. 4
      packages/nocodb/src/controllers/command-palette.controller.ts
  16. 24
      packages/nocodb/src/controllers/comments.controller.ts
  17. 42
      packages/nocodb/src/controllers/data-alias-export.controller.ts
  18. 43
      packages/nocodb/src/controllers/data-alias-nested.controller.ts
  19. 54
      packages/nocodb/src/controllers/data-alias.controller.ts
  20. 54
      packages/nocodb/src/controllers/data-table.controller.ts
  21. 66
      packages/nocodb/src/controllers/datas.controller.ts
  22. 27
      packages/nocodb/src/controllers/extensions.controller.ts
  23. 54
      packages/nocodb/src/controllers/filters.controller.ts
  24. 8
      packages/nocodb/src/controllers/form-columns.controller.ts
  25. 20
      packages/nocodb/src/controllers/forms.controller.ts
  26. 20
      packages/nocodb/src/controllers/galleries.controller.ts
  27. 13
      packages/nocodb/src/controllers/grid-columns.controller.ts
  28. 9
      packages/nocodb/src/controllers/grids.controller.ts
  29. 55
      packages/nocodb/src/controllers/hooks.controller.ts
  30. 20
      packages/nocodb/src/controllers/kanbans.controller.ts
  31. 20
      packages/nocodb/src/controllers/maps.controller.ts
  32. 12
      packages/nocodb/src/controllers/meta-diffs.controller.ts
  33. 9
      packages/nocodb/src/controllers/model-visibilities.controller.ts
  34. 21
      packages/nocodb/src/controllers/notifications.controller.ts
  35. 20
      packages/nocodb/src/controllers/old-datas/old-datas.controller.ts
  36. 87
      packages/nocodb/src/controllers/old-datas/old-datas.service.ts
  37. 8
      packages/nocodb/src/controllers/org-tokens.controller.ts
  38. 13
      packages/nocodb/src/controllers/org-users.controller.ts
  39. 45
      packages/nocodb/src/controllers/public-datas-export.controller.ts
  40. 68
      packages/nocodb/src/controllers/public-datas.controller.ts
  41. 11
      packages/nocodb/src/controllers/public-metas.controller.ts
  42. 71
      packages/nocodb/src/controllers/shared-bases.controller.ts
  43. 31
      packages/nocodb/src/controllers/sorts.controller.ts
  44. 22
      packages/nocodb/src/controllers/sources.controller.ts
  45. 5
      packages/nocodb/src/controllers/sql-views.controller.ts
  46. 20
      packages/nocodb/src/controllers/sync.controller.ts
  47. 39
      packages/nocodb/src/controllers/tables.controller.ts
  48. 2
      packages/nocodb/src/controllers/users/users.controller.ts
  49. 6
      packages/nocodb/src/controllers/utils.controller.ts
  50. 36
      packages/nocodb/src/controllers/view-columns.controller.ts
  51. 62
      packages/nocodb/src/controllers/views.controller.ts
  52. 1080
      packages/nocodb/src/db/BaseModelSqlv2.ts
  53. 121
      packages/nocodb/src/db/conditionV2.ts
  54. 170
      packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts
  55. 22
      packages/nocodb/src/db/genRollupSelectv2.ts
  56. 135
      packages/nocodb/src/db/generateLookupSelectQuery.ts
  57. 19
      packages/nocodb/src/db/sortV2.ts
  58. 9
      packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts
  59. 8
      packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2.ts
  60. 11
      packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2Trans.ts
  61. 8
      packages/nocodb/src/db/sql-migrator/lib/KnexMigratorv2.ts
  62. 10
      packages/nocodb/src/db/sql-migrator/lib/KnexMigratorv2Tans.ts
  63. 10
      packages/nocodb/src/decorators/tenant-context.decorator.ts
  64. 79
      packages/nocodb/src/helpers/NcPluginMgrv2.ts
  65. 11
      packages/nocodb/src/helpers/catchError.ts
  66. 98
      packages/nocodb/src/helpers/columnHelpers.ts
  67. 99
      packages/nocodb/src/helpers/dataHelpers.ts
  68. 6
      packages/nocodb/src/helpers/exportImportHelpers.ts
  69. 2
      packages/nocodb/src/helpers/extractProps.ts
  70. 4
      packages/nocodb/src/helpers/formulaFnHelper.ts
  71. 4
      packages/nocodb/src/helpers/formulaHelpers.ts
  72. 124
      packages/nocodb/src/helpers/getAst.ts
  73. 27
      packages/nocodb/src/helpers/initAdminFromEnv.ts
  74. 134
      packages/nocodb/src/helpers/populateMeta.ts
  75. 56
      packages/nocodb/src/helpers/populateSamplePayload.ts
  76. 10
      packages/nocodb/src/helpers/syncMigration.ts
  77. 48
      packages/nocodb/src/helpers/webhookHelpers.ts
  78. 2
      packages/nocodb/src/interface/Jobs.ts
  79. 24
      packages/nocodb/src/interface/config.ts
  80. 453
      packages/nocodb/src/meta/meta.service.ts
  81. 4
      packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  82. 409
      packages/nocodb/src/meta/migrations/v2/nc_050_tenant_isolation.ts
  83. 160
      packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts
  84. 44
      packages/nocodb/src/models/ApiToken.ts
  85. 72
      packages/nocodb/src/models/Audit.ts
  86. 49
      packages/nocodb/src/models/BarcodeColumn.ts
  87. 232
      packages/nocodb/src/models/Base.ts
  88. 84
      packages/nocodb/src/models/BaseUser.ts
  89. 58
      packages/nocodb/src/models/CalendarRange.ts
  90. 61
      packages/nocodb/src/models/CalendarView.ts
  91. 52
      packages/nocodb/src/models/CalendarViewColumn.ts
  92. 462
      packages/nocodb/src/models/Column.ts
  93. 69
      packages/nocodb/src/models/Comment.ts
  94. 58
      packages/nocodb/src/models/Extension.ts
  95. 306
      packages/nocodb/src/models/Filter.ts
  96. 64
      packages/nocodb/src/models/FormView.ts
  97. 40
      packages/nocodb/src/models/FormViewColumn.ts
  98. 36
      packages/nocodb/src/models/FormulaColumn.ts
  99. 51
      packages/nocodb/src/models/GalleryView.ts
  100. 50
      packages/nocodb/src/models/GalleryViewColumn.ts
  101. Some files were not shown because too many files have changed in this diff Show More

5
packages/nocodb-sdk/src/lib/Api.ts

@ -2100,6 +2100,11 @@ export interface BaseType {
* @example p_124hhlkbeasewh
*/
id?: string;
/**
* Workspace ID
* @example ws_123456
*/
fk_workspace_id?: string;
/** Model for Bool */
is_meta?: BoolType;
/** Meta Info such as theme colors */

1
packages/nocodb-sdk/src/lib/globals.ts

@ -134,6 +134,7 @@ export enum NcErrorType {
VIEW_NOT_FOUND = 'VIEW_NOT_FOUND',
FIELD_NOT_FOUND = 'FIELD_NOT_FOUND',
RECORD_NOT_FOUND = 'RECORD_NOT_FOUND',
GENERIC_NOT_FOUND = 'GENERIC_NOT_FOUND',
ERROR_DUPLICATE_RECORD = 'ERROR_DUPLICATE_RECORD',
USER_NOT_FOUND = 'USER_NOT_FOUND',
INVALID_OFFSET_VALUE = 'INVALID_OFFSET_VALUE',

52
packages/nocodb/src/Noco.ts

@ -13,7 +13,7 @@ import type { MetaService } from '~/meta/meta.service';
import type { IEventEmitter } from '~/modules/event-emitter/event-emitter.interface';
import type { Express } from 'express';
import type http from 'http';
import { MetaTable } from '~/utils/globals';
import { MetaTable, RootScopes } from '~/utils/globals';
import { AppModule } from '~/app.module';
import { isEE } from '~/utils';
@ -146,15 +146,26 @@ export default class Noco {
if (this.config?.auth?.jwt) {
if (!this.config.auth.jwt.secret) {
let secret = (
await this._ncMeta.metaGet('', '', MetaTable.STORE, {
key: 'nc_auth_jwt_secret',
})
await this._ncMeta.metaGet(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.STORE,
{
key: 'nc_auth_jwt_secret',
},
)
)?.value;
if (!secret) {
await this._ncMeta.metaInsert('', '', MetaTable.STORE, {
key: 'nc_auth_jwt_secret',
value: (secret = uuidv4()),
});
await this._ncMeta.metaInsert2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.STORE,
{
key: 'nc_auth_jwt_secret',
value: (secret = uuidv4()),
},
true,
);
}
this.config.auth.jwt.secret = secret;
}
@ -166,15 +177,26 @@ export default class Noco {
}
}
let serverId = (
await this._ncMeta.metaGet('', '', MetaTable.STORE, {
key: 'nc_server_id',
})
await this._ncMeta.metaGet(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.STORE,
{
key: 'nc_server_id',
},
)
)?.value;
if (!serverId) {
await this._ncMeta.metaInsert('', '', MetaTable.STORE, {
key: 'nc_server_id',
value: (serverId = T.id),
});
await this._ncMeta.metaInsert2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.STORE,
{
key: 'nc_server_id',
value: (serverId = T.id),
},
true,
);
}
process.env.NC_SERVER_UUID = serverId;
}

18
packages/nocodb/src/controllers/api-docs/api-docs.controller.ts

@ -13,6 +13,8 @@ import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { ApiDocsService } from '~/services/api-docs/api-docs.service';
import { PublicApiLimiterGuard } from '~/guards/public-api-limiter.guard';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext } from '~/interface/config';
@Controller()
export class ApiDocsController {
@ -21,8 +23,12 @@ export class ApiDocsController {
@Get(['/api/v1/db/meta/projects/:baseId/swagger.json'])
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@Acl('swaggerJson')
async swaggerJson(@Param('baseId') baseId: string, @Request() req) {
const swagger = await this.apiDocsService.swaggerJson({
async swaggerJson(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Request() req,
) {
const swagger = await this.apiDocsService.swaggerJson(context, {
baseId: baseId,
siteUrl: req.ncSiteUrl,
});
@ -33,8 +39,12 @@ export class ApiDocsController {
@Get(['/api/v2/meta/bases/:baseId/swagger.json'])
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@Acl('swaggerJson')
async swaggerJsonV2(@Param('baseId') baseId: string, @Request() req) {
const swagger = await this.apiDocsService.swaggerJsonV2({
async swaggerJsonV2(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Request() req,
) {
const swagger = await this.apiDocsService.swaggerJsonV2(context, {
baseId: baseId,
siteUrl: req.ncSiteUrl,
});

8
packages/nocodb/src/controllers/api-tokens.controller.ts

@ -9,12 +9,12 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { ApiTokensService } from '~/services/api-tokens.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -26,7 +26,7 @@ export class ApiTokensController {
'/api/v2/meta/bases/:baseId/api-tokens',
])
@Acl('baseApiTokenList')
async apiTokenList(@Req() req: Request) {
async apiTokenList(@Req() req: NcRequest) {
return new PagedResponseImpl(
await this.apiTokensService.apiTokenList({ userId: req['user'].id }),
);
@ -38,7 +38,7 @@ export class ApiTokensController {
])
@HttpCode(200)
@Acl('baseApiTokenCreate')
async apiTokenCreate(@Req() req: Request, @Body() body) {
async apiTokenCreate(@Req() req: NcRequest, @Body() body) {
return await this.apiTokensService.apiTokenCreate({
tokenBody: body,
userId: req['user'].id,
@ -51,7 +51,7 @@ export class ApiTokensController {
'/api/v2/meta/bases/:baseId/api-tokens/:token',
])
@Acl('baseApiTokenDelete')
async apiTokenDelete(@Req() req: Request, @Param('token') token: string) {
async apiTokenDelete(@Req() req: NcRequest, @Param('token') token: string) {
return await this.apiTokensService.apiTokenDelete({
token,
user: req['user'],

6
packages/nocodb/src/controllers/attachments-secure.controller.ts

@ -16,8 +16,8 @@ import hash from 'object-hash';
import moment from 'moment';
import { AnyFilesInterceptor } from '@nestjs/platform-express';
import { Response } from 'express';
import type { Request } from 'express';
import type { AttachmentReqType, FileType } from 'nocodb-sdk';
import type { NcRequest } from '~/interface/config';
import { GlobalGuard } from '~/guards/global/global.guard';
import { AttachmentsService } from '~/services/attachments.service';
import { PresignedUrl } from '~/models';
@ -34,7 +34,7 @@ export class AttachmentsSecureController {
@UseInterceptors(UploadAllowedInterceptor, AnyFilesInterceptor())
async upload(
@UploadedFiles() files: Array<FileType>,
@Req() req: Request & { user: { id: string } },
@Req() req: NcRequest & { user: { id: string } },
) {
const path = `${moment().format('YYYY/MM/DD')}/${hash(req.user.id)}`;
@ -53,7 +53,7 @@ export class AttachmentsSecureController {
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
async uploadViaURL(
@Body() body: Array<AttachmentReqType>,
@Req() req: Request & { user: { id: string } },
@Req() req: NcRequest & { user: { id: string } },
) {
const path = `${moment().format('YYYY/MM/DD')}/${hash(req.user.id)}`;

7
packages/nocodb/src/controllers/attachments.controller.ts

@ -14,13 +14,14 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { AnyFilesInterceptor } from '@nestjs/platform-express';
import { Request, Response } from 'express';
import { Response } from 'express';
import type { AttachmentReqType, FileType } from 'nocodb-sdk';
import { UploadAllowedInterceptor } from '~/interceptors/is-upload-allowed/is-upload-allowed.interceptor';
import { GlobalGuard } from '~/guards/global/global.guard';
import { AttachmentsService } from '~/services/attachments.service';
import { PresignedUrl } from '~/models';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
@Controller()
export class AttachmentsController {
@ -30,7 +31,7 @@ export class AttachmentsController {
@Post(['/api/v1/db/storage/upload', '/api/v2/storage/upload'])
@HttpCode(200)
@UseInterceptors(UploadAllowedInterceptor, AnyFilesInterceptor())
async upload(@UploadedFiles() files: Array<FileType>, @Req() req: Request) {
async upload(@UploadedFiles() files: Array<FileType>, @Req() req: NcRequest) {
const attachments = await this.attachmentsService.upload({
files: files,
path: req.query?.path?.toString(),
@ -47,7 +48,7 @@ export class AttachmentsController {
async uploadViaURL(
@Body() body: Array<AttachmentReqType>,
@Query('path') path: string,
@Req() req: Request,
@Req() req: NcRequest,
) {
const attachments = await this.attachmentsService.uploadViaURL({
urls: body,

15
packages/nocodb/src/controllers/audits.controller.ts

@ -8,12 +8,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { AuditsService } from '~/services/audits.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -22,7 +23,7 @@ export class AuditsController {
@Get(['/api/v1/db/meta/audits/', '/api/v2/meta/audits/'])
@Acl('auditList')
async auditListRow(@Req() req: Request) {
async auditListRow(@Req() req: NcRequest) {
return new PagedResponseImpl(
await this.auditsService.auditOnlyList({ query: req.query as any }),
);
@ -34,8 +35,12 @@ export class AuditsController {
])
@HttpCode(200)
@Acl('auditRowUpdate')
async auditRowUpdate(@Param('rowId') rowId: string, @Body() body: any) {
return await this.auditsService.auditRowUpdate({
async auditRowUpdate(
@TenantContext() context: NcContext,
@Param('rowId') rowId: string,
@Body() body: any,
) {
return await this.auditsService.auditRowUpdate(context, {
rowId,
body,
});
@ -46,7 +51,7 @@ export class AuditsController {
'/api/v2/meta/bases/:baseId/audits/',
])
@Acl('auditList')
async auditList(@Req() req: Request, @Param('baseId') baseId: string) {
async auditList(@Req() req: NcRequest, @Param('baseId') baseId: string) {
return new PagedResponseImpl(
await this.auditsService.auditList({
query: req.query,

38
packages/nocodb/src/controllers/base-users.controller.ts

@ -10,13 +10,14 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ProjectRoles, ProjectUserReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { BaseUsersService } from '~/services/base-users/base-users.service';
import { NcError } from '~/helpers/catchError';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@Controller()
@ -28,8 +29,12 @@ export class BaseUsersController {
'/api/v2/meta/bases/:baseId/users',
])
@Acl('baseUserList')
async userList(@Param('baseId') baseId: string, @Req() req: Request) {
const baseRoles = Object.keys(req.user?.base_roles ?? {});
async userList(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Req() req: NcRequest,
) {
const baseRoles = Object.keys((req.user as any)?.base_roles ?? {});
const mode =
baseRoles.includes(ProjectRoles.OWNER) ||
baseRoles.includes(ProjectRoles.CREATOR)
@ -37,7 +42,7 @@ export class BaseUsersController {
: 'viewer';
return {
users: await this.baseUsersService.userList({
users: await this.baseUsersService.userList(context, {
baseId,
mode,
}),
@ -51,15 +56,16 @@ export class BaseUsersController {
@HttpCode(200)
@Acl('userInvite')
async userInvite(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Req() req: Request,
@Req() req: NcRequest,
@Body() body: ProjectUserReqType,
): Promise<any> {
// todo: move this to a service
if (!body.email) {
NcError.badRequest('Email is required');
}
return await this.baseUsersService.userInvite({
return await this.baseUsersService.userInvite(context, {
baseId,
baseUser: body,
req,
@ -72,15 +78,16 @@ export class BaseUsersController {
])
@Acl('baseUserUpdate')
async baseUserUpdate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('userId') userId: string,
@Req() req: Request,
@Req() req: NcRequest,
@Body()
body: ProjectUserReqType & {
base_id: string;
},
): Promise<any> {
await this.baseUsersService.baseUserUpdate({
await this.baseUsersService.baseUserUpdate(context, {
baseUser: body,
baseId,
userId,
@ -97,11 +104,12 @@ export class BaseUsersController {
])
@Acl('baseUserDelete')
async baseUserDelete(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('userId') userId: string,
@Req() req: Request,
@Req() req: NcRequest,
): Promise<any> {
await this.baseUsersService.baseUserDelete({
await this.baseUsersService.baseUserDelete(context, {
baseId,
userId,
req,
@ -118,12 +126,13 @@ export class BaseUsersController {
@HttpCode(200)
@Acl('baseUserInviteResend')
async baseUserInviteResend(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('userId') userId: string,
@Req() req: Request,
@Req() req: NcRequest,
@Body() body: ProjectUserReqType,
): Promise<any> {
await this.baseUsersService.baseUserInviteResend({
await this.baseUsersService.baseUserInviteResend(context, {
baseId: baseId,
userId: userId,
baseUser: body,
@ -140,11 +149,12 @@ export class BaseUsersController {
])
@Acl('baseUserMetaUpdate')
async baseUserMetaUpdate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Req() req: Request,
@Req() req: NcRequest,
@Body() body: ProjectUserReqType,
): Promise<any> {
return await this.baseUsersService.baseUserMetaUpdate({
return await this.baseUsersService.baseUserMetaUpdate(context, {
baseId,
body,
user: req.user,

44
packages/nocodb/src/controllers/bases.controller.ts

@ -11,7 +11,6 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import isDocker from 'is-docker';
import { ProjectReqType } from 'nocodb-sdk';
import type { BaseType } from 'nocodb-sdk';
@ -23,6 +22,8 @@ import { BasesService } from '~/services/bases.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { Filter } from '~/models';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@Controller()
@ -33,8 +34,12 @@ export class BasesController {
scope: 'org',
})
@Get(['/api/v1/db/meta/projects/', '/api/v2/meta/bases/'])
async list(@Query() queryParams: Record<string, any>, @Req() req: Request) {
const bases = await this.projectsService.baseList({
async list(
@TenantContext() context: NcContext,
@Query() queryParams: Record<string, any>,
@Req() req: NcRequest,
) {
const bases = await this.projectsService.baseList(context, {
user: req.user,
query: queryParams,
});
@ -62,8 +67,11 @@ export class BasesController {
@Acl('baseGet')
@Get(['/api/v1/db/meta/projects/:baseId', '/api/v2/meta/bases/:baseId'])
async baseGet(@Param('baseId') baseId: string) {
const base = await this.projectsService.getProjectWithInfo({
async baseGet(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
) {
const base = await this.projectsService.getProjectWithInfo(context, {
baseId: baseId,
});
@ -75,11 +83,12 @@ export class BasesController {
@Acl('baseUpdate')
@Patch(['/api/v1/db/meta/projects/:baseId', '/api/v2/meta/bases/:baseId'])
async baseUpdate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Body() body: Record<string, any>,
@Req() req: Request,
@Req() req: NcRequest,
) {
const base = await this.projectsService.baseUpdate({
const base = await this.projectsService.baseUpdate(context, {
baseId,
base: body,
user: req.user,
@ -91,8 +100,12 @@ export class BasesController {
@Acl('baseDelete')
@Delete(['/api/v1/db/meta/projects/:baseId', '/api/v2/meta/bases/:baseId'])
async baseDelete(@Param('baseId') baseId: string, @Req() req: Request) {
const deleted = await this.projectsService.baseSoftDelete({
async baseDelete(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Req() req: NcRequest,
) {
const deleted = await this.projectsService.baseSoftDelete(context, {
baseId,
user: req.user,
req,
@ -106,7 +119,11 @@ export class BasesController {
})
@Post(['/api/v1/db/meta/projects', '/api/v2/meta/bases'])
@HttpCode(200)
async baseCreate(@Body() baseBody: ProjectReqType, @Req() req: Request) {
async baseCreate(
@TenantContext() context: NcContext,
@Body() baseBody: ProjectReqType,
@Req() req: NcRequest,
) {
const base = await this.projectsService.baseCreate({
base: baseBody,
req,
@ -121,7 +138,10 @@ export class BasesController {
'/api/v1/db/meta/projects/:baseId/has-empty-or-null-filters',
'/api/v2/meta/bases/:baseId/has-empty-or-null-filters',
])
async hasEmptyOrNullFilters(@Param('baseId') baseId: string) {
return await Filter.hasEmptyOrNullFilters(baseId);
async hasEmptyOrNullFilters(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
) {
return await Filter.hasEmptyOrNullFilters(context, baseId);
}
}

29
packages/nocodb/src/controllers/bulk-data-alias.controller.ts

@ -10,11 +10,13 @@ import {
Res,
UseGuards,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Response } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { BulkDataAliasService } from '~/services/bulk-data-alias.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -25,13 +27,14 @@ export class BulkDataAliasController {
@HttpCode(200)
@Acl('bulkDataInsert')
async bulkDataInsert(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Body() body: any,
) {
const exists = await this.bulkDataAliasService.bulkDataInsert({
const exists = await this.bulkDataAliasService.bulkDataInsert(context, {
body: body,
cookie: req,
baseName: baseName,
@ -44,12 +47,13 @@ export class BulkDataAliasController {
@Patch(['/api/v1/db/data/bulk/:orgs/:baseName/:tableName'])
@Acl('bulkDataUpdate')
async bulkDataUpdate(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Body() body: any,
) {
return await this.bulkDataAliasService.bulkDataUpdate({
return await this.bulkDataAliasService.bulkDataUpdate(context, {
body: body,
cookie: req,
baseName: baseName,
@ -61,12 +65,13 @@ export class BulkDataAliasController {
@Patch(['/api/v1/db/data/bulk/:orgs/:baseName/:tableName/all'])
@Acl('bulkDataUpdateAll')
async bulkDataUpdateAll(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Body() body: any,
) {
return await this.bulkDataAliasService.bulkDataUpdateAll({
return await this.bulkDataAliasService.bulkDataUpdateAll(context, {
body: body,
cookie: req,
baseName: baseName,
@ -78,12 +83,13 @@ export class BulkDataAliasController {
@Delete(['/api/v1/db/data/bulk/:orgs/:baseName/:tableName'])
@Acl('bulkDataDelete')
async bulkDataDelete(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Body() body: any,
) {
return await this.bulkDataAliasService.bulkDataDelete({
return await this.bulkDataAliasService.bulkDataDelete(context, {
body: body,
cookie: req,
baseName: baseName,
@ -96,11 +102,12 @@ export class BulkDataAliasController {
@Delete(['/api/v1/db/data/bulk/:orgs/:baseName/:tableName/all'])
@Acl('bulkDataDeleteAll')
async bulkDataDeleteAll(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.bulkDataAliasService.bulkDataDeleteAll({
return await this.bulkDataAliasService.bulkDataDeleteAll(context, {
// cookie: req,
baseName: baseName,
tableName: tableName,

52
packages/nocodb/src/controllers/calendars-datas.controller.ts

@ -7,13 +7,15 @@ import {
Res,
UseGuards,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Response } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { CalendarDatasService } from '~/services/calendar-datas.service';
import { parseHrtimeToMilliSeconds } from '~/helpers';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -23,12 +25,13 @@ export class CalendarDatasController {
@Get(['/api/v1/db/calendar-data/:orgs/:baseName/:tableName/views/:viewName'])
@Acl('dataList')
async dataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewName') viewId: string,
@Query('from_date') fromDate: string,
@Query('to_date') toDate: string,
) {
return await this.calendarDatasService.getCalendarDataList({
return await this.calendarDatasService.getCalendarDataList(context, {
viewId: viewId,
query: req.query,
from_date: fromDate,
@ -41,7 +44,8 @@ export class CalendarDatasController {
])
@Acl('dataList')
async calendarDataCount(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@ -51,12 +55,15 @@ export class CalendarDatasController {
) {
const startTime = process.hrtime();
const data = await this.calendarDatasService.getCalendarRecordCount({
query: req.query,
viewId: viewName,
from_date: fromDate,
to_date: toDate,
});
const data = await this.calendarDatasService.getCalendarRecordCount(
context,
{
query: req.query,
viewId: viewName,
from_date: fromDate,
to_date: toDate,
},
);
const elapsedSeconds = parseHrtimeToMilliSeconds(process.hrtime(startTime));
res.setHeader('xc-db-response', elapsedSeconds);
@ -68,18 +75,22 @@ export class CalendarDatasController {
'/api/v2/public/calendar-view/:sharedViewUuid/countByDate',
])
async countByDate(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
@Query('from_date') fromDate: string,
@Query('to_date') toDate: string,
) {
return await this.calendarDatasService.getPublicCalendarRecordCount({
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid,
from_date: fromDate,
to_date: toDate,
});
return await this.calendarDatasService.getPublicCalendarRecordCount(
context,
{
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid,
from_date: fromDate,
to_date: toDate,
},
);
}
@Get([
@ -87,12 +98,13 @@ export class CalendarDatasController {
'/api/v2/public/calendar-view/:sharedViewUuid',
])
async getPublicCalendarDataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
@Query('from_date') fromDate: string,
@Query('to_date') toDate: string,
) {
return await this.calendarDatasService.getPublicCalendarDataList({
return await this.calendarDatasService.getPublicCalendarDataList(context, {
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid,

20
packages/nocodb/src/controllers/calendars.controller.ts

@ -9,12 +9,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ViewCreateReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { CalendarsService } from '~/services/calendars.service';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -26,8 +27,11 @@ export class CalendarsController {
'/api/v2/meta/calendars/:calendarViewId',
])
@Acl('calendarViewGet')
async calendarViewGet(@Param('calendarViewId') calendarViewId: string) {
return await this.calendarsService.calendarViewGet({
async calendarViewGet(
@TenantContext() context: NcContext,
@Param('calendarViewId') calendarViewId: string,
) {
return await this.calendarsService.calendarViewGet(context, {
calendarViewId,
});
}
@ -39,11 +43,12 @@ export class CalendarsController {
@HttpCode(200)
@Acl('calendarViewCreate')
async calendarViewCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ViewCreateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.calendarsService.calendarViewCreate({
return await this.calendarsService.calendarViewCreate(context, {
tableId,
calendar: body,
user: req.user,
@ -57,11 +62,12 @@ export class CalendarsController {
])
@Acl('calendarViewUpdate')
async calendarViewUpdate(
@TenantContext() context: NcContext,
@Param('calendarViewId') calendarViewId: string,
@Body() body,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.calendarsService.calendarViewUpdate({
return await this.calendarsService.calendarViewUpdate(context, {
calendarViewId,
calendar: body,
req,

47
packages/nocodb/src/controllers/columns.controller.ts

@ -10,13 +10,14 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ColumnReqType } from 'nocodb-sdk';
import type { Column } from '~/models';
import { GlobalGuard } from '~/guards/global/global.guard';
import { ColumnsService } from '~/services/columns.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -30,11 +31,12 @@ export class ColumnsController {
@HttpCode(200)
@Acl('columnAdd')
async columnAdd(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ColumnReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.columnsService.columnAdd({
return await this.columnsService.columnAdd(context, {
tableId,
column: body,
req,
@ -48,11 +50,12 @@ export class ColumnsController {
])
@Acl('columnUpdate')
async columnUpdate(
@TenantContext() context: NcContext,
@Param('columnId') columnId: string,
@Body() body: ColumnReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.columnsService.columnUpdate({
return await this.columnsService.columnUpdate(context, {
columnId: columnId,
column: body,
req,
@ -65,8 +68,12 @@ export class ColumnsController {
'/api/v2/meta/columns/:columnId',
])
@Acl('columnDelete')
async columnDelete(@Param('columnId') columnId: string, @Req() req: Request) {
return await this.columnsService.columnDelete({
async columnDelete(
@TenantContext() context: NcContext,
@Param('columnId') columnId: string,
@Req() req: NcRequest,
) {
return await this.columnsService.columnDelete(context, {
columnId,
req,
user: req.user,
@ -75,8 +82,11 @@ export class ColumnsController {
@Get(['/api/v1/db/meta/columns/:columnId', '/api/v2/meta/columns/:columnId'])
@Acl('columnGet')
async columnGet(@Param('columnId') columnId: string) {
return await this.columnsService.columnGet({ columnId });
async columnGet(
@TenantContext() context: NcContext,
@Param('columnId') columnId: string,
) {
return await this.columnsService.columnGet(context, { columnId });
}
@Post([
@ -85,8 +95,11 @@ export class ColumnsController {
])
@HttpCode(200)
@Acl('columnSetAsPrimary')
async columnSetAsPrimary(@Param('columnId') columnId: string) {
return await this.columnsService.columnSetAsPrimary({ columnId });
async columnSetAsPrimary(
@TenantContext() context: NcContext,
@Param('columnId') columnId: string,
) {
return await this.columnsService.columnSetAsPrimary(context, { columnId });
}
@Get([
@ -94,8 +107,11 @@ export class ColumnsController {
'/api/v2/meta/tables/:tableId/columns/hash',
])
@Acl('columnsHash')
async columnsHash(@Param('tableId') tableId: string) {
return await this.columnsService.columnsHash(tableId);
async columnsHash(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
) {
return await this.columnsService.columnsHash(context, tableId);
}
@Post([
@ -105,6 +121,7 @@ export class ColumnsController {
@HttpCode(200)
@Acl('columnBulk')
async columnBulk(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body()
body: {
@ -114,8 +131,8 @@ export class ColumnsController {
column: Partial<Column>;
}[];
},
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.columnsService.columnBulk(tableId, body, req);
return await this.columnsService.columnBulk(context, tableId, body, req);
}
}

4
packages/nocodb/src/controllers/command-palette.controller.ts

@ -1,10 +1,10 @@
import { Controller, HttpCode, Post, Req, UseGuards } from '@nestjs/common';
import { Request } from 'express';
import type { UserType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { CommandPaletteService } from '~/services/command-palette.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -16,7 +16,7 @@ export class CommandPaletteController {
scope: 'org',
})
@HttpCode(200)
async commandPalette(@Req() req: Request) {
async commandPalette(@Req() req: NcRequest) {
const data = this.commandPaletteService.commandPalette({
user: req?.user as UserType,
body: req.body,

24
packages/nocodb/src/controllers/comments.controller.ts

@ -16,7 +16,8 @@ import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { CommentsService } from '~/services/comments.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -25,17 +26,21 @@ export class CommentsController {
@Get(['/api/v1/db/meta/comments', '/api/v2/meta/comments'])
@Acl('commentList')
async commentList(@Req() req: any) {
async commentList(@TenantContext() context: NcContext, @Req() req: any) {
return new PagedResponseImpl(
await this.commentsService.commentList({ query: req.query }),
await this.commentsService.commentList(context, { query: req.query }),
);
}
@Post(['/api/v1/db/meta/comments', '/api/v2/meta/comments'])
@HttpCode(200)
@Acl('commentRow')
async commentRow(@Req() req: NcRequest, @Body() body: any) {
return await this.commentsService.commentRow({
async commentRow(
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Body() body: any,
) {
return await this.commentsService.commentRow(context, {
user: req.user,
body: body,
req,
@ -48,10 +53,11 @@ export class CommentsController {
])
@Acl('commentDelete')
async commentDelete(
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('commentId') commentId: string,
) {
return await this.commentsService.commentDelete({
return await this.commentsService.commentDelete(context, {
commentId,
user: req.user,
req,
@ -64,11 +70,12 @@ export class CommentsController {
])
@Acl('commentUpdate')
async commentUpdate(
@TenantContext() context: NcContext,
@Param('commentId') commentId: string,
@Req() req: any,
@Body() body: any,
) {
return await this.commentsService.commentUpdate({
return await this.commentsService.commentUpdate(context, {
commentId: commentId,
user: req.user,
body: body,
@ -79,10 +86,11 @@ export class CommentsController {
@Get(['/api/v1/db/meta/comments/count', '/api/v2/meta/comments/count'])
@Acl('commentsCount')
async commentsCount(
@TenantContext() context: NcContext,
@Query('fk_model_id') fk_model_id: string,
@Query('ids') ids: string[],
) {
return await this.commentsService.commentsCount({
return await this.commentsService.commentsCount(context, {
fk_model_id,
ids,
});

42
packages/nocodb/src/controllers/data-alias-export.controller.ts

@ -1,5 +1,5 @@
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
import { Request, Response } from 'express';
import { Response } from 'express';
import * as XLSX from 'xlsx';
import { GlobalGuard } from '~/guards/global/global.guard';
import { DatasService } from '~/services/datas.service';
@ -7,6 +7,8 @@ import { extractCsvData, extractXlsxData } from '~/helpers/dataHelpers';
import { View } from '~/models';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -18,14 +20,25 @@ export class DataAliasExportController {
'/api/v1/db/data/:orgs/:baseName/:tableName/views/:viewName/export/excel',
])
@Acl('exportExcel')
async excelDataExport(@Req() req: Request, @Res() res: Response) {
async excelDataExport(
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
) {
const { model, view } =
await this.datasService.getViewAndModelFromRequestByAliasOrId(req);
await this.datasService.getViewAndModelFromRequestByAliasOrId(
context,
req,
);
let targetView = view;
if (!targetView) {
targetView = await View.getDefaultView(model.id);
targetView = await View.getDefaultView(context, model.id);
}
const { offset, elapsed, data } = await extractXlsxData(targetView, req);
const { offset, elapsed, data } = await extractXlsxData(
context,
targetView,
req,
);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, data, targetView.title);
const buf = XLSX.write(wb, { type: 'base64', bookType: 'xlsx' });
@ -45,14 +58,25 @@ export class DataAliasExportController {
'/api/v1/db/data/:orgs/:baseName/:tableName/export/csv',
])
@Acl('exportCsv')
async csvDataExport(@Req() req: Request, @Res() res: Response) {
async csvDataExport(
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
) {
const { model, view } =
await this.datasService.getViewAndModelFromRequestByAliasOrId(req);
await this.datasService.getViewAndModelFromRequestByAliasOrId(
context,
req,
);
let targetView = view;
if (!targetView) {
targetView = await View.getDefaultView(model.id);
targetView = await View.getDefaultView(context, model.id);
}
const { offset, elapsed, data } = await extractCsvData(targetView, req);
const { offset, elapsed, data } = await extractCsvData(
context,
targetView,
req,
);
res.set({
'Access-Control-Expose-Headers': 'nc-export-offset',

43
packages/nocodb/src/controllers/data-alias-nested.controller.ts

@ -8,11 +8,12 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { DataAliasNestedService } from '~/services/data-alias-nested.service';
import { GlobalGuard } from '~/guards/global/global.guard';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -23,13 +24,14 @@ export class DataAliasNestedController {
@Get(['/api/v1/db/data/:orgs/:baseName/:tableName/:rowId/mm/:columnName'])
@Acl('mmList')
async mmList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.dataAliasNestedService.mmList({
return await this.dataAliasNestedService.mmList(context, {
query: req.query,
columnName: columnName,
rowId: rowId,
@ -43,13 +45,14 @@ export class DataAliasNestedController {
])
@Acl('mmExcludedList')
async mmExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.dataAliasNestedService.mmExcludedList({
return await this.dataAliasNestedService.mmExcludedList(context, {
query: req.query,
columnName: columnName,
rowId: rowId,
@ -63,13 +66,14 @@ export class DataAliasNestedController {
])
@Acl('hmExcludedList')
async hmExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.dataAliasNestedService.hmExcludedList({
return await this.dataAliasNestedService.hmExcludedList(context, {
query: req.query,
columnName: columnName,
rowId: rowId,
@ -83,13 +87,14 @@ export class DataAliasNestedController {
])
@Acl('btExcludedList')
async btExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.dataAliasNestedService.btExcludedList({
return await this.dataAliasNestedService.btExcludedList(context, {
query: req.query,
columnName: columnName,
rowId: rowId,
@ -103,13 +108,14 @@ export class DataAliasNestedController {
])
@Acl('ooExcludedList')
async ooExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.dataAliasNestedService.ooExcludedList({
return await this.dataAliasNestedService.ooExcludedList(context, {
query: req.query,
columnName: columnName,
rowId: rowId,
@ -123,13 +129,14 @@ export class DataAliasNestedController {
@Get(['/api/v1/db/data/:orgs/:baseName/:tableName/:rowId/hm/:columnName'])
@Acl('hmList')
async hmList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
) {
return await this.dataAliasNestedService.hmList({
return await this.dataAliasNestedService.hmList(context, {
query: req.query,
columnName: columnName,
rowId: rowId,
@ -143,14 +150,15 @@ export class DataAliasNestedController {
])
@Acl('relationDataRemove')
async relationDataRemove(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('refRowId') refRowId: string,
) {
await this.dataAliasNestedService.relationDataRemove({
await this.dataAliasNestedService.relationDataRemove(context, {
columnName: columnName,
rowId: rowId,
baseName: baseName,
@ -169,14 +177,15 @@ export class DataAliasNestedController {
@Acl('relationDataAdd')
@HttpCode(200)
async relationDataAdd(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('columnName') columnName: string,
@Param('rowId') rowId: string,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('refRowId') refRowId: string,
) {
await this.dataAliasNestedService.relationDataAdd({
await this.dataAliasNestedService.relationDataAdd(context, {
columnName: columnName,
rowId: rowId,
baseName: baseName,

54
packages/nocodb/src/controllers/data-alias.controller.ts

@ -12,12 +12,14 @@ import {
Res,
UseGuards,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Response } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { parseHrtimeToMilliSeconds } from '~/helpers';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DatasService } from '~/services/datas.service';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -31,7 +33,8 @@ export class DataAliasController {
])
@Acl('dataList')
async dataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@ -39,7 +42,7 @@ export class DataAliasController {
@Query('opt') opt: string,
) {
const startTime = process.hrtime();
const responseData = await this.datasService.dataList({
const responseData = await this.datasService.dataList(context, {
query: req.query,
baseName: baseName,
tableName: tableName,
@ -63,12 +66,13 @@ export class DataAliasController {
])
@Acl('dataFindOne')
async dataFindOne(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
) {
return await this.datasService.dataFindOne({
return await this.datasService.dataFindOne(context, {
query: req.query,
baseName: baseName,
tableName: tableName,
@ -82,12 +86,13 @@ export class DataAliasController {
])
@Acl('dataGroupBy')
async dataGroupBy(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
) {
return await this.datasService.dataGroupBy({
return await this.datasService.dataGroupBy(context, {
query: req.query,
baseName: baseName,
tableName: tableName,
@ -101,13 +106,14 @@ export class DataAliasController {
])
@Acl('dataCount')
async dataCount(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
) {
const countResult = await this.datasService.dataCount({
const countResult = await this.datasService.dataCount(context, {
query: req.query,
baseName: baseName,
tableName: tableName,
@ -124,14 +130,15 @@ export class DataAliasController {
@HttpCode(200)
@Acl('dataInsert')
async dataInsert(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
@Body() body: any,
@Query('opt') opt: string,
) {
return await this.datasService.dataInsert({
return await this.datasService.dataInsert(context, {
baseName: baseName,
tableName: tableName,
viewName: viewName,
@ -147,14 +154,15 @@ export class DataAliasController {
])
@Acl('dataUpdate')
async dataUpdate(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
@Param('rowId') rowId: string,
@Query('opt') opt: string,
) {
return await this.datasService.dataUpdate({
return await this.datasService.dataUpdate(context, {
baseName: baseName,
tableName: tableName,
viewName: viewName,
@ -171,13 +179,14 @@ export class DataAliasController {
])
@Acl('dataDelete')
async dataDelete(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.dataDelete({
return await this.datasService.dataDelete(context, {
baseName: baseName,
tableName: tableName,
viewName: viewName,
@ -192,7 +201,8 @@ export class DataAliasController {
])
@Acl('dataRead')
async dataRead(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
@ -200,7 +210,7 @@ export class DataAliasController {
@Query('opt') opt: string,
@Query('getHiddenColumn') getHiddenColumn: boolean,
) {
return await this.datasService.dataRead({
return await this.datasService.dataRead(context, {
baseName: baseName,
tableName: tableName,
viewName: viewName,
@ -217,14 +227,15 @@ export class DataAliasController {
])
@Acl('dataExist')
async dataExist(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@Param('viewName') viewName: string,
@Param('rowId') rowId: string,
) {
const exists = await this.datasService.dataExist({
const exists = await this.datasService.dataExist(context, {
baseName: baseName,
tableName: tableName,
viewName: viewName,
@ -243,7 +254,8 @@ export class DataAliasController {
])
@Acl('groupedDataList')
async groupedDataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('baseName') baseName: string,
@Param('tableName') tableName: string,
@ -251,7 +263,7 @@ export class DataAliasController {
@Param('columnId') columnId: string,
) {
const startTime = process.hrtime();
const groupedData = await this.datasService.groupedDataList({
const groupedData = await this.datasService.groupedDataList(context, {
baseName: baseName,
tableName: tableName,
viewName: viewName,

54
packages/nocodb/src/controllers/data-table.controller.ts

@ -12,12 +12,14 @@ import {
Res,
UseGuards,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { Response } from 'express';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DataTableService } from '~/services/data-table.service';
import { parseHrtimeToMilliSeconds } from '~/helpers';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { GlobalGuard } from '~/guards/global/global.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -28,13 +30,14 @@ export class DataTableController {
@Get('/api/v2/tables/:modelId/records')
@Acl('dataList')
async dataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
) {
const startTime = process.hrtime();
const responseData = await this.dataTableService.dataList({
const responseData = await this.dataTableService.dataList(context, {
query: req.query,
modelId: modelId,
viewId: viewId,
@ -47,12 +50,13 @@ export class DataTableController {
@Get(['/api/v2/tables/:modelId/records/count'])
@Acl('dataCount')
async dataCount(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Res() res: Response,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
) {
const countResult = await this.dataTableService.dataCount({
const countResult = await this.dataTableService.dataCount(context, {
query: req.query,
modelId,
viewId,
@ -65,12 +69,13 @@ export class DataTableController {
@HttpCode(200)
@Acl('dataInsert')
async dataInsert(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Body() body: any,
) {
return await this.dataTableService.dataInsert({
return await this.dataTableService.dataInsert(context, {
modelId: modelId,
body: body,
viewId,
@ -81,12 +86,13 @@ export class DataTableController {
@Patch(['/api/v2/tables/:modelId/records'])
@Acl('dataUpdate')
async dataUpdate(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('rowId') _rowId: string,
) {
return await this.dataTableService.dataUpdate({
return await this.dataTableService.dataUpdate(context, {
modelId: modelId,
body: req.body,
cookie: req,
@ -97,12 +103,13 @@ export class DataTableController {
@Delete(['/api/v2/tables/:modelId/records'])
@Acl('dataDelete')
async dataDelete(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('rowId') _rowId: string,
) {
return await this.dataTableService.dataDelete({
return await this.dataTableService.dataDelete(context, {
modelId: modelId,
cookie: req,
viewId,
@ -113,12 +120,13 @@ export class DataTableController {
@Get(['/api/v2/tables/:modelId/records/:rowId'])
@Acl('dataRead')
async dataRead(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('rowId') rowId: string,
) {
return await this.dataTableService.dataRead({
return await this.dataTableService.dataRead(context, {
modelId,
rowId: rowId,
query: req.query,
@ -129,13 +137,14 @@ export class DataTableController {
@Get(['/api/v2/tables/:modelId/links/:columnId/records/:rowId'])
@Acl('nestedDataList')
async nestedDataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
) {
return await this.dataTableService.nestedDataList({
return await this.dataTableService.nestedDataList(context, {
modelId,
rowId: rowId,
query: req.query,
@ -147,7 +156,8 @@ export class DataTableController {
@Post(['/api/v2/tables/:modelId/links/:columnId/records/:rowId'])
@Acl('nestedDataLink')
async nestedLink(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@ -161,7 +171,7 @@ export class DataTableController {
| Record<string, any>
| Record<string, any>[],
) {
return await this.dataTableService.nestedLink({
return await this.dataTableService.nestedLink(context, {
modelId,
rowId: rowId,
query: req.query,
@ -175,7 +185,8 @@ export class DataTableController {
@Delete(['/api/v2/tables/:modelId/links/:columnId/records/:rowId'])
@Acl('nestedDataUnlink')
async nestedUnlink(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@ -183,7 +194,7 @@ export class DataTableController {
@Body()
refRowIds: string | string[] | number | number[] | Record<string, any>,
) {
return await this.dataTableService.nestedUnlink({
return await this.dataTableService.nestedUnlink(context, {
modelId,
rowId: rowId,
query: req.query,
@ -198,7 +209,8 @@ export class DataTableController {
@Post(['/api/v2/tables/:modelId/links/:columnId/records'])
@Acl('nestedDataListCopyPasteOrDeleteAll')
async nestedListCopyPasteOrDeleteAll(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('modelId') modelId: string,
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@ -210,7 +222,7 @@ export class DataTableController {
fk_related_model_id: string;
}[],
) {
return await this.dataTableService.nestedListCopyPasteOrDeleteAll({
return await this.dataTableService.nestedListCopyPasteOrDeleteAll(context, {
modelId,
query: req.query,
viewId,

66
packages/nocodb/src/controllers/datas.controller.ts

@ -10,11 +10,12 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { DatasService } from '~/services/datas.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -23,8 +24,12 @@ export class DatasController {
@Get('/data/:viewId/')
@Acl('dataList')
async dataList(@Req() req: Request, @Param('viewId') viewId: string) {
return await this.datasService.dataListByViewId({
async dataList(
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
) {
return await this.datasService.dataListByViewId(context, {
viewId: viewId,
query: req.query,
});
@ -33,12 +38,13 @@ export class DatasController {
@Get('/data/:viewId/:rowId/mm/:colId')
@Acl('mmList')
async mmList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('colId') colId: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.mmList({
return await this.datasService.mmList(context, {
viewId: viewId,
colId: colId,
rowId: rowId,
@ -49,12 +55,13 @@ export class DatasController {
@Get('/data/:viewId/:rowId/mm/:colId/exclude')
@Acl('mmExcludedList')
async mmExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('colId') colId: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.mmExcludedList({
return await this.datasService.mmExcludedList(context, {
viewId: viewId,
colId: colId,
rowId: rowId,
@ -65,12 +72,13 @@ export class DatasController {
@Get('/data/:viewId/:rowId/hm/:colId/exclude')
@Acl('hmExcludedList')
async hmExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('colId') colId: string,
@Param('rowId') rowId: string,
) {
await this.datasService.hmExcludedList({
await this.datasService.hmExcludedList(context, {
viewId: viewId,
colId: colId,
rowId: rowId,
@ -81,12 +89,13 @@ export class DatasController {
@Get('/data/:viewId/:rowId/bt/:colId/exclude')
@Acl('btExcludedList')
async btExcludedList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('colId') colId: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.btExcludedList({
return await this.datasService.btExcludedList(context, {
viewId: viewId,
colId: colId,
rowId: rowId,
@ -97,12 +106,13 @@ export class DatasController {
@Get('/data/:viewId/:rowId/hm/:colId')
@Acl('hmList')
async hmList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('colId') colId: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.hmList({
return await this.datasService.hmList(context, {
viewId: viewId,
colId: colId,
rowId: rowId,
@ -113,11 +123,12 @@ export class DatasController {
@Get('/data/:viewId/:rowId')
@Acl('dataRead')
async dataRead(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.dataReadByViewId({
return await this.datasService.dataReadByViewId(context, {
viewId,
rowId,
query: req.query,
@ -128,11 +139,12 @@ export class DatasController {
@HttpCode(200)
@Acl('dataInsert')
async dataInsert(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Body() body: any,
) {
return await this.datasService.dataInsertByViewId({
return await this.datasService.dataInsertByViewId(context, {
viewId: viewId,
body: body,
cookie: req,
@ -142,12 +154,13 @@ export class DatasController {
@Patch('/data/:viewId/:rowId')
@Acl('dataUpdate')
async dataUpdate(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('rowId') rowId: string,
@Body() body: any,
) {
return await this.datasService.dataUpdateByViewId({
return await this.datasService.dataUpdateByViewId(context, {
viewId: viewId,
rowId: rowId,
body: body,
@ -158,11 +171,12 @@ export class DatasController {
@Delete('/data/:viewId/:rowId')
@Acl('dataDelete')
async dataDelete(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('rowId') rowId: string,
) {
return await this.datasService.dataDeleteByViewId({
return await this.datasService.dataDeleteByViewId(context, {
viewId: viewId,
rowId: rowId,
cookie: req,
@ -172,14 +186,15 @@ export class DatasController {
@Delete('/data/:viewId/:rowId/:relationType/:colId/:childId')
@Acl('relationDataDelete')
async relationDataDelete(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('rowId') rowId: string,
@Param('relationType') relationType: string,
@Param('colId') colId: string,
@Param('childId') childId: string,
) {
await this.datasService.relationDataDelete({
await this.datasService.relationDataDelete(context, {
viewId: viewId,
colId: colId,
childId: childId,
@ -194,14 +209,15 @@ export class DatasController {
@HttpCode(200)
@Acl('relationDataAdd')
async relationDataAdd(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('viewId') viewId: string,
@Param('rowId') rowId: string,
@Param('relationType') relationType: string,
@Param('colId') colId: string,
@Param('childId') childId: string,
) {
await this.datasService.relationDataAdd({
await this.datasService.relationDataAdd(context, {
viewId: viewId,
colId: colId,
childId: childId,

27
packages/nocodb/src/controllers/extensions.controller.ts

@ -14,8 +14,9 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { ExtensionsService } from '~/services/extensions.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -24,20 +25,25 @@ export class ExtensionsController {
@Get(['/api/v2/extensions/:baseId'])
@Acl('extensionList')
async extensionList(@Param('baseId') baseId: string, @Req() _req: NcRequest) {
async extensionList(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Req() _req: NcRequest,
) {
return new PagedResponseImpl(
await this.extensionsService.extensionList({ baseId }),
await this.extensionsService.extensionList(context, { baseId }),
);
}
@Post(['/api/v2/extensions/:baseId'])
@Acl('extensionCreate')
async extensionCreate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Body() body: Partial<ExtensionReqType>,
@Req() req: NcRequest,
) {
return await this.extensionsService.extensionCreate({
return await this.extensionsService.extensionCreate(context, {
extension: {
...body,
base_id: baseId,
@ -48,18 +54,22 @@ export class ExtensionsController {
@Get(['/api/v2/extensions/:extensionId'])
@Acl('extensionRead')
async extensionRead(@Param('extensionId') extensionId: string) {
return await this.extensionsService.extensionRead({ extensionId });
async extensionRead(
@TenantContext() context: NcContext,
@Param('extensionId') extensionId: string,
) {
return await this.extensionsService.extensionRead(context, { extensionId });
}
@Patch(['/api/v2/extensions/:extensionId'])
@Acl('extensionUpdate')
async extensionUpdate(
@TenantContext() context: NcContext,
@Param('extensionId') extensionId: string,
@Body() body: Partial<ExtensionReqType>,
@Req() req: NcRequest,
) {
return await this.extensionsService.extensionUpdate({
return await this.extensionsService.extensionUpdate(context, {
extensionId,
extension: body,
req,
@ -69,10 +79,11 @@ export class ExtensionsController {
@Delete(['/api/v2/extensions/:extensionId'])
@Acl('extensionDelete')
async extensionDelete(
@TenantContext() context: NcContext,
@Param('extensionId') extensionId: string,
@Req() req: NcRequest,
) {
return await this.extensionsService.extensionDelete({
return await this.extensionsService.extensionDelete(context, {
extensionId,
req,
});

54
packages/nocodb/src/controllers/filters.controller.ts

@ -10,13 +10,14 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { FilterReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { FiltersService } from '~/services/filters.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -28,9 +29,12 @@ export class FiltersController {
'/api/v2/meta/views/:viewId/filters',
])
@Acl('filterList')
async filterList(@Param('viewId') viewId: string) {
async filterList(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
) {
return new PagedResponseImpl(
await this.filtersService.filterList({
await this.filtersService.filterList(context, {
viewId,
}),
);
@ -43,11 +47,12 @@ export class FiltersController {
@HttpCode(200)
@Acl('filterCreate')
async filterCreate(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Body() body: FilterReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const filter = await this.filtersService.filterCreate({
const filter = await this.filtersService.filterCreate(context, {
filter: body,
viewId: viewId,
user: req.user,
@ -63,11 +68,12 @@ export class FiltersController {
@HttpCode(200)
@Acl('hookFilterCreate')
async hookFilterCreate(
@TenantContext() context: NcContext,
@Param('hookId') hookId: string,
@Body() body: FilterReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const filter = await this.filtersService.hookFilterCreate({
const filter = await this.filtersService.hookFilterCreate(context, {
filter: body,
hookId,
user: req.user,
@ -78,8 +84,11 @@ export class FiltersController {
@Get(['/api/v1/db/meta/filters/:filterId', '/api/v2/meta/filters/:filterId'])
@Acl('filterGet')
async filterGet(@Param('filterId') filterId: string) {
return await this.filtersService.filterGet({ filterId });
async filterGet(
@TenantContext() context: NcContext,
@Param('filterId') filterId: string,
) {
return await this.filtersService.filterGet(context, { filterId });
}
@Get([
@ -87,9 +96,12 @@ export class FiltersController {
'/api/v2/meta/filters/:filterParentId/children',
])
@Acl('filterChildrenList')
async filterChildrenRead(@Param('filterParentId') filterParentId: string) {
async filterChildrenRead(
@TenantContext() context: NcContext,
@Param('filterParentId') filterParentId: string,
) {
return new PagedResponseImpl(
await this.filtersService.filterChildrenList({
await this.filtersService.filterChildrenList(context, {
filterId: filterParentId,
}),
);
@ -101,11 +113,12 @@ export class FiltersController {
])
@Acl('filterUpdate')
async filterUpdate(
@TenantContext() context: NcContext,
@Param('filterId') filterId: string,
@Body() body: FilterReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const filter = await this.filtersService.filterUpdate({
const filter = await this.filtersService.filterUpdate(context, {
filterId: filterId,
filter: body,
user: req.user,
@ -119,8 +132,12 @@ export class FiltersController {
'/api/v2/meta/filters/:filterId',
])
@Acl('filterDelete')
async filterDelete(@Param('filterId') filterId: string, @Req() req: Request) {
const filter = await this.filtersService.filterDelete({
async filterDelete(
@TenantContext() context: NcContext,
@Param('filterId') filterId: string,
@Req() req: NcRequest,
) {
const filter = await this.filtersService.filterDelete(context, {
req,
filterId,
});
@ -132,9 +149,12 @@ export class FiltersController {
'/api/v2/meta/hooks/:hookId/filters',
])
@Acl('hookFilterList')
async hookFilterList(@Param('hookId') hookId: string) {
async hookFilterList(
@TenantContext() context: NcContext,
@Param('hookId') hookId: string,
) {
return new PagedResponseImpl(
await this.filtersService.hookFilterList({
await this.filtersService.hookFilterList(context, {
hookId: hookId,
}),
);

8
packages/nocodb/src/controllers/form-columns.controller.ts

@ -1,9 +1,10 @@
import { Body, Controller, Param, Patch, Req, UseGuards } from '@nestjs/common';
import { Request } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { FormColumnsService } from '~/services/form-columns.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
class FormColumnUpdateReqType {}
@ -18,12 +19,13 @@ export class FormColumnsController {
])
@Acl('formViewUpdate')
async columnUpdate(
@TenantContext() context: NcContext,
@Param('formViewColumnId') formViewColumnId: string,
@Body() formViewColumnbody: FormColumnUpdateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.formColumnsService.columnUpdate({
return await this.formColumnsService.columnUpdate(context, {
formViewColumnId,
formViewColumn: formViewColumnbody,
req,

20
packages/nocodb/src/controllers/forms.controller.ts

@ -9,12 +9,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ViewCreateReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { FormsService } from '~/services/forms.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -23,8 +24,11 @@ export class FormsController {
@Get(['/api/v1/db/meta/forms/:formViewId', '/api/v2/meta/forms/:formViewId'])
@Acl('formViewGet')
async formViewGet(@Param('formViewId') formViewId: string) {
const formViewData = await this.formsService.formViewGet({
async formViewGet(
@TenantContext() context: NcContext,
@Param('formViewId') formViewId: string,
) {
const formViewData = await this.formsService.formViewGet(context, {
formViewId,
});
return formViewData;
@ -37,11 +41,12 @@ export class FormsController {
@HttpCode(200)
@Acl('formViewCreate')
async formViewCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ViewCreateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const view = await this.formsService.formViewCreate({
const view = await this.formsService.formViewCreate(context, {
body,
tableId,
user: req.user,
@ -55,11 +60,12 @@ export class FormsController {
])
@Acl('formViewUpdate')
async formViewUpdate(
@TenantContext() context: NcContext,
@Param('formViewId') formViewId: string,
@Body() body,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.formsService.formViewUpdate({
return await this.formsService.formViewUpdate(context, {
formViewId,
form: body,
req,

20
packages/nocodb/src/controllers/galleries.controller.ts

@ -9,12 +9,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { GalleryUpdateReqType, ViewCreateReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { GalleriesService } from '~/services/galleries.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -26,8 +27,11 @@ export class GalleriesController {
'/api/v2/meta/galleries/:galleryViewId',
])
@Acl('galleryViewGet')
async galleryViewGet(@Param('galleryViewId') galleryViewId: string) {
return await this.galleriesService.galleryViewGet({
async galleryViewGet(
@TenantContext() context: NcContext,
@Param('galleryViewId') galleryViewId: string,
) {
return await this.galleriesService.galleryViewGet(context, {
galleryViewId,
});
}
@ -39,11 +43,12 @@ export class GalleriesController {
@HttpCode(200)
@Acl('galleryViewCreate')
async galleryViewCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ViewCreateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.galleriesService.galleryViewCreate({
return await this.galleriesService.galleryViewCreate(context, {
gallery: body,
// todo: sanitize
tableId,
@ -58,12 +63,13 @@ export class GalleriesController {
])
@Acl('galleryViewUpdate')
async galleryViewUpdate(
@TenantContext() context: NcContext,
@Param('galleryViewId') galleryViewId: string,
@Body() body: GalleryUpdateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.galleriesService.galleryViewUpdate({
return await this.galleriesService.galleryViewUpdate(context, {
galleryViewId,
gallery: body,
req,

13
packages/nocodb/src/controllers/grid-columns.controller.ts

@ -12,7 +12,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { GridColumnsService } from '~/services/grid-columns.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -24,8 +25,11 @@ export class GridColumnsController {
'/api/v2/meta/grids/:gridViewId/grid-columns',
])
@Acl('columnList')
async columnList(@Param('gridViewId') gridViewId: string) {
return await this.gridColumnsService.columnList({
async columnList(
@TenantContext() context: NcContext,
@Param('gridViewId') gridViewId: string,
) {
return await this.gridColumnsService.columnList(context, {
gridViewId,
});
}
@ -35,12 +39,13 @@ export class GridColumnsController {
])
@Acl('gridColumnUpdate')
async gridColumnUpdate(
@TenantContext() context: NcContext,
@Param('gridViewColumnId') gridViewColumnId: string,
@Body() body: GridColumnReqType,
@Req() req: NcRequest,
) {
return this.gridColumnsService.gridColumnUpdate({
return this.gridColumnsService.gridColumnUpdate(context, {
gridViewColumnId,
grid: body,
req,

9
packages/nocodb/src/controllers/grids.controller.ts

@ -13,7 +13,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { GridsService } from '~/services/grids.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -27,11 +28,12 @@ export class GridsController {
@HttpCode(200)
@Acl('gridViewCreate')
async gridViewCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ViewCreateReqType,
@Req() req: NcRequest,
) {
const view = await this.gridsService.gridViewCreate({
const view = await this.gridsService.gridViewCreate(context, {
grid: body,
tableId,
req,
@ -41,11 +43,12 @@ export class GridsController {
@Patch(['/api/v1/db/meta/grids/:viewId', '/api/v2/meta/grids/:viewId'])
@Acl('gridViewUpdate')
async gridViewUpdate(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Body() body,
@Req() req: NcRequest,
) {
return await this.gridsService.gridViewUpdate({
return await this.gridsService.gridViewUpdate(context, {
viewId,
grid: body,
req,

55
packages/nocodb/src/controllers/hooks.controller.ts

@ -10,7 +10,6 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { HookReqType, HookTestReqType } from 'nocodb-sdk';
import type { HookType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
@ -18,6 +17,8 @@ import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { HooksService } from '~/services/hooks.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -29,8 +30,13 @@ export class HooksController {
'/api/v2/meta/tables/:tableId/hooks',
])
@Acl('hookList')
async hookList(@Param('tableId') tableId: string) {
return new PagedResponseImpl(await this.hooksService.hookList({ tableId }));
async hookList(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
) {
return new PagedResponseImpl(
await this.hooksService.hookList(context, { tableId }),
);
}
@Post([
@ -40,11 +46,12 @@ export class HooksController {
@HttpCode(200)
@Acl('hookCreate')
async hookCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: HookReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const hook = await this.hooksService.hookCreate({
const hook = await this.hooksService.hookCreate(context, {
hook: body,
tableId,
req,
@ -54,18 +61,27 @@ export class HooksController {
@Delete(['/api/v1/db/meta/hooks/:hookId', '/api/v2/meta/hooks/:hookId'])
@Acl('hookDelete')
async hookDelete(@Param('hookId') hookId: string, @Req() req: Request) {
return await this.hooksService.hookDelete({ hookId, req });
async hookDelete(
@TenantContext() context: NcContext,
@Param('hookId') hookId: string,
@Req() req: NcRequest,
) {
return await this.hooksService.hookDelete(context, { hookId, req });
}
@Patch(['/api/v1/db/meta/hooks/:hookId', '/api/v2/meta/hooks/:hookId'])
@Acl('hookUpdate')
async hookUpdate(
@TenantContext() context: NcContext,
@Param('hookId') hookId: string,
@Body() body: HookReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.hooksService.hookUpdate({ hookId, hook: body, req });
return await this.hooksService.hookUpdate(context, {
hookId,
hook: body,
req,
});
}
@Post([
@ -74,9 +90,13 @@ export class HooksController {
])
@HttpCode(200)
@Acl('hookTest')
async hookTest(@Body() body: HookTestReqType, @Req() req: Request) {
async hookTest(
@TenantContext() context: NcContext,
@Body() body: HookTestReqType,
@Req() req: NcRequest,
) {
try {
await this.hooksService.hookTest({
await this.hooksService.hookTest(context, {
hookTest: {
...body,
payload: {
@ -100,11 +120,12 @@ export class HooksController {
])
@Acl('tableSampleData')
async tableSampleData(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Param('operation') operation: HookType['operation'],
@Param('version') version: HookType['version'],
) {
return await this.hooksService.tableSampleData({
return await this.hooksService.tableSampleData(context, {
tableId,
operation,
version,
@ -116,15 +137,19 @@ export class HooksController {
'/api/v2/meta/hooks/:hookId/logs',
])
@Acl('hookLogList')
async hookLogList(@Param('hookId') hookId: string, @Req() req: Request) {
async hookLogList(
@TenantContext() context: NcContext,
@Param('hookId') hookId: string,
@Req() req: NcRequest,
) {
return new PagedResponseImpl(
await this.hooksService.hookLogList({
await this.hooksService.hookLogList(context, {
query: req.query,
hookId,
}),
{
...req.query,
count: await this.hooksService.hookLogCount({
count: await this.hooksService.hookLogCount(context, {
hookId,
}),
},

20
packages/nocodb/src/controllers/kanbans.controller.ts

@ -9,12 +9,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ViewCreateReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { KanbansService } from '~/services/kanbans.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -26,8 +27,11 @@ export class KanbansController {
'/api/v2/meta/kanbans/:kanbanViewId',
])
@Acl('kanbanViewGet')
async kanbanViewGet(@Param('kanbanViewId') kanbanViewId: string) {
return await this.kanbansService.kanbanViewGet({
async kanbanViewGet(
@TenantContext() context: NcContext,
@Param('kanbanViewId') kanbanViewId: string,
) {
return await this.kanbansService.kanbanViewGet(context, {
kanbanViewId,
});
}
@ -39,11 +43,12 @@ export class KanbansController {
@HttpCode(200)
@Acl('kanbanViewCreate')
async kanbanViewCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ViewCreateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.kanbansService.kanbanViewCreate({
return await this.kanbansService.kanbanViewCreate(context, {
tableId,
kanban: body,
user: req.user,
@ -57,12 +62,13 @@ export class KanbansController {
])
@Acl('kanbanViewUpdate')
async kanbanViewUpdate(
@TenantContext() context: NcContext,
@Param('kanbanViewId') kanbanViewId: string,
@Body() body,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.kanbansService.kanbanViewUpdate({
return await this.kanbansService.kanbanViewUpdate(context, {
kanbanViewId,
kanban: body,
req,

20
packages/nocodb/src/controllers/maps.controller.ts

@ -9,12 +9,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { MapUpdateReqType, ViewCreateReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { MapsService } from '~/services/maps.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -23,8 +24,11 @@ export class MapsController {
@Get(['/api/v1/db/meta/maps/:mapViewId', '/api/v2/meta/maps/:mapViewId'])
@Acl('mapViewGet')
async mapViewGet(@Param('mapViewId') mapViewId: string) {
return await this.mapsService.mapViewGet({ mapViewId });
async mapViewGet(
@TenantContext() context: NcContext,
@Param('mapViewId') mapViewId: string,
) {
return await this.mapsService.mapViewGet(context, { mapViewId });
}
@Post([
@ -34,11 +38,12 @@ export class MapsController {
@HttpCode(200)
@Acl('mapViewCreate')
async mapViewCreate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: ViewCreateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const view = await this.mapsService.mapViewCreate({
const view = await this.mapsService.mapViewCreate(context, {
tableId,
map: body,
user: req.user,
@ -50,12 +55,13 @@ export class MapsController {
@Patch(['/api/v1/db/meta/maps/:mapViewId', '/api/v2/meta/maps/:mapViewId'])
@Acl('mapViewUpdate')
async mapViewUpdate(
@TenantContext() context: NcContext,
@Param('mapViewId') mapViewId: string,
@Body() body: MapUpdateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.mapsService.mapViewUpdate({
return await this.mapsService.mapViewUpdate(context, {
mapViewId: mapViewId,
map: body,
req,

12
packages/nocodb/src/controllers/meta-diffs.controller.ts

@ -3,6 +3,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { MetaDiffsService } from '~/services/meta-diffs.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -14,8 +16,11 @@ export class MetaDiffsController {
'/api/v2/meta/bases/:baseId/meta-diff',
])
@Acl('metaDiff')
async metaDiff(@Param('baseId') baseId: string) {
return await this.metaDiffsService.metaDiff({ baseId });
async metaDiff(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
) {
return await this.metaDiffsService.metaDiff(context, { baseId });
}
@Get([
@ -24,10 +29,11 @@ export class MetaDiffsController {
])
@Acl('metaDiff')
async baseMetaDiff(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('sourceId') sourceId: string,
) {
return await this.metaDiffsService.baseMetaDiff({
return await this.metaDiffsService.baseMetaDiff(context, {
sourceId,
baseId,
});

9
packages/nocodb/src/controllers/model-visibilities.controller.ts

@ -13,7 +13,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { ModelVisibilitiesService } from '~/services/model-visibilities.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -29,11 +30,12 @@ export class ModelVisibilitiesController {
@HttpCode(200)
@Acl('modelVisibilitySet')
async xcVisibilityMetaSetAll(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Body() body: any,
@Req() req: NcRequest,
) {
await this.modelVisibilitiesService.xcVisibilityMetaSetAll({
await this.modelVisibilitiesService.xcVisibilityMetaSetAll(context, {
visibilityRule: body,
baseId,
req,
@ -48,10 +50,11 @@ export class ModelVisibilitiesController {
])
@Acl('modelVisibilityList')
async modelVisibilityList(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Query('includeM2M') includeM2M: boolean | string,
) {
return await this.modelVisibilitiesService.xcVisibilityMetaGet({
return await this.modelVisibilitiesService.xcVisibilityMetaGet(context, {
baseId,
includeM2M: includeM2M === true || includeM2M === 'true',
});

21
packages/nocodb/src/controllers/notifications.controller.ts

@ -18,8 +18,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { extractProps } from '~/helpers/extractProps';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { NcError } from '~/helpers/catchError';
import { PubSubRedis } from '~/redis/pubsub-redis';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
const nanoidv2 = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 14);
const POLL_INTERVAL = 30000;
@ -30,6 +30,9 @@ export class NotificationsController {
constructor(private readonly notificationsService: NotificationsService) {}
@Get('/api/v1/notifications/poll')
@Acl('notification', {
scope: 'org',
})
async notificationPoll(
@Req() req: NcRequest,
@Res()
@ -40,10 +43,6 @@ export class NotificationsController {
res.setHeader('Cache-Control', 'no-cache, must-revalidate');
res.resId = nanoidv2();
if (!req.user?.id) {
NcError.authenticationRequired();
}
this.notificationsService.addConnection(req.user.id, res);
if (PubSubRedis.available) {
@ -72,6 +71,9 @@ export class NotificationsController {
}
@Get('/api/v1/notifications')
@Acl('notification', {
scope: 'org',
})
async notificationList(@Req() req: NcRequest) {
return this.notificationsService.notificationList({
user: req.user,
@ -82,6 +84,9 @@ export class NotificationsController {
}
@Patch('/api/v1/notifications/:notificationId')
@Acl('notification', {
scope: 'org',
})
async notificationUpdate(
@Param('notificationId') notificationId,
@Body() body,
@ -95,6 +100,9 @@ export class NotificationsController {
}
@Delete('/api/v1/notifications/:notificationId')
@Acl('notification', {
scope: 'org',
})
async notificationDelete(
@Param('notificationId') notificationId,
@Req() req: NcRequest,
@ -106,6 +114,9 @@ export class NotificationsController {
}
@Post('/api/v1/notifications/mark-all-read')
@Acl('notification', {
scope: 'org',
})
@HttpCode(200)
async markAllRead(@Req() req: NcRequest) {
return this.notificationsService.markAllRead({

20
packages/nocodb/src/controllers/old-datas/old-datas.controller.ts

@ -15,6 +15,8 @@ import { OldDatasService } from './old-datas.service';
import { GlobalGuard } from '~/guards/global/global.guard';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { DataApiLimiterGuard } from '~/guards/data-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext } from '~/interface/config';
@Controller()
@UseGuards(DataApiLimiterGuard, GlobalGuard)
@ -24,13 +26,14 @@ export class OldDatasController {
@Get('/nc/:baseId/api/v1/:tableName')
@Acl('dataList')
async dataList(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('baseId') baseId: string,
@Param('tableName') tableName: string,
) {
res.json(
await this.oldDatasService.dataList({
await this.oldDatasService.dataList(context, {
query: req.query,
baseId: baseId,
tableName: tableName,
@ -41,13 +44,14 @@ export class OldDatasController {
@Get('/nc/:baseId/api/v1/:tableName/count')
@Acl('dataCount')
async dataCount(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('baseId') baseId: string,
@Param('tableName') tableName: string,
) {
res.json(
await this.oldDatasService.dataCount({
await this.oldDatasService.dataCount(context, {
query: req.query,
baseId: baseId,
tableName: tableName,
@ -59,6 +63,7 @@ export class OldDatasController {
@HttpCode(200)
@Acl('dataInsert')
async dataInsert(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('baseId') baseId: string,
@ -66,7 +71,7 @@ export class OldDatasController {
@Body() body: any,
) {
res.json(
await this.oldDatasService.dataInsert({
await this.oldDatasService.dataInsert(context, {
baseId: baseId,
tableName: tableName,
body: body,
@ -78,6 +83,7 @@ export class OldDatasController {
@Get('/nc/:baseId/api/v1/:tableName/:rowId')
@Acl('dataRead')
async dataRead(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('baseId') baseId: string,
@ -85,7 +91,7 @@ export class OldDatasController {
@Param('rowId') rowId: string,
) {
res.json(
await this.oldDatasService.dataRead({
await this.oldDatasService.dataRead(context, {
baseId: baseId,
tableName: tableName,
rowId: rowId,
@ -97,6 +103,7 @@ export class OldDatasController {
@Patch('/nc/:baseId/api/v1/:tableName/:rowId')
@Acl('dataUpdate')
async dataUpdate(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('baseId') baseId: string,
@ -104,7 +111,7 @@ export class OldDatasController {
@Param('rowId') rowId: string,
) {
res.json(
await this.oldDatasService.dataUpdate({
await this.oldDatasService.dataUpdate(context, {
baseId: baseId,
tableName: tableName,
body: req.body,
@ -117,6 +124,7 @@ export class OldDatasController {
@Delete('/nc/:baseId/api/v1/:tableName/:rowId')
@Acl('dataDelete')
async dataDelete(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('baseId') baseId: string,
@ -124,7 +132,7 @@ export class OldDatasController {
@Param('rowId') rowId: string,
) {
res.json(
await this.oldDatasService.dataDelete({
await this.oldDatasService.dataDelete(context, {
baseId: baseId,
tableName: tableName,
cookie: req,

87
packages/nocodb/src/controllers/old-datas/old-datas.service.ts

@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { nocoExecute } from 'nc-help';
import type { OldPathParams } from '~/helpers/dataHelpers';
import type { NcContext } from '~/interface/config';
import getAst from '~/helpers/getAst';
import { NcError } from '~/helpers/catchError';
import { Base, Model, Source, View } from '~/models';
@ -8,17 +9,20 @@ import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
@Injectable()
export class OldDatasService {
async dataList(param: OldPathParams & { query: any }) {
const { model, view } = await this.getViewAndModelFromRequest(param);
const source = await Source.get(model.source_id);
async dataList(context: NcContext, param: OldPathParams & { query: any }) {
const { model, view } = await this.getViewAndModelFromRequest(
context,
param,
);
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
});
const { ast } = await getAst({
const { ast } = await getAst(context, {
query: param.query,
model,
view,
@ -35,11 +39,14 @@ export class OldDatasService {
return await nocoExecute(ast, await baseModel.list(listArgs), {}, listArgs);
}
async dataCount(param: OldPathParams & { query: any }) {
const { model, view } = await this.getViewAndModelFromRequest(param);
const source = await Source.get(model.source_id);
async dataCount(context: NcContext, param: OldPathParams & { query: any }) {
const { model, view } = await this.getViewAndModelFromRequest(
context,
param,
);
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
@ -53,12 +60,18 @@ export class OldDatasService {
return await baseModel.count(listArgs);
}
async dataInsert(param: OldPathParams & { body: unknown; cookie: any }) {
const { model, view } = await this.getViewAndModelFromRequest(param);
async dataInsert(
context: NcContext,
param: OldPathParams & { body: unknown; cookie: any },
) {
const { model, view } = await this.getViewAndModelFromRequest(
context,
param,
);
const source = await Source.get(model.source_id);
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
@ -67,18 +80,24 @@ export class OldDatasService {
return await baseModel.insert(param.body, null, param.cookie);
}
async dataRead(param: OldPathParams & { query: any; rowId: string }) {
const { model, view } = await this.getViewAndModelFromRequest(param);
async dataRead(
context: NcContext,
param: OldPathParams & { query: any; rowId: string },
) {
const { model, view } = await this.getViewAndModelFromRequest(
context,
param,
);
const source = await Source.get(model.source_id);
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
});
const { ast } = await getAst({
const { ast } = await getAst(context, {
query: param.query,
model,
view,
@ -93,12 +112,16 @@ export class OldDatasService {
}
async dataUpdate(
context: NcContext,
param: OldPathParams & { body: unknown; cookie: any; rowId: string },
) {
const { model, view } = await this.getViewAndModelFromRequest(param);
const source = await Source.get(model.source_id);
const { model, view } = await this.getViewAndModelFromRequest(
context,
param,
);
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view.id,
dbDriver: await NcConnectionMgrv2.get(source),
@ -112,10 +135,16 @@ export class OldDatasService {
);
}
async dataDelete(param: OldPathParams & { rowId: string; cookie: any }) {
const { model, view } = await this.getViewAndModelFromRequest(param);
const source = await Source.get(model.source_id);
const baseModel = await Model.getBaseModelSQL({
async dataDelete(
context: NcContext,
param: OldPathParams & { rowId: string; cookie: any },
) {
const { model, view } = await this.getViewAndModelFromRequest(
context,
param,
);
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view.id,
dbDriver: await NcConnectionMgrv2.get(source),
@ -124,15 +153,15 @@ export class OldDatasService {
return await baseModel.delByPk(param.rowId, null, param.cookie);
}
async getViewAndModelFromRequest(req) {
const base = await Base.getWithInfo(req.params.baseId);
const model = await Model.getByAliasOrId({
async getViewAndModelFromRequest(context: NcContext, req) {
const base = await Base.getWithInfo(context, req.params.baseId);
const model = await Model.getByAliasOrId(context, {
base_id: base.id,
aliasOrId: req.params.tableName,
});
const view =
req.params.viewName &&
(await View.getByTitleOrId({
(await View.getByTitleOrId(context, {
titleOrId: req.params.viewName,
fk_model_id: model.id,
}));

8
packages/nocodb/src/controllers/org-tokens.controller.ts

@ -9,7 +9,6 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ApiTokenReqType } from 'nocodb-sdk';
import { AuthGuard } from '@nestjs/passport';
import { getConditionalHandler } from '~/helpers/getHandler';
@ -17,6 +16,7 @@ import { OrgTokensEeService } from '~/services/org-tokens-ee.service';
import { OrgTokensService } from '~/services/org-tokens.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
@UseGuards(MetaApiLimiterGuard, AuthGuard('jwt'))
@Controller()
@ -31,7 +31,7 @@ export class OrgTokensController {
scope: 'org',
blockApiTokenAccess: true,
})
async apiTokenList(@Req() req: Request) {
async apiTokenList(@Req() req: NcRequest) {
return await getConditionalHandler(
this.orgTokensService.apiTokenList,
this.orgTokensEeService.apiTokenListEE,
@ -47,7 +47,7 @@ export class OrgTokensController {
scope: 'org',
blockApiTokenAccess: true,
})
async apiTokenCreate(@Req() req: Request, @Body() body: ApiTokenReqType) {
async apiTokenCreate(@Req() req: NcRequest, @Body() body: ApiTokenReqType) {
return await this.orgTokensService.apiTokenCreate({
apiToken: body,
user: req['user'],
@ -61,7 +61,7 @@ export class OrgTokensController {
// allowedRoles: [OrgUserRoles.SUPER],
blockApiTokenAccess: true,
})
async apiTokenDelete(@Req() req: Request, @Param('token') token: string) {
async apiTokenDelete(@Req() req: NcRequest, @Param('token') token: string) {
await this.orgTokensService.apiTokenDelete({
token,
user: req['user'],

13
packages/nocodb/src/controllers/org-users.controller.ts

@ -10,7 +10,6 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { OrgUserRoles } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
@ -18,6 +17,7 @@ import { OrgUsersService } from '~/services/org-users.service';
import { User } from '~/models';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -30,7 +30,7 @@ export class OrgUsersController {
allowedRoles: [OrgUserRoles.SUPER_ADMIN],
blockApiTokenAccess: true,
})
async userList(@Req() req: Request) {
async userList(@Req() req: NcRequest) {
return new PagedResponseImpl(
await this.orgUsersService.userList({
query: req.query,
@ -76,7 +76,7 @@ export class OrgUsersController {
allowedRoles: [OrgUserRoles.SUPER_ADMIN],
blockApiTokenAccess: true,
})
async userAdd(@Body() body, @Req() req: Request) {
async userAdd(@Body() body, @Req() req: NcRequest) {
const result = await this.orgUsersService.userAdd({
user: req.body,
req,
@ -105,7 +105,7 @@ export class OrgUsersController {
blockApiTokenAccess: true,
})
async userInviteResend(
@Req() req: Request,
@Req() req: NcRequest,
@Param('userId') userId: string,
): Promise<any> {
await this.orgUsersService.userInviteResend({
@ -123,7 +123,10 @@ export class OrgUsersController {
allowedRoles: [OrgUserRoles.SUPER_ADMIN],
blockApiTokenAccess: true,
})
async generateResetUrl(@Req() req: Request, @Param('userId') userId: string) {
async generateResetUrl(
@Req() req: NcRequest,
@Param('userId') userId: string,
) {
const result = await this.orgUsersService.generateResetUrl({
siteUrl: req.ncSiteUrl,
userId,

45
packages/nocodb/src/controllers/public-datas-export.controller.ts

@ -17,6 +17,8 @@ import { PublicDatasExportService } from '~/services/public-datas-export.service
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
import { Column, Model, Source, View } from '~/models';
import { PublicApiLimiterGuard } from '~/guards/public-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext } from '~/interface/config';
@UseGuards(PublicApiLimiterGuard)
@Controller()
@ -30,11 +32,12 @@ export class PublicDatasExportController {
'/api/v2/public/shared-view/:publicDataUuid/rows/export/excel',
])
async exportExcel(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
@Param('publicDataUuid') publicDataUuid: string,
) {
const view = await View.getByUUID(publicDataUuid);
const view = await View.getByUUID(context, publicDataUuid);
if (!view) NcError.viewNotFound(publicDataUuid);
if (
view.type !== ViewTypes.GRID &&
@ -49,11 +52,16 @@ export class PublicDatasExportController {
NcError.invalidSharedViewPassword();
}
const model = await view.getModelWithInfo();
const model = await view.getModelWithInfo(context);
await view.getColumns();
await view.getColumns(context);
const { offset, dbRows, elapsed } = await this.getDbRows(model, view, req);
const { offset, dbRows, elapsed } = await this.getDbRows(
context,
model,
view,
req,
);
const fields = req.query.fields as string[];
@ -85,8 +93,12 @@ export class PublicDatasExportController {
'/api/v1/db/public/shared-view/:publicDataUuid/rows/export/csv',
'/api/v2/public/shared-view/:publicDataUuid/rows/export/csv',
])
async exportCsv(@Request() req, @Response() res) {
const view = await View.getByUUID(req.params.publicDataUuid);
async exportCsv(
@TenantContext() context: NcContext,
@Request() req,
@Response() res,
) {
const view = await View.getByUUID(context, req.params.publicDataUuid);
const fields = req.query.fields;
if (!view) NcError.viewNotFound(req.params.publicDataUuid);
@ -103,10 +115,15 @@ export class PublicDatasExportController {
NcError.invalidSharedViewPassword();
}
const model = await view.getModelWithInfo();
await view.getColumns();
const model = await view.getModelWithInfo(context);
await view.getColumns(context);
const { offset, dbRows, elapsed } = await this.getDbRows(model, view, req);
const { offset, dbRows, elapsed } = await this.getDbRows(
context,
model,
view,
req,
);
const data = papaparse.unparse(
{
@ -142,7 +159,7 @@ export class PublicDatasExportController {
res.send(data);
}
async getDbRows(model, view: View, req) {
async getDbRows(@TenantContext() context: NcContext, model, view: View, req) {
view.model.columns = view.columns
.filter((c) => c.show)
.map(
@ -164,14 +181,14 @@ export class PublicDatasExportController {
listArgs.sortArr = JSON.parse(listArgs.sortArrJson);
} catch (e) {}
const source = await Source.get(model.source_id);
const baseModel = await Model.getBaseModelSQL({
const source = await Source.get(context, model.source_id);
const baseModel = await Model.getBaseModelSQL(context, {
id: model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
});
const { ast } = await getAst({
const { ast } = await getAst(context, {
query: req.query,
model,
view,
@ -209,7 +226,7 @@ export class PublicDatasExportController {
const dbRow = { ...row };
for (const column of view.model.columns) {
dbRow[column.title] = await serializeCellValue({
dbRow[column.title] = await serializeCellValue(context, {
value: row[column.title],
column,
siteUrl: req.ncSiteUrl,

68
packages/nocodb/src/controllers/public-datas.controller.ts

@ -8,10 +8,11 @@ import {
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { Request } from 'express';
import { AnyFilesInterceptor } from '@nestjs/platform-express';
import { PublicDatasService } from '~/services/public-datas.service';
import { PublicApiLimiterGuard } from '~/guards/public-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@UseGuards(PublicApiLimiterGuard)
@Controller()
@ -23,10 +24,11 @@ export class PublicDatasController {
'/api/v2/public/shared-view/:sharedViewUuid/rows',
])
async dataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
) {
const pagedResponse = await this.publicDatasService.dataList({
const pagedResponse = await this.publicDatasService.dataList(context, {
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid,
@ -39,10 +41,11 @@ export class PublicDatasController {
'/api/v2/public/shared-view/:sharedViewUuid/groupby',
])
async dataGroupBy(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
) {
return await this.publicDatasService.dataGroupBy({
return await this.publicDatasService.dataGroupBy(context, {
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
@ -54,11 +57,12 @@ export class PublicDatasController {
'/api/v2/public/shared-view/:sharedViewUuid/group/:columnId',
])
async groupedDataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
@Param('columnId') columnId: string,
) {
const groupedData = await this.publicDatasService.groupedDataList({
const groupedData = await this.publicDatasService.groupedDataList(context, {
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
@ -74,10 +78,11 @@ export class PublicDatasController {
@HttpCode(200)
@UseInterceptors(AnyFilesInterceptor())
async dataInsert(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
) {
const insertResult = await this.publicDatasService.dataInsert({
const insertResult = await this.publicDatasService.dataInsert(context, {
sharedViewUuid: sharedViewUuid,
password: req.headers?.['xc-password'] as string,
body: req.body?.data,
@ -93,7 +98,8 @@ export class PublicDatasController {
'/api/v2/public/shared-view/:sharedViewUuid/nested/:columnId',
])
async relDataList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
@Param('columnId') columnId: string,
) {
@ -107,7 +113,7 @@ export class PublicDatasController {
}
}
const pagedResponse = await this.publicDatasService.relDataList({
const pagedResponse = await this.publicDatasService.relDataList(context, {
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
@ -123,18 +129,22 @@ export class PublicDatasController {
'/api/v2/public/shared-view/:sharedViewUuid/rows/:rowId/mm/:columnId',
])
async publicMmList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
@Param('rowId') rowId: string,
@Param('columnId') columnId: string,
) {
const paginatedResponse = await this.publicDatasService.publicMmList({
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
columnId: columnId,
rowId: rowId,
});
const paginatedResponse = await this.publicDatasService.publicMmList(
context,
{
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
columnId: columnId,
rowId: rowId,
},
);
return paginatedResponse;
}
@ -143,18 +153,22 @@ export class PublicDatasController {
'/api/v2/public/shared-view/:sharedViewUuid/rows/:rowId/hm/:columnId',
])
async publicHmList(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
@Param('rowId') rowId: string,
@Param('columnId') columnId: string,
) {
const paginatedResponse = await this.publicDatasService.publicHmList({
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
columnId: columnId,
rowId: rowId,
});
const paginatedResponse = await this.publicDatasService.publicHmList(
context,
{
query: req.query,
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
columnId: columnId,
rowId: rowId,
},
);
return paginatedResponse;
}
}

11
packages/nocodb/src/controllers/public-metas.controller.ts

@ -1,7 +1,8 @@
import { Controller, Get, Param, Req, UseGuards } from '@nestjs/common';
import { Request } from 'express';
import { PublicMetasService } from '~/services/public-metas.service';
import { PublicApiLimiterGuard } from '~/guards/public-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@UseGuards(PublicApiLimiterGuard)
@Controller()
@ -13,10 +14,11 @@ export class PublicMetasController {
'/api/v2/public/shared-view/:sharedViewUuid/meta',
])
async viewMetaGet(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('sharedViewUuid') sharedViewUuid: string,
) {
return await this.publicMetasService.viewMetaGet({
return await this.publicMetasService.viewMetaGet(context, {
password: req.headers?.['xc-password'] as string,
sharedViewUuid: sharedViewUuid,
});
@ -27,9 +29,10 @@ export class PublicMetasController {
'/api/v2/public/shared-base/:sharedBaseUuid/meta',
])
async publicSharedBaseGet(
@TenantContext() context: NcContext,
@Param('sharedBaseUuid') sharedBaseUuid: string,
): Promise<any> {
return await this.publicMetasService.publicSharedBaseGet({
return await this.publicMetasService.publicSharedBaseGet(context, {
sharedBaseUuid,
});
}

71
packages/nocodb/src/controllers/shared-bases.controller.ts

@ -10,11 +10,12 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { SharedBasesService } from '~/services/shared-bases.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -28,17 +29,21 @@ export class SharedBasesController {
@HttpCode(200)
@Acl('createSharedBaseLink')
async createSharedBaseLink(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Body() body: any,
@Param('baseId') baseId: string,
): Promise<any> {
const sharedBase = await this.sharedBasesService.createSharedBaseLink({
baseId: baseId,
roles: body?.roles,
password: body?.password,
siteUrl: req.ncSiteUrl,
req,
});
const sharedBase = await this.sharedBasesService.createSharedBaseLink(
context,
{
baseId: baseId,
roles: body?.roles,
password: body?.password,
siteUrl: req.ncSiteUrl,
req,
},
);
return sharedBase;
}
@ -49,17 +54,21 @@ export class SharedBasesController {
])
@Acl('updateSharedBaseLink')
async updateSharedBaseLink(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Body() body: any,
@Param('baseId') baseId: string,
): Promise<any> {
const sharedBase = await this.sharedBasesService.updateSharedBaseLink({
baseId: baseId,
roles: body?.roles,
password: body?.password,
siteUrl: req.ncSiteUrl,
req,
});
const sharedBase = await this.sharedBasesService.updateSharedBaseLink(
context,
{
baseId: baseId,
roles: body?.roles,
password: body?.password,
siteUrl: req.ncSiteUrl,
req,
},
);
return sharedBase;
}
@ -70,13 +79,17 @@ export class SharedBasesController {
])
@Acl('disableSharedBaseLink')
async disableSharedBaseLink(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Req() req: Request,
@Req() req: NcRequest,
): Promise<any> {
const sharedBase = await this.sharedBasesService.disableSharedBaseLink({
baseId,
req,
});
const sharedBase = await this.sharedBasesService.disableSharedBaseLink(
context,
{
baseId,
req,
},
);
return sharedBase;
}
@ -87,13 +100,17 @@ export class SharedBasesController {
])
@Acl('getSharedBaseLink')
async getSharedBaseLink(
@Req() req: Request,
@TenantContext() context: NcContext,
@Req() req: NcRequest,
@Param('baseId') baseId: string,
): Promise<any> {
const sharedBase = await this.sharedBasesService.getSharedBaseLink({
baseId: baseId,
siteUrl: req.ncSiteUrl,
});
const sharedBase = await this.sharedBasesService.getSharedBaseLink(
context,
{
baseId: baseId,
siteUrl: req.ncSiteUrl,
},
);
return sharedBase;
}

31
packages/nocodb/src/controllers/sorts.controller.ts

@ -16,7 +16,8 @@ import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { SortsService } from '~/services/sorts.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -28,9 +29,12 @@ export class SortsController {
'/api/v2/meta/views/:viewId/sorts/',
])
@Acl('sortList')
async sortList(@Param('viewId') viewId: string) {
async sortList(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
) {
return new PagedResponseImpl(
await this.sortsService.sortList({
await this.sortsService.sortList(context, {
viewId,
}),
);
@ -43,11 +47,12 @@ export class SortsController {
@HttpCode(200)
@Acl('sortCreate')
async sortCreate(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Body() body: SortReqType,
@Req() req: NcRequest,
) {
const sort = await this.sortsService.sortCreate({
const sort = await this.sortsService.sortCreate(context, {
sort: body,
viewId,
req,
@ -57,8 +62,11 @@ export class SortsController {
@Get(['/api/v1/db/meta/sorts/:sortId', '/api/v2/meta/sorts/:sortId'])
@Acl('sortGet')
async sortGet(@Param('sortId') sortId: string) {
const sort = await this.sortsService.sortGet({
async sortGet(
@TenantContext() context: NcContext,
@Param('sortId') sortId: string,
) {
const sort = await this.sortsService.sortGet(context, {
sortId,
});
return sort;
@ -67,11 +75,12 @@ export class SortsController {
@Patch(['/api/v1/db/meta/sorts/:sortId', '/api/v2/meta/sorts/:sortId'])
@Acl('sortUpdate')
async sortUpdate(
@TenantContext() context: NcContext,
@Param('sortId') sortId: string,
@Body() body: SortReqType,
@Req() req: NcRequest,
) {
const sort = await this.sortsService.sortUpdate({
const sort = await this.sortsService.sortUpdate(context, {
sortId,
sort: body,
req,
@ -81,8 +90,12 @@ export class SortsController {
@Delete(['/api/v1/db/meta/sorts/:sortId', '/api/v2/meta/sorts/:sortId'])
@Acl('sortDelete')
async sortDelete(@Param('sortId') sortId: string, @Req() req: NcRequest) {
const sort = await this.sortsService.sortDelete({
async sortDelete(
@TenantContext() context: NcContext,
@Param('sortId') sortId: string,
@Req() req: NcRequest,
) {
const sort = await this.sortsService.sortDelete(context, {
sortId,
req,
});

22
packages/nocodb/src/controllers/sources.controller.ts

@ -7,13 +7,14 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { BaseReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { SourcesService } from '~/services/sources.service';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -25,8 +26,11 @@ export class SourcesController {
'/api/v2/meta/bases/:baseId/sources/:sourceId',
])
@Acl('baseGet')
async baseGet(@Param('sourceId') sourceId: string) {
const source = await this.sourcesService.baseGetWithConfig({
async baseGet(
@TenantContext() context: NcContext,
@Param('sourceId') sourceId: string,
) {
const source = await this.sourcesService.baseGetWithConfig(context, {
sourceId,
});
@ -43,12 +47,13 @@ export class SourcesController {
])
@Acl('baseUpdate')
async baseUpdate(
@TenantContext() context: NcContext,
@Param('sourceId') sourceId: string,
@Param('baseId') baseId: string,
@Body() body: BaseReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const source = await this.sourcesService.baseUpdate({
const source = await this.sourcesService.baseUpdate(context, {
sourceId,
source: body,
baseId,
@ -63,8 +68,11 @@ export class SourcesController {
'/api/v2/meta/bases/:baseId/sources',
])
@Acl('baseList')
async baseList(@Param('baseId') baseId: string) {
const sources = await this.sourcesService.baseList({
async baseList(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
) {
const sources = await this.sourcesService.baseList(context, {
baseId,
});

5
packages/nocodb/src/controllers/sql-views.controller.ts

@ -10,6 +10,8 @@ import { SqlViewsService } from '~/services/sql-views.service';
import { GlobalGuard } from '~/guards/global/global.guard';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -22,12 +24,13 @@ export class SqlViewsController {
])
@Acl('sqlViewCreate')
async sqlViewCreate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('sourceId') sourceId: string,
@Request() req,
@Body() body,
) {
const table = await this.sqlViewsService.sqlViewCreate({
const table = await this.sqlViewsService.sqlViewCreate(context, {
clientIp: (req as any).clientIp,
body,
baseId,

20
packages/nocodb/src/controllers/sync.controller.ts

@ -14,7 +14,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { SyncService } from '~/services/sync.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -29,10 +30,11 @@ export class SyncController {
])
@Acl('syncSourceList')
async syncSourceList(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('sourceId') sourceId?: string,
) {
return await this.syncService.syncSourceList({
return await this.syncService.syncSourceList(context, {
baseId,
sourceId,
});
@ -47,12 +49,13 @@ export class SyncController {
@HttpCode(200)
@Acl('syncSourceCreate')
async syncCreate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Body() body: any,
@Req() req: NcRequest,
@Param('sourceId') sourceId?: string,
) {
return await this.syncService.syncCreate({
return await this.syncService.syncCreate(context, {
baseId: baseId,
sourceId: sourceId,
userId: (req as any).user.id,
@ -63,8 +66,12 @@ export class SyncController {
@Delete(['/api/v1/db/meta/syncs/:syncId', '/api/v2/meta/syncs/:syncId'])
@Acl('syncSourceDelete')
async syncDelete(@Param('syncId') syncId: string, @Req() req: NcRequest) {
return await this.syncService.syncDelete({
async syncDelete(
@TenantContext() context: NcContext,
@Param('syncId') syncId: string,
@Req() req: NcRequest,
) {
return await this.syncService.syncDelete(context, {
syncId: syncId,
req,
});
@ -73,11 +80,12 @@ export class SyncController {
@Patch(['/api/v1/db/meta/syncs/:syncId', '/api/v2/meta/syncs/:syncId'])
@Acl('syncSourceUpdate')
async syncUpdate(
@TenantContext() context: NcContext,
@Param('syncId') syncId: string,
@Body() body: any,
@Req() req: NcRequest,
) {
return await this.syncService.syncUpdate({
return await this.syncService.syncUpdate(context, {
syncId: syncId,
syncPayload: body,
req,

39
packages/nocodb/src/controllers/tables.controller.ts

@ -17,6 +17,8 @@ import { TablesService } from '~/services/tables.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -31,13 +33,14 @@ export class TablesController {
])
@Acl('tableList')
async tableList(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('sourceId') sourceId: string,
@Query('includeM2M') includeM2M: string,
@Request() req,
) {
return new PagedResponseImpl(
await this.tablesService.getAccessibleTables({
await this.tablesService.getAccessibleTables(context, {
baseId,
sourceId,
includeM2M: includeM2M === 'true',
@ -55,12 +58,13 @@ export class TablesController {
@HttpCode(200)
@Acl('tableCreate')
async tableCreate(
@TenantContext() context: NcContext,
@Param('baseId') baseId: string,
@Param('sourceId') sourceId: string,
@Body() body: TableReqType,
@Request() req,
) {
const result = await this.tablesService.tableCreate({
const result = await this.tablesService.tableCreate(context, {
baseId: baseId,
sourceId: sourceId,
table: body,
@ -72,11 +76,18 @@ export class TablesController {
@Get(['/api/v1/db/meta/tables/:tableId', '/api/v2/meta/tables/:tableId'])
@Acl('tableGet')
async tableGet(@Param('tableId') tableId: string, @Request() req) {
const table = await this.tablesService.getTableWithAccessibleViews({
tableId: req.params.tableId,
user: req.user,
});
async tableGet(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Request() req,
) {
const table = await this.tablesService.getTableWithAccessibleViews(
context,
{
tableId: req.params.tableId,
user: req.user,
},
);
return table;
}
@ -84,11 +95,12 @@ export class TablesController {
@Patch(['/api/v1/db/meta/tables/:tableId', '/api/v2/meta/tables/:tableId'])
@Acl('tableUpdate')
async tableUpdate(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: TableReqType,
@Request() req,
) {
await this.tablesService.tableUpdate({
await this.tablesService.tableUpdate(context, {
tableId: tableId,
table: body,
baseId: req.ncBaseId,
@ -100,8 +112,12 @@ export class TablesController {
@Delete(['/api/v1/db/meta/tables/:tableId', '/api/v2/meta/tables/:tableId'])
@Acl('tableDelete')
async tableDelete(@Param('tableId') tableId: string, @Request() req) {
const result = await this.tablesService.tableDelete({
async tableDelete(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Request() req,
) {
const result = await this.tablesService.tableDelete(context, {
tableId: req.params.tableId,
user: (req as any).user,
req,
@ -117,10 +133,11 @@ export class TablesController {
@Acl('tableReorder')
@HttpCode(200)
async tableReorder(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Body() body: { order: number },
) {
return this.tablesService.reorderTable({
return this.tablesService.reorderTable(context, {
tableId,
order: body.order,
});

2
packages/nocodb/src/controllers/users/users.controller.ts

@ -15,6 +15,8 @@ import { GlobalGuard } from '~/guards/global/global.guard';
import { AppHooksService } from '~/services/app-hooks/app-hooks.service';
import { UsersService } from '~/services/users/users.service';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
export class UsersController {

6
packages/nocodb/src/controllers/utils.controller.ts

@ -9,13 +9,13 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { GlobalGuard } from '~/guards/global/global.guard';
import { UtilsService } from '~/services/utils.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { PublicApiLimiterGuard } from '~/guards/public-api-limiter.guard';
import { TelemetryService } from '~/services/telemetry.service';
import { NcRequest } from '~/interface/config';
@Controller()
export class UtilsController {
@ -49,7 +49,7 @@ export class UtilsController {
scope: 'org',
})
@HttpCode(200)
async testConnection(@Body() body: any, @Req() _req: Request) {
async testConnection(@Body() body: any, @Req() _req: NcRequest) {
body.pool = {
min: 0,
max: 1,
@ -64,7 +64,7 @@ export class UtilsController {
'/api/v2/meta/nocodb/info',
'/api/v1/meta/nocodb/info',
])
async appInfo(@Req() req: Request) {
async appInfo(@Req() req: NcRequest) {
return await this.utilsService.appInfo({
req: {
ncSiteUrl: (req as any).ncSiteUrl,

36
packages/nocodb/src/controllers/view-columns.controller.ts

@ -22,7 +22,8 @@ import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { ViewColumnsService } from '~/services/view-columns.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { NcRequest } from '~/interface/config';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -34,9 +35,12 @@ export class ViewColumnsController {
'/api/v2/meta/views/:viewId/columns/',
])
@Acl('columnList')
async columnList(@Param('viewId') viewId: string) {
async columnList(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
) {
return new PagedResponseImpl(
await this.viewColumnsService.columnList({
await this.viewColumnsService.columnList(context, {
viewId,
}),
);
@ -49,11 +53,12 @@ export class ViewColumnsController {
@HttpCode(200)
@Acl('columnAdd')
async columnAdd(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Body() body: ViewColumnReqType,
@Req() req: NcRequest,
) {
const viewColumn = await this.viewColumnsService.columnAdd({
const viewColumn = await this.viewColumnsService.columnAdd(context, {
viewId,
column: body,
req,
@ -67,12 +72,13 @@ export class ViewColumnsController {
])
@Acl('viewColumnUpdate')
async viewColumnUpdate(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Param('columnId') columnId: string,
@Body() body: ViewColumnReqType,
@Req() req: NcRequest,
) {
const result = await this.viewColumnsService.columnUpdate({
const result = await this.viewColumnsService.columnUpdate(context, {
viewId,
columnId,
column: body,
@ -84,6 +90,7 @@ export class ViewColumnsController {
@Patch('/api/v3/meta/views/:viewId/columns')
@Acl('columnUpdate')
async viewColumnUpdateV3(
@TenantContext() context: NcContext,
@Req() req,
@Param('viewId') viewId: string,
@Body()
@ -106,7 +113,7 @@ export class ViewColumnsController {
>,
) {
return new PagedResponseImpl(
await this.viewColumnsService.columnsUpdate({
await this.viewColumnsService.columnsUpdate(context, {
viewId,
columns: body,
req,
@ -116,12 +123,19 @@ export class ViewColumnsController {
@Get('/api/v3/meta/views/:viewId/columns')
@Acl('columnList')
async viewColumnListV3(@Req() req, @Param('viewId') viewId: string) {
async viewColumnListV3(
@TenantContext() context: NcContext,
@Req() req,
@Param('viewId') viewId: string,
) {
return {
[APIContext.VIEW_COLUMNS]: await this.viewColumnsService.viewColumnList({
viewId,
req,
}),
[APIContext.VIEW_COLUMNS]: await this.viewColumnsService.viewColumnList(
context,
{
viewId,
req,
},
),
};
}
}

62
packages/nocodb/src/controllers/views.controller.ts

@ -11,13 +11,14 @@ import {
Req,
UseGuards,
} from '@nestjs/common';
import { Request } from 'express';
import { ViewUpdateReqType } from 'nocodb-sdk';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
import { GlobalGuard } from '~/guards/global/global.guard';
import { ViewsService } from '~/services/views.service';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { MetaApiLimiterGuard } from '~/guards/meta-api-limiter.guard';
import { TenantContext } from '~/decorators/tenant-context.decorator';
import { NcContext, NcRequest } from '~/interface/config';
@Controller()
@UseGuards(MetaApiLimiterGuard, GlobalGuard)
@ -29,9 +30,13 @@ export class ViewsController {
'/api/v2/meta/tables/:tableId/views',
])
@Acl('viewList')
async viewList(@Param('tableId') tableId: string, @Req() req: Request) {
async viewList(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
@Req() req: NcRequest,
) {
return new PagedResponseImpl(
await this.viewsService.viewList({
await this.viewsService.viewList(context, {
tableId,
user: req.user,
}),
@ -41,11 +46,12 @@ export class ViewsController {
@Patch(['/api/v1/db/meta/views/:viewId', '/api/v2/meta/views/:viewId'])
@Acl('viewUpdate')
async viewUpdate(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Body() body: ViewUpdateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
const result = await this.viewsService.viewUpdate({
const result = await this.viewsService.viewUpdate(context, {
viewId,
view: body,
user: req.user,
@ -56,8 +62,12 @@ export class ViewsController {
@Delete(['/api/v1/db/meta/views/:viewId', '/api/v2/meta/views/:viewId'])
@Acl('viewDelete')
async viewDelete(@Param('viewId') viewId: string, @Req() req: Request) {
const result = await this.viewsService.viewDelete({
async viewDelete(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Req() req: NcRequest,
) {
const result = await this.viewsService.viewDelete(context, {
viewId,
user: req.user,
req,
@ -72,10 +82,11 @@ export class ViewsController {
@HttpCode(200)
@Acl('showAllColumns')
async showAllColumns(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Query('ignoreIds') ignoreIds: string[],
) {
return await this.viewsService.showAllColumns({
return await this.viewsService.showAllColumns(context, {
viewId,
ignoreIds,
});
@ -87,10 +98,11 @@ export class ViewsController {
@HttpCode(200)
@Acl('hideAllColumns')
async hideAllColumns(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Query('ignoreIds') ignoreIds: string[],
) {
return await this.viewsService.hideAllColumns({
return await this.viewsService.hideAllColumns(context, {
viewId,
ignoreIds,
});
@ -102,8 +114,16 @@ export class ViewsController {
])
@HttpCode(200)
@Acl('shareView')
async shareView(@Param('viewId') viewId: string, @Req() req: Request) {
return await this.viewsService.shareView({ viewId, user: req.user, req });
async shareView(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Req() req: NcRequest,
) {
return await this.viewsService.shareView(context, {
viewId,
user: req.user,
req,
});
}
@Get([
@ -111,9 +131,12 @@ export class ViewsController {
'/api/v2/meta/tables/:tableId/share',
])
@Acl('shareViewList')
async shareViewList(@Param('tableId') tableId: string) {
async shareViewList(
@TenantContext() context: NcContext,
@Param('tableId') tableId: string,
) {
return new PagedResponseImpl(
await this.viewsService.shareViewList({
await this.viewsService.shareViewList(context, {
tableId,
}),
);
@ -125,11 +148,12 @@ export class ViewsController {
])
@Acl('shareViewUpdate')
async shareViewUpdate(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Body() body: ViewUpdateReqType,
@Req() req: Request,
@Req() req: NcRequest,
) {
return await this.viewsService.shareViewUpdate({
return await this.viewsService.shareViewUpdate(context, {
viewId,
sharedView: body,
user: req.user,
@ -142,8 +166,12 @@ export class ViewsController {
'/api/v2/meta/views/:viewId/share',
])
@Acl('shareViewDelete')
async shareViewDelete(@Param('viewId') viewId: string, @Req() req: Request) {
return await this.viewsService.shareViewDelete({
async shareViewDelete(
@TenantContext() context: NcContext,
@Param('viewId') viewId: string,
@Req() req: NcRequest,
) {
return await this.viewsService.shareViewDelete(context, {
viewId,
user: req.user,
req,

1080
packages/nocodb/src/db/BaseModelSqlv2.ts

File diff suppressed because it is too large Load Diff

121
packages/nocodb/src/db/conditionV2.ts

@ -74,6 +74,8 @@ const parseConditionV2 = async (
) => {
const knex = baseModelSqlv2.dbDriver;
const context = baseModelSqlv2.context;
let filter: Filter;
if (!Array.isArray(_filter)) {
if (!(_filter instanceof Filter)) filter = new Filter(_filter as Filter);
@ -101,7 +103,7 @@ const parseConditionV2 = async (
});
};
} else if (filter.is_group) {
const children = await filter.getChildren();
const children = await filter.getChildren(context);
const qbs = await Promise.all(
(children || []).map((child) =>
@ -134,13 +136,16 @@ const parseConditionV2 = async (
) {
(filter as any).groupby = true;
const column = await getRefColumnIfAlias(await filter.getColumn());
const column = await getRefColumnIfAlias(
context,
await filter.getColumn(context),
);
if (
column.uidt === UITypes.Lookup ||
column.uidt === UITypes.LinkToAnotherRecord
) {
const model = await column.getModel();
const model = await column.getModel(context);
const lkQb = await generateLookupSelectQuery({
baseModelSqlv2,
alias: alias,
@ -159,12 +164,15 @@ const parseConditionV2 = async (
// if qrCode or Barcode replace it with value column
if ([UITypes.QrCode, UITypes.Barcode].includes(column.uidt))
filter.fk_column_id = await column
.getColOptions<BarcodeColumn | QrCodeColumn>()
.getColOptions<BarcodeColumn | QrCodeColumn>(context)
.then((col) => col.fk_column_id);
}
}
const column = await getRefColumnIfAlias(await filter.getColumn());
const column = await getRefColumnIfAlias(
context,
await filter.getColumn(context),
);
if (!column) {
if (throwErrorIfInvalid) {
NcError.fieldNotFound(filter.fk_column_id);
@ -172,14 +180,15 @@ const parseConditionV2 = async (
return;
}
if (column.uidt === UITypes.LinkToAnotherRecord) {
const colOptions =
(await column.getColOptions()) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const colOptions = (await column.getColOptions(
context,
)) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn(context);
const parentColumn = await colOptions.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
let relationType = colOptions.type;
@ -325,9 +334,9 @@ const parseConditionV2 = async (
} else qbP.whereIn(childColumn.column_name, selectQb);
};
} else if (relationType === RelationTypes.MANY_TO_MANY) {
const mmModel = await colOptions.getMMModel();
const mmParentColumn = await colOptions.getMMParentColumn();
const mmChildColumn = await colOptions.getMMChildColumn();
const mmModel = await colOptions.getMMModel(context);
const mmParentColumn = await colOptions.getMMParentColumn(context);
const mmChildColumn = await colOptions.getMMChildColumn(context);
if (
['blank', 'notblank', 'checked', 'notchecked'].includes(
@ -428,7 +437,7 @@ const parseConditionV2 = async (
baseModelSqlv2,
knex,
alias,
columnOptions: (await column.getColOptions()) as RollupColumn,
columnOptions: (await column.getColOptions(context)) as RollupColumn,
})
).builder;
return parseConditionV2(
@ -445,8 +454,8 @@ const parseConditionV2 = async (
builder,
);
} else if (column.uidt === UITypes.Formula && !customWhereClause) {
const model = await column.getModel();
const formula = await column.getColOptions<FormulaColumn>();
const model = await column.getModel(context);
const formula = await column.getColOptions<FormulaColumn>(context);
const builder = (
await formulaQueryBuilderv2(
baseModelSqlv2,
@ -479,9 +488,9 @@ const parseConditionV2 = async (
['like', 'nlike'].includes(filter.comparison_op)
) {
// get column name for CreatedBy, LastModifiedBy
column.column_name = await getColumnName(column);
column.column_name = await getColumnName(context, column);
const baseUsers = await BaseUser.getUsersList({
const baseUsers = await BaseUser.getUsersList(context, {
base_id: column.base_id,
});
return (qb: Knex.QueryBuilder) => {
@ -573,7 +582,7 @@ const parseConditionV2 = async (
const _val = customWhereClause ? customWhereClause : filter.value;
// get column name for CreateTime, LastModifiedTime
column.column_name = await getColumnName(column);
column.column_name = await getColumnName(context, column);
return (qb: Knex.QueryBuilder) => {
let [field, val] = [_field, _val];
@ -1240,21 +1249,23 @@ async function generateLookupCondition(
aliasCount = { count: 0 },
throwErrorIfInvalid = false,
): Promise<any> {
const colOptions = await col.getColOptions<LookupColumn>();
const relationColumn = await colOptions.getRelationColumn();
const context = baseModelSqlv2.context;
const colOptions = await col.getColOptions<LookupColumn>(context);
const relationColumn = await colOptions.getRelationColumn(context);
const relationColumnOptions =
await relationColumn.getColOptions<LinkToAnotherRecordColumn>();
await relationColumn.getColOptions<LinkToAnotherRecordColumn>(context);
// const relationModel = await relationColumn.getModel();
const lookupColumn = await colOptions.getLookupColumn();
const lookupColumn = await colOptions.getLookupColumn(context);
const alias = getAlias(aliasCount);
let qb;
{
const childColumn = await relationColumnOptions.getChildColumn();
const parentColumn = await relationColumnOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relationColumnOptions.getChildColumn(context);
const parentColumn = await relationColumnOptions.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
let relationType = relationColumnOptions.type;
@ -1330,9 +1341,13 @@ async function generateLookupCondition(
else qbP.whereIn(childColumn.column_name, qb);
};
} else if (relationType === RelationTypes.MANY_TO_MANY) {
const mmModel = await relationColumnOptions.getMMModel();
const mmParentColumn = await relationColumnOptions.getMMParentColumn();
const mmChildColumn = await relationColumnOptions.getMMChildColumn();
const mmModel = await relationColumnOptions.getMMModel(context);
const mmParentColumn = await relationColumnOptions.getMMParentColumn(
context,
);
const mmChildColumn = await relationColumnOptions.getMMChildColumn(
context,
);
const childAlias = `__nc${aliasCount.count++}`;
@ -1391,6 +1406,8 @@ async function nestedConditionJoin(
aliasCount: { count: number },
throwErrorIfInvalid = false,
) {
const context = baseModelSqlv2.context;
if (
lookupColumn.uidt === UITypes.Lookup ||
lookupColumn.uidt === UITypes.LinkToAnotherRecord
@ -1398,19 +1415,19 @@ async function nestedConditionJoin(
const relationColumn =
lookupColumn.uidt === UITypes.Lookup
? await (
await lookupColumn.getColOptions<LookupColumn>()
).getRelationColumn()
await lookupColumn.getColOptions<LookupColumn>(context)
).getRelationColumn(context)
: lookupColumn;
const relationColOptions =
await relationColumn.getColOptions<LinkToAnotherRecordColumn>();
await relationColumn.getColOptions<LinkToAnotherRecordColumn>(context);
const relAlias = `__nc${aliasCount.count++}`;
const childColumn = await relationColOptions.getChildColumn();
const parentColumn = await relationColOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relationColOptions.getChildColumn(context);
const parentColumn = await relationColOptions.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
{
switch (relationColOptions.type) {
case RelationTypes.HAS_MANY:
@ -1439,9 +1456,13 @@ async function nestedConditionJoin(
break;
case 'mm':
{
const mmModel = await relationColOptions.getMMModel();
const mmParentColumn = await relationColOptions.getMMParentColumn();
const mmChildColumn = await relationColOptions.getMMChildColumn();
const mmModel = await relationColOptions.getMMModel(context);
const mmParentColumn = await relationColOptions.getMMParentColumn(
context,
);
const mmChildColumn = await relationColOptions.getMMChildColumn(
context,
);
const assocAlias = `__nc${aliasCount.count++}`;
@ -1470,8 +1491,8 @@ async function nestedConditionJoin(
baseModelSqlv2,
filter,
await (
await lookupColumn.getColOptions<LookupColumn>()
).getLookupColumn(),
await lookupColumn.getColOptions<LookupColumn>(context)
).getLookupColumn(context),
qb,
knex,
relAlias,
@ -1542,7 +1563,7 @@ async function nestedConditionJoin(
baseModelSqlv2,
new Filter({
...filter,
fk_model_id: (await lookupColumn.getModel()).id,
fk_model_id: (await lookupColumn.getModel(context)).id,
fk_column_id: lookupColumn?.id,
}),
aliasCount,

170
packages/nocodb/src/db/formulav2/formulaQueryBuilderv2.ts

@ -70,7 +70,9 @@ async function _formulaQueryBuilder(
) {
const knex = baseModelSqlv2.dbDriver;
const columns = await model.getColumns();
const context = baseModelSqlv2.context;
const columns = await model.getColumns(context);
let tree = parsedTree;
if (!tree) {
@ -92,15 +94,15 @@ async function _formulaQueryBuilder(
| 'sqlite'
| 'snowflake',
getMeta: async (modelId) => {
const model = await Model.get(modelId);
await model.getColumns();
const model = await Model.get(context, modelId);
await model.getColumns(context);
return model;
},
});
// populate and save parsedTree to column if not exist
if (column) {
FormulaColumn.update(column.id, { parsed_tree: tree }).then(
FormulaColumn.update(context, column.id, { parsed_tree: tree }).then(
() => {
// ignore
},
@ -121,7 +123,9 @@ async function _formulaQueryBuilder(
case UITypes.Formula:
{
aliasToColumn[col.id] = async () => {
const formulOption = await col.getColOptions<FormulaColumn>();
const formulOption = await col.getColOptions<FormulaColumn>(
context,
);
const { builder } = await _formulaQueryBuilder(
baseModelSqlv2,
formulOption.formula,
@ -144,19 +148,21 @@ async function _formulaQueryBuilder(
let selectQb;
let isArray = false;
const alias = `__nc_formula${aliasCount++}`;
const lookup = await col.getColOptions<LookupColumn>();
const lookup = await col.getColOptions<LookupColumn>(context);
{
const relationCol = await lookup.getRelationColumn();
const relationCol = await lookup.getRelationColumn(context);
const relation =
await relationCol.getColOptions<LinkToAnotherRecordColumn>();
await relationCol.getColOptions<LinkToAnotherRecordColumn>(
context,
);
// if (relation.type !== RelationTypes.BELONGS_TO) continue;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
let relationType = relation.type;
@ -203,9 +209,13 @@ async function _formulaQueryBuilder(
case RelationTypes.MANY_TO_MANY:
{
isArray = true;
const mmModel = await relation.getMMModel();
const mmParentColumn = await relation.getMMParentColumn();
const mmChildColumn = await relation.getMMChildColumn();
const mmModel = await relation.getMMModel(context);
const mmParentColumn = await relation.getMMParentColumn(
context,
);
const mmChildColumn = await relation.getMMChildColumn(
context,
);
const assocAlias = `__nc${aliasCount++}`;
selectQb = knex(
@ -235,25 +245,27 @@ async function _formulaQueryBuilder(
break;
}
let lookupColumn = await lookup.getLookupColumn();
let lookupColumn = await lookup.getLookupColumn(context);
let prevAlias = alias;
while (lookupColumn.uidt === UITypes.Lookup) {
const nestedAlias = `__nc_formula${aliasCount++}`;
const nestedLookup =
await lookupColumn.getColOptions<LookupColumn>();
const relationCol = await nestedLookup.getRelationColumn();
await lookupColumn.getColOptions<LookupColumn>(context);
const relationCol = await nestedLookup.getRelationColumn(context);
const relation =
await relationCol.getColOptions<LinkToAnotherRecordColumn>();
await relationCol.getColOptions<LinkToAnotherRecordColumn>(
context,
);
// if any of the relation in nested lookup is
// not belongs to then ignore the sort option
// if (relation.type !== RelationTypes.BELONGS_TO) continue;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
let relationType = relation.type;
@ -291,9 +303,13 @@ async function _formulaQueryBuilder(
break;
case RelationTypes.MANY_TO_MANY: {
isArray = true;
const mmModel = await relation.getMMModel();
const mmParentColumn = await relation.getMMParentColumn();
const mmChildColumn = await relation.getMMChildColumn();
const mmModel = await relation.getMMModel(context);
const mmParentColumn = await relation.getMMParentColumn(
context,
);
const mmChildColumn = await relation.getMMChildColumn(
context,
);
const assocAlias = `__nc${aliasCount++}`;
@ -323,7 +339,7 @@ async function _formulaQueryBuilder(
`${prevAlias}.${childColumn.title}`
);*/
lookupColumn = await nestedLookup.getLookupColumn();
lookupColumn = await nestedLookup.getLookupColumn(context);
prevAlias = nestedAlias;
}
@ -336,8 +352,9 @@ async function _formulaQueryBuilder(
baseModelSqlv2,
knex,
alias: prevAlias,
columnOptions:
(await lookupColumn.getColOptions()) as RollupColumn,
columnOptions: (await lookupColumn.getColOptions(
context,
)) as RollupColumn,
})
).builder;
// selectQb.select(builder);
@ -363,17 +380,22 @@ async function _formulaQueryBuilder(
{
const nestedAlias = `__nc_formula${aliasCount++}`;
const relation =
await lookupColumn.getColOptions<LinkToAnotherRecordColumn>();
await lookupColumn.getColOptions<LinkToAnotherRecordColumn>(
context,
);
// if (relation.type !== RelationTypes.BELONGS_TO) continue;
const colOptions =
(await lookupColumn.getColOptions()) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const colOptions = (await lookupColumn.getColOptions(
context,
)) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn(context);
const parentColumn = await colOptions.getParentColumn(
context,
);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
let cn;
let relationType = relation.type;
@ -421,10 +443,13 @@ async function _formulaQueryBuilder(
case RelationTypes.MANY_TO_MANY:
{
isArray = true;
const mmModel = await relation.getMMModel();
const mmParentColumn =
await relation.getMMParentColumn();
const mmChildColumn = await relation.getMMChildColumn();
const mmModel = await relation.getMMModel(context);
const mmParentColumn = await relation.getMMParentColumn(
context,
);
const mmChildColumn = await relation.getMMChildColumn(
context,
);
const assocAlias = `__nc${aliasCount++}`;
@ -481,8 +506,8 @@ async function _formulaQueryBuilder(
case UITypes.Formula:
{
const formulaOption =
await lookupColumn.getColOptions<FormulaColumn>();
const lookupModel = await lookupColumn.getModel();
await lookupColumn.getColOptions<FormulaColumn>(context);
const lookupModel = await lookupColumn.getModel(context);
const { builder } = await _formulaQueryBuilder(
baseModelSqlv2,
formulaOption.formula,
@ -546,7 +571,7 @@ async function _formulaQueryBuilder(
const qb = await genRollupSelectv2({
baseModelSqlv2,
knex,
columnOptions: (await col.getColOptions()) as RollupColumn,
columnOptions: (await col.getColOptions(context)) as RollupColumn,
alias: tableAlias,
});
return { builder: knex.raw(qb.builder).wrap('(', ')') };
@ -555,17 +580,20 @@ async function _formulaQueryBuilder(
case UITypes.LinkToAnotherRecord:
aliasToColumn[col.id] = async (): Promise<any> => {
const alias = `__nc_formula_ll`;
const relation = await col.getColOptions<LinkToAnotherRecordColumn>();
const relation = await col.getColOptions<LinkToAnotherRecordColumn>(
context,
);
// if (relation.type !== RelationTypes.BELONGS_TO) continue;
const colOptions =
(await col.getColOptions()) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn();
const parentColumn = await colOptions.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const colOptions = (await col.getColOptions(
context,
)) as LinkToAnotherRecordColumn;
const childColumn = await colOptions.getChildColumn(context);
const parentColumn = await colOptions.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
let relationType = relation.type;
@ -641,9 +669,9 @@ async function _formulaQueryBuilder(
// todo: provide unique alias
const mmModel = await relation.getMMModel();
const mmParentColumn = await relation.getMMParentColumn();
const mmChildColumn = await relation.getMMChildColumn();
const mmModel = await relation.getMMModel(context);
const mmParentColumn = await relation.getMMParentColumn(context);
const mmChildColumn = await relation.getMMChildColumn(context);
const qb = knex(
knex.raw(`?? as ??`, [
@ -693,7 +721,7 @@ async function _formulaQueryBuilder(
case UITypes.LastModifiedTime:
case UITypes.DateTime:
{
const refCol = await getRefColumnIfAlias(col);
const refCol = await getRefColumnIfAlias(context, col);
if (refCol.id in aliasToColumn) {
aliasToColumn[col.id] = aliasToColumn[refCol.id];
@ -749,8 +777,8 @@ async function _formulaQueryBuilder(
case UITypes.CreatedBy:
case UITypes.LastModifiedBy:
{
const base = await Base.get(model.base_id);
const baseUsers = await BaseUser.getUsersList({
const base = await Base.get(context, model.base_id);
const baseUsers = await BaseUser.getUsersList(context, {
base_id: base.id,
});
@ -902,6 +930,7 @@ async function _formulaQueryBuilder(
if (calleeName === 'CONCAT') {
if (knex.clientType() !== 'sqlite3') {
query = await convertDateFormatForConcat(
context,
arg,
columnIdToUidt,
query,
@ -1126,12 +1155,14 @@ async function _formulaQueryBuilder(
if (pt.left.fnName === 'CONCAT' && knex.clientType() === 'sqlite3') {
// handle date format
left = await convertDateFormatForConcat(
context,
pt.left?.arguments?.[0],
columnIdToUidt,
left,
knex.clientType(),
);
right = await convertDateFormatForConcat(
context,
pt.right?.arguments?.[0],
columnIdToUidt,
right,
@ -1228,6 +1259,9 @@ export default async function formulaQueryBuilderv2(
parsedTree?: any,
) {
const knex = baseModelSqlv2.dbDriver;
const context = baseModelSqlv2.context;
// register jsep curly hook once only
jsep.plugins.register(jsepCurlyHook);
let qb;
@ -1242,7 +1276,7 @@ export default async function formulaQueryBuilderv2(
tableAlias,
parsedTree ??
(await column
?.getColOptions<FormulaColumn>()
?.getColOptions<FormulaColumn>(context)
.then((formula) => formula?.getParsedTree())),
);
@ -1260,10 +1294,10 @@ export default async function formulaQueryBuilderv2(
// if column is provided, i.e. formula has been created
if (column) {
const formula = await column.getColOptions<FormulaColumn>();
const formula = await column.getColOptions<FormulaColumn>(context);
// clean the previous formula error if the formula works this time
if (formula.error) {
await FormulaColumn.update(column.id, {
await FormulaColumn.update(context, column.id, {
error: null,
});
}
@ -1274,7 +1308,7 @@ export default async function formulaQueryBuilderv2(
console.error(e);
if (column) {
// add formula error to show in UI
await FormulaColumn.update(column.id, {
await FormulaColumn.update(context, column.id, {
error: e.message,
});
// update cache to reflect the error in UI

22
packages/nocodb/src/db/genRollupSelectv2.ts

@ -21,14 +21,16 @@ export default async function ({
alias?: string;
columnOptions: RollupColumn | LinksColumn;
}): Promise<{ builder: Knex.QueryBuilder | any }> {
const relationColumn = await columnOptions.getRelationColumn();
const context = baseModelSqlv2.context;
const relationColumn = await columnOptions.getRelationColumn(context);
const relationColumnOption: LinkToAnotherRecordColumn =
(await relationColumn.getColOptions()) as LinkToAnotherRecordColumn;
const rollupColumn = await columnOptions.getRollupColumn();
const childCol = await relationColumnOption.getChildColumn();
const childModel = await childCol?.getModel();
const parentCol = await relationColumnOption.getParentColumn();
const parentModel = await parentCol?.getModel();
(await relationColumn.getColOptions(context)) as LinkToAnotherRecordColumn;
const rollupColumn = await columnOptions.getRollupColumn(context);
const childCol = await relationColumnOption.getChildColumn(context);
const childModel = await childCol?.getModel(context);
const parentCol = await relationColumnOption.getParentColumn(context);
const parentModel = await parentCol?.getModel(context);
const refTableAlias = `__nc_rollup`;
switch (relationColumnOption.type) {
@ -83,9 +85,9 @@ export default async function ({
}
case RelationTypes.MANY_TO_MANY: {
const mmModel = await relationColumnOption.getMMModel();
const mmChildCol = await relationColumnOption.getMMChildColumn();
const mmParentCol = await relationColumnOption.getMMParentColumn();
const mmModel = await relationColumnOption.getMMModel(context);
const mmChildCol = await relationColumnOption.getMMChildColumn(context);
const mmParentCol = await relationColumnOption.getMMParentColumn(context);
if (!mmModel) {
return this.dbDriver.raw(`?`, [

135
packages/nocodb/src/db/generateLookupSelectQuery.ts

@ -10,6 +10,7 @@ import type {
QrCodeColumn,
RollupColumn,
} from '~/models';
import type { NcContext } from '~/interface/config';
import { Model } from '~/models';
import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2';
import genRollupSelectv2 from '~/db/genRollupSelectv2';
@ -19,12 +20,13 @@ import { NcError } from '~/helpers/catchError';
const LOOKUP_VAL_SEPARATOR = '___';
export async function getDisplayValueOfRefTable(
context: NcContext,
relationCol: Column<LinkToAnotherRecordColumn | LinksColumn>,
) {
return await relationCol
.getColOptions()
.then((colOpt) => colOpt.getRelatedTable())
.then((model) => model.getColumns())
.getColOptions(context)
.then((colOpt) => colOpt.getRelatedTable(context))
.then((model) => model.getColumns(context))
.then((cols) => cols.find((col) => col.pv));
}
@ -48,6 +50,8 @@ export default async function generateLookupSelectQuery({
}): Promise<any> {
const knex = baseModelSqlv2.dbDriver;
const context = baseModelSqlv2.context;
const rootAlias = alias;
{
@ -57,18 +61,18 @@ export default async function generateLookupSelectQuery({
let isBtLookup = true;
if (column.uidt === UITypes.Lookup) {
lookupColOpt = await column.getColOptions<LookupColumn>();
lookupColOpt = await column.getColOptions<LookupColumn>(context);
} else if (column.uidt !== UITypes.LinkToAnotherRecord) {
NcError.badRequest('Invalid field type');
}
await column.getColOptions<LookupColumn>();
await column.getColOptions<LookupColumn>(context);
{
const relationCol = lookupColOpt
? await lookupColOpt.getRelationColumn()
? await lookupColOpt.getRelationColumn(context)
: column;
const relation =
await relationCol.getColOptions<LinkToAnotherRecordColumn>();
await relationCol.getColOptions<LinkToAnotherRecordColumn>(context);
let relationType = relation.type;
@ -79,12 +83,12 @@ export default async function generateLookupSelectQuery({
}
if (relationType === RelationTypes.BELONGS_TO) {
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
selectQb = knex(
knex.raw(`?? as ??`, [
@ -101,12 +105,12 @@ export default async function generateLookupSelectQuery({
);
} else if (relationType === RelationTypes.HAS_MANY) {
isBtLookup = false;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
selectQb = knex(
knex.raw(`?? as ??`, [
@ -123,12 +127,12 @@ export default async function generateLookupSelectQuery({
);
} else if (relationType === RelationTypes.MANY_TO_MANY) {
isBtLookup = false;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
selectQb = knex(
knex.raw(`?? as ??`, [
@ -139,9 +143,9 @@ export default async function generateLookupSelectQuery({
const mmTableAlias = getAlias();
const mmModel = await relation.getMMModel();
const mmChildCol = await relation.getMMChildColumn();
const mmParentCol = await relation.getMMParentColumn();
const mmModel = await relation.getMMModel(context);
const mmChildCol = await relation.getMMChildColumn(context);
const mmParentCol = await relation.getMMParentColumn(context);
selectQb
.innerJoin(
@ -162,14 +166,14 @@ export default async function generateLookupSelectQuery({
}
}
let lookupColumn = lookupColOpt
? await lookupColOpt.getLookupColumn()
: await getDisplayValueOfRefTable(column);
? await lookupColOpt.getLookupColumn(context)
: await getDisplayValueOfRefTable(context, column);
// if lookup column is qr code or barcode extract the referencing column
if ([UITypes.QrCode, UITypes.Barcode].includes(lookupColumn.uidt)) {
lookupColumn = await lookupColumn
.getColOptions<BarcodeColumn | QrCodeColumn>()
.then((barcode) => barcode.getValueColumn());
.getColOptions<BarcodeColumn | QrCodeColumn>(context)
.then((barcode) => barcode.getValueColumn(context));
}
let prevAlias = alias;
@ -183,14 +187,16 @@ export default async function generateLookupSelectQuery({
let nestedLookupColOpt: LookupColumn;
if (lookupColumn.uidt === UITypes.Lookup) {
nestedLookupColOpt = await lookupColumn.getColOptions<LookupColumn>();
relationCol = await nestedLookupColOpt.getRelationColumn();
nestedLookupColOpt = await lookupColumn.getColOptions<LookupColumn>(
context,
);
relationCol = await nestedLookupColOpt.getRelationColumn(context);
} else {
relationCol = lookupColumn;
}
const relation =
await relationCol.getColOptions<LinkToAnotherRecordColumn>();
await relationCol.getColOptions<LinkToAnotherRecordColumn>(context);
let relationType = relation.type;
@ -203,12 +209,12 @@ export default async function generateLookupSelectQuery({
// if any of the relation in nested lookupColOpt is
// not belongs to then throw error as we don't support
if (relationType === RelationTypes.BELONGS_TO) {
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
selectQb.join(
knex.raw(`?? as ??`, [
@ -220,12 +226,12 @@ export default async function generateLookupSelectQuery({
);
} else if (relationType === RelationTypes.HAS_MANY) {
isBtLookup = false;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
selectQb.join(
knex.raw(`?? as ??`, [
@ -237,18 +243,18 @@ export default async function generateLookupSelectQuery({
);
} else if (relationType === RelationTypes.MANY_TO_MANY) {
isBtLookup = false;
const childColumn = await relation.getChildColumn();
const parentColumn = await relation.getParentColumn();
const childModel = await childColumn.getModel();
await childModel.getColumns();
const parentModel = await parentColumn.getModel();
await parentModel.getColumns();
const childColumn = await relation.getChildColumn(context);
const parentColumn = await relation.getParentColumn(context);
const childModel = await childColumn.getModel(context);
await childModel.getColumns(context);
const parentModel = await parentColumn.getModel(context);
await parentModel.getColumns(context);
const mmTableAlias = getAlias();
const mmModel = await relation.getMMModel();
const mmChildCol = await relation.getMMChildColumn();
const mmParentCol = await relation.getMMParentColumn();
const mmModel = await relation.getMMModel(context);
const mmChildCol = await relation.getMMChildColumn(context);
const mmParentCol = await relation.getMMParentColumn(context);
selectQb
.innerJoin(
@ -278,15 +284,15 @@ export default async function generateLookupSelectQuery({
}
if (lookupColumn.uidt === UITypes.Lookup)
lookupColumn = await nestedLookupColOpt.getLookupColumn();
else lookupColumn = await getDisplayValueOfRefTable(relationCol);
lookupColumn = await nestedLookupColOpt.getLookupColumn(context);
else lookupColumn = await getDisplayValueOfRefTable(context, relationCol);
prevAlias = nestedAlias;
}
{
// get basemodel and model of lookup column
const model = await lookupColumn.getModel();
const baseModelSqlv2 = await Model.getBaseModelSQL({
const model = await lookupColumn.getModel(context);
const baseModelSqlv2 = await Model.getBaseModelSQL(context, {
model,
dbDriver: knex,
});
@ -304,8 +310,9 @@ export default async function generateLookupSelectQuery({
await genRollupSelectv2({
baseModelSqlv2,
knex,
columnOptions:
(await lookupColumn.getColOptions()) as RollupColumn,
columnOptions: (await lookupColumn.getColOptions(
context,
)) as RollupColumn,
alias: prevAlias,
})
).builder;
@ -318,12 +325,12 @@ export default async function generateLookupSelectQuery({
await formulaQueryBuilderv2(
baseModelSqlv2,
(
await lookupColumn.getColOptions<FormulaColumn>()
await lookupColumn.getColOptions<FormulaColumn>(context)
).formula,
lookupColumn.id,
model,
lookupColumn,
await model.getAliasColMapping(),
await model.getAliasColMapping(context),
prevAlias,
)
).builder;

19
packages/nocodb/src/db/sortV2.ts

@ -19,6 +19,8 @@ export default async function sortV2(
) {
const knex = baseModelSqlv2.dbDriver;
const context = baseModelSqlv2.context;
if (!sortList?.length) {
return;
}
@ -30,14 +32,17 @@ export default async function sortV2(
} else {
sort = new Sort(_sort);
}
const column = await getRefColumnIfAlias(await sort.getColumn());
const column = await getRefColumnIfAlias(
context,
await sort.getColumn(context),
);
if (!column) {
if (throwErrorIfInvalid) {
NcError.fieldNotFound(sort.fk_column_id);
}
continue;
}
const model = await column.getModel();
const model = await column.getModel(context);
const nulls = sort.direction === 'desc' ? 'LAST' : 'FIRST';
@ -49,7 +54,9 @@ export default async function sortV2(
await genRollupSelectv2({
baseModelSqlv2,
knex,
columnOptions: (await column.getColOptions()) as RollupColumn,
columnOptions: (await column.getColOptions(
context,
)) as RollupColumn,
alias,
})
).builder;
@ -63,7 +70,7 @@ export default async function sortV2(
await formulaQueryBuilderv2(
baseModelSqlv2,
(
await column.getColOptions<FormulaColumn>()
await column.getColOptions<FormulaColumn>(context)
).formula,
null,
model,
@ -144,8 +151,8 @@ export default async function sortV2(
case UITypes.User:
case UITypes.CreatedBy:
case UITypes.LastModifiedBy: {
const base = await Base.get(model.base_id);
const baseUsers = await BaseUser.getUsersList({
const base = await Base.get(context, model.base_id);
const baseUsers = await BaseUser.getUsersList(context, {
base_id: base.id,
});

9
packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts

@ -3,6 +3,7 @@ import SqlMgrv2Trans from './SqlMgrv2Trans';
import type { MetaService } from '~/meta/meta.service';
// import type NcMetaIO from '~/meta/NcMetaIO';
import type Source from '~/models/Source';
import type { NcContext } from '~/interface/config';
export default class ProjectMgrv2 {
private static sqlMgrMap: {
@ -10,24 +11,26 @@ export default class ProjectMgrv2 {
} = {};
public static getSqlMgr(
context: NcContext,
base: { id: string },
ncMeta: MetaService = null,
): SqlMgrv2 {
if (ncMeta) return new SqlMgrv2(base, ncMeta);
if (ncMeta) return new SqlMgrv2(context, base, ncMeta);
if (!this.sqlMgrMap[base.id]) {
this.sqlMgrMap[base.id] = new SqlMgrv2(base);
this.sqlMgrMap[base.id] = new SqlMgrv2(context, base);
}
return this.sqlMgrMap[base.id];
}
public static async getSqlMgrTrans(
context: NcContext,
base: { id: string },
// todo: tobe changed
ncMeta: any,
source: Source,
): Promise<SqlMgrv2Trans> {
const sqlMgr = new SqlMgrv2Trans(base, ncMeta, source);
const sqlMgr = new SqlMgrv2Trans(context, base, ncMeta, source);
await sqlMgr.startTransaction(source);
return sqlMgr;
}

8
packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2.ts

@ -3,6 +3,7 @@
// import {XKnex} from "../sql-data-mapper";
import type { MetaService } from '~/meta/meta.service';
import type Source from '~/models/Source';
import type { NcContext } from '~/interface/config';
import SqlClientFactory from '~/db/sql-client/lib/SqlClientFactory';
import KnexMigratorv2 from '~/db/sql-migrator/lib/KnexMigratorv2';
import Debug from '~/db/util/Debug';
@ -11,6 +12,8 @@ import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
const log = new Debug('SqlMgr');
export default class SqlMgrv2 {
public context: NcContext;
protected _migrator: KnexMigratorv2;
protected ncMeta?: MetaService;
// @ts-ignore
@ -22,12 +25,13 @@ export default class SqlMgrv2 {
* @param {String} args.toolDbPath - path to sqlite file that sql mgr will use
* @memberof SqlMgr
*/
constructor(args: { id: string }, ncMeta = null) {
constructor(context: NcContext, args: { id: string }, ncMeta = null) {
const func = 'constructor';
log.api(`${func}:args:`, args);
// this.metaDb = args.metaDb;
this._migrator = new KnexMigratorv2(args);
this._migrator = new KnexMigratorv2(context, args);
this.ncMeta = ncMeta;
this.context = context;
}
public async migrator(_base: Source) {

11
packages/nocodb/src/db/sql-mgr/v2/SqlMgrv2Trans.ts

@ -3,6 +3,7 @@ import SqlMgrv2 from './SqlMgrv2';
import type { Knex } from 'knex';
import type { XKnex } from '../../CustomKnex';
import type Source from '~/models/Source';
import type { NcContext } from '~/interface/config';
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
export default class SqlMgrv2Trans extends SqlMgrv2 {
@ -19,8 +20,13 @@ export default class SqlMgrv2Trans extends SqlMgrv2 {
* @memberof SqlMgr
*/
// todo: tobe changed
constructor(args: { id: string }, ncMeta: any, source: Source) {
super(args);
constructor(
context: NcContext,
args: { id: string },
ncMeta: any,
source: Source,
) {
super(context, args);
this.baseId = args.id;
this.ncMeta = ncMeta;
this.source = source;
@ -28,6 +34,7 @@ export default class SqlMgrv2Trans extends SqlMgrv2 {
public async migrator() {
return new KnexMigratorv2Tans(
this.context,
{ id: this.baseId },
await this.getSqlClient(this.source),
this.ncMeta,

8
packages/nocodb/src/db/sql-migrator/lib/KnexMigratorv2.ts

@ -9,6 +9,7 @@ import Result from '../../util/Result';
import type Source from '~/models/Source';
import type { XKnex } from '~/db/CustomKnex';
import type { Knex } from 'knex';
import type { NcContext } from '~/interface/config';
import SqlClientFactory from '~/db/sql-client/lib/SqlClientFactory';
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
import Noco from '~/Noco';
@ -26,6 +27,8 @@ const NC_MIGRATION = 'nc_migrations';
* @extends {SqlMigrator}
*/
export default class KnexMigratorv2 {
public context: NcContext;
//extends SqlMigrator {
private baseId: string;
private toolDir = process.cwd();
@ -34,8 +37,9 @@ export default class KnexMigratorv2 {
* Creates an instance of KnexMigrator.
* @memberof KnexMigrator
*/
constructor(base: { id: string }) {
constructor(context: NcContext, base: { id: string }) {
this.baseId = base.id;
this.context = context;
}
protected get metaDb(): XKnex {
@ -43,7 +47,7 @@ export default class KnexMigratorv2 {
}
private async getProject(): Promise<Base> {
return Base.getWithInfo(this.baseId);
return Base.getWithInfo(this.context, this.baseId);
}
emit(data, _args?) {

10
packages/nocodb/src/db/sql-migrator/lib/KnexMigratorv2Tans.ts

@ -12,6 +12,7 @@ import type MysqlClient from '~/db/sql-client/lib/mysql/MysqlClient';
import type OracleClient from '~/db/sql-client/lib/oracle/OracleClient';
import type PGClient from '~/db/sql-client/lib/pg/PgClient';
import type SqliteClient from '~/db/sql-client/lib/sqlite/SqliteClient';
import type { NcContext } from '~/interface/config';
import Noco from '~/Noco';
import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
@ -20,8 +21,13 @@ export default class KnexMigratorv2Tans extends KnexMigratorv2 {
// todo: tobe changed
protected ncMeta: any; // NcMetaIO;
constructor(base: { id: string }, sqlClient = null, ncMeta = Noco.ncMeta) {
super(base);
constructor(
context: NcContext,
base: { id: string },
sqlClient = null,
ncMeta = Noco.ncMeta,
) {
super(context, base);
this.sqlClient = sqlClient;
this.ncMeta = ncMeta;
}

10
packages/nocodb/src/decorators/tenant-context.decorator.ts

@ -0,0 +1,10 @@
import { createParamDecorator } from '@nestjs/common';
import type { ExecutionContext } from '@nestjs/common';
import type { NcRequest } from '~/interface/config';
export const TenantContext = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest<NcRequest>();
return request.context;
},
);

79
packages/nocodb/src/helpers/NcPluginMgrv2.ts

@ -30,7 +30,7 @@ import VultrPluginConfig from '~/plugins/vultr';
import SESPluginConfig from '~/plugins/ses';
import Noco from '~/Noco';
import Local from '~/plugins/storage/Local';
import { MetaTable } from '~/utils/globals';
import { MetaTable, RootScopes } from '~/utils/globals';
import Plugin from '~/models/Plugin';
const defaultPlugins = [
@ -67,24 +67,34 @@ class NcPluginMgrv2 {
public static async init(ncMeta = Noco.ncMeta): Promise<void> {
/* Populate rows into nc_plugins table if not present */
for (const plugin of defaultPlugins) {
const pluginConfig = await ncMeta.metaGet(null, null, MetaTable.PLUGIN, {
title: plugin.title,
});
const pluginConfig = await ncMeta.metaGet(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.PLUGIN,
{
title: plugin.title,
},
);
if (!pluginConfig) {
await ncMeta.metaInsert2(null, null, MetaTable.PLUGIN, {
title: plugin.title,
version: plugin.version,
logo: plugin.logo,
description: plugin.description,
tags: plugin.tags,
category: plugin.category,
input_schema: JSON.stringify(plugin.inputs),
});
await ncMeta.metaInsert2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.PLUGIN,
{
title: plugin.title,
version: plugin.version,
logo: plugin.logo,
description: plugin.description,
tags: plugin.tags,
category: plugin.category,
input_schema: JSON.stringify(plugin.inputs),
},
);
} else if (pluginConfig.version !== plugin.version) {
await ncMeta.metaUpdate(
null,
null,
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.PLUGIN,
{
title: plugin.title,
@ -152,10 +162,15 @@ class NcPluginMgrv2 {
public static async storageAdapter(
ncMeta = Noco.ncMeta,
): Promise<IStorageAdapterV2> {
const pluginData = await ncMeta.metaGet2(null, null, MetaTable.PLUGIN, {
category: PluginCategory.STORAGE,
active: true,
});
const pluginData = await ncMeta.metaGet2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.PLUGIN,
{
category: PluginCategory.STORAGE,
active: true,
},
);
if (!pluginData) return new Local();
@ -177,10 +192,15 @@ class NcPluginMgrv2 {
isUserInvite = true,
ncMeta = Noco.ncMeta,
): Promise<IEmailAdapter> {
const pluginData = await ncMeta.metaGet2(null, null, MetaTable.PLUGIN, {
category: PluginCategory.EMAIL,
active: true,
});
const pluginData = await ncMeta.metaGet2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.PLUGIN,
{
category: PluginCategory.EMAIL,
active: true,
},
);
if (!pluginData) {
// return null to show the invite link in UI
@ -207,10 +227,15 @@ class NcPluginMgrv2 {
title: string,
ncMeta = Noco.ncMeta,
): Promise<IWebhookNotificationAdapter> {
const pluginData = await ncMeta.metaGet2(null, null, MetaTable.PLUGIN, {
title,
active: true,
});
const pluginData = await ncMeta.metaGet2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.PLUGIN,
{
title,
active: true,
},
);
if (!pluginData) throw new Error('Plugin not configured / active');

11
packages/nocodb/src/helpers/catchError.ts

@ -498,6 +498,10 @@ const errorHelpers: {
},
code: 404,
},
[NcErrorType.GENERIC_NOT_FOUND]: {
message: (resource: string, id: string) => `${resource} '${id}' not found`,
code: 404,
},
[NcErrorType.ERROR_DUPLICATE_RECORD]: {
message: (...ids: string[]) => {
const isMultiple = Array.isArray(ids) && ids.length > 1;
@ -665,6 +669,13 @@ export class NcError {
});
}
static genericNotFound(resource: string, id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.GENERIC_NOT_FOUND, {
params: [resource, id],
...args,
});
}
static duplicateRecord(id: string | string[], args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.ERROR_DUPLICATE_RECORD, {
params: id,

98
packages/nocodb/src/helpers/columnHelpers.ts

@ -16,6 +16,7 @@ import type {
import type LinkToAnotherRecordColumn from '~/models/LinkToAnotherRecordColumn';
import type LookupColumn from '~/models/LookupColumn';
import type Model from '~/models/Model';
import type { NcContext } from '~/interface/config';
import type { RollupColumn, View } from '~/models';
import { GridViewColumn } from '~/models';
import validateParams from '~/helpers/validateParams';
@ -29,6 +30,7 @@ export const randomID = customAlphabet(
);
export async function createHmAndBtColumn(
context: NcContext,
child: Model,
parent: Model,
childColumn: Column,
@ -45,10 +47,10 @@ export async function createHmAndBtColumn(
// save bt column
{
const title = getUniqueColumnAliasName(
await child.getColumns(),
await child.getColumns(context),
(type === 'bt' && alias) || `${parent.title}`,
);
await Column.insert<LinkToAnotherRecordColumn>({
await Column.insert<LinkToAnotherRecordColumn>(context, {
title,
fk_model_id: child.id,
@ -71,7 +73,7 @@ export async function createHmAndBtColumn(
// save hm column
{
const title = getUniqueColumnAliasName(
await parent.getColumns(),
await parent.getColumns(context),
(type === 'hm' && alias) || pluralize(child.title),
);
const meta = {
@ -80,7 +82,7 @@ export async function createHmAndBtColumn(
singular: columnMeta?.singular || singularize(child.title),
};
await Column.insert({
await Column.insert(context, {
title,
fk_model_id: parent.id,
uidt: isLinks ? UITypes.Links : UITypes.LinkToAnotherRecord,
@ -114,6 +116,7 @@ export async function createHmAndBtColumn(
* @param {any} [colExtra] - Additional column parameters.
*/
export async function createOOColumn(
context: NcContext,
child: Model,
parent: Model,
childColumn: Column,
@ -129,10 +132,10 @@ export async function createOOColumn(
// save bt column
{
const title = getUniqueColumnAliasName(
await child.getColumns(),
await child.getColumns(context),
`${parent.title}`,
);
await Column.insert<LinkToAnotherRecordColumn>({
await Column.insert<LinkToAnotherRecordColumn>(context, {
title,
fk_model_id: child.id,
// ref_db_alias
@ -160,7 +163,7 @@ export async function createOOColumn(
// save hm column
{
const title = getUniqueColumnAliasName(
await parent.getColumns(),
await parent.getColumns(context),
alias || child.title,
);
const meta = {
@ -169,7 +172,7 @@ export async function createOOColumn(
singular: columnMeta?.singular || singularize(child.title),
};
await Column.insert({
await Column.insert(context, {
title,
fk_model_id: parent.id,
uidt: UITypes.LinkToAnotherRecord,
@ -188,7 +191,10 @@ export async function createOOColumn(
}
}
export async function validateRollupPayload(payload: ColumnReqType | Column) {
export async function validateRollupPayload(
context: NcContext,
payload: ColumnReqType | Column,
) {
validateParams(
[
'title',
@ -200,10 +206,10 @@ export async function validateRollupPayload(payload: ColumnReqType | Column) {
);
const relation = await (
await Column.get({
await Column.get(context, {
colId: (payload as RollupColumnReqType).fk_relation_column_id,
})
).getColOptions<LinkToAnotherRecordType>();
).getColOptions<LinkToAnotherRecordType>(context);
if (!relation) {
throw new Error('Relation column not found');
@ -212,21 +218,21 @@ export async function validateRollupPayload(payload: ColumnReqType | Column) {
let relatedColumn: Column;
switch (relation.type) {
case 'hm':
relatedColumn = await Column.get({
relatedColumn = await Column.get(context, {
colId: relation.fk_child_column_id,
});
break;
case 'mm':
case 'bt':
relatedColumn = await Column.get({
relatedColumn = await Column.get(context, {
colId: relation.fk_parent_column_id,
});
break;
}
const relatedTable = await relatedColumn.getModel();
const relatedTable = await relatedColumn.getModel(context);
if (
!(await relatedTable.getColumns()).find(
!(await relatedTable.getColumns(context)).find(
(c) => c.id === (payload as RollupColumnReqType).fk_rollup_column_id,
)
)
@ -246,6 +252,7 @@ export async function validateRollupPayload(payload: ColumnReqType | Column) {
}
export async function validateLookupPayload(
context: NcContext,
payload: ColumnReqType,
columnId?: string,
) {
@ -262,20 +269,20 @@ export async function validateLookupPayload(
// check if lookup column is same as column itself
if (columnId === lkCol.fk_lookup_column_id)
throw new Error('Circular lookup reference not allowed');
lkCol = await Column.get({ colId: lkCol.fk_lookup_column_id }).then(
(c: Column) => {
if (c.uidt === 'Lookup') {
return c.getColOptions<LookupColumn>();
}
return null;
},
);
lkCol = await Column.get(context, {
colId: lkCol.fk_lookup_column_id,
}).then((c: Column) => {
if (c.uidt === 'Lookup') {
return c.getColOptions<LookupColumn>(context);
}
return null;
});
}
}
const column = await Column.get({
const column = await Column.get(context, {
colId: (payload as LookupColumnReqType).fk_relation_column_id,
});
const relation = await column.getColOptions<LinkToAnotherRecordType>();
const relation = await column.getColOptions<LinkToAnotherRecordType>(context);
if (!relation) {
throw new Error('Relation column not found');
@ -284,18 +291,18 @@ export async function validateLookupPayload(
let relatedColumn: Column;
switch (relation.type) {
case 'hm':
relatedColumn = await Column.get({
relatedColumn = await Column.get(context, {
colId: relation.fk_child_column_id,
});
break;
case 'mm':
case 'bt':
relatedColumn = await Column.get({
relatedColumn = await Column.get(context, {
colId: relation.fk_parent_column_id,
});
break;
case 'oo':
relatedColumn = await Column.get({
relatedColumn = await Column.get(context, {
colId: column.meta?.bt
? relation.fk_parent_column_id
: relation.fk_child_column_id,
@ -303,9 +310,9 @@ export async function validateLookupPayload(
break;
}
const relatedTable = await relatedColumn.getModel();
const relatedTable = await relatedColumn.getModel(context);
if (
!(await relatedTable.getColumns()).find(
!(await relatedTable.getColumns(context)).find(
(c) => c.id === (payload as LookupColumnReqType).fk_lookup_column_id,
)
)
@ -336,34 +343,37 @@ export const generateFkName = (parent: TableType, child: TableType) => {
};
export async function populateRollupForLTAR({
context,
column,
columnMeta,
alias,
}: {
context: NcContext;
column: Column;
columnMeta?: any;
alias?: string;
}) {
const model = await column.getModel();
const model = await column.getModel(context);
const views = await model.getViews();
const views = await model.getViews(context);
const relatedModel = await column
.getColOptions<LinkToAnotherRecordColumn>()
.then((colOpt) => colOpt.getRelatedTable());
await relatedModel.getColumns();
.getColOptions<LinkToAnotherRecordColumn>(context)
.then((colOpt) => colOpt.getRelatedTable(context));
await relatedModel.getColumns(context);
const pkId =
relatedModel.primaryKey?.id || (await relatedModel.getColumns())[0]?.id;
relatedModel.primaryKey?.id ||
(await relatedModel.getColumns(context))[0]?.id;
const meta = {
plural: columnMeta?.plural || pluralize(relatedModel.title),
singular: columnMeta?.singular || singularize(relatedModel.title),
};
await Column.insert<RollupColumn>({
await Column.insert<RollupColumn>(context, {
uidt: UITypes.Links,
title: getUniqueColumnAliasName(
await model.getColumns(),
await model.getColumns(context),
alias || `${relatedModel.title} Count`,
),
fk_rollup_column_id: pkId,
@ -373,10 +383,10 @@ export async function populateRollupForLTAR({
meta,
});
const viewCol = await GridViewColumn.list(views[0].id).then((cols) =>
const viewCol = await GridViewColumn.list(context, views[0].id).then((cols) =>
cols.find((c) => c.fk_column_id === column.id),
);
await GridViewColumn.update(viewCol.id, { show: false });
await GridViewColumn.update(context, viewCol.id, { show: false });
}
export const sanitizeColumnName = (name: string, sourceType?: DriverClient) => {
@ -399,6 +409,7 @@ export const sanitizeColumnName = (name: string, sourceType?: DriverClient) => {
// if column is an alias column then return the original column
// for example CreatedTime is an alias column for CreatedTime system column
export const getRefColumnIfAlias = async (
context: NcContext,
column: Column,
columns?: Column[],
) => {
@ -415,8 +426,9 @@ export const getRefColumnIfAlias = async (
return column;
return (
(columns || (await Column.list({ fk_model_id: column.fk_model_id }))).find(
(c) => c.system && c.uidt === column.uidt,
) || column
(
columns ||
(await Column.list(context, { fk_model_id: column.fk_model_id }))
).find((c) => c.system && c.uidt === column.uidt) || column
);
};

99
packages/nocodb/src/helpers/dataHelpers.ts

@ -5,6 +5,7 @@ import papaparse from 'papaparse';
import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2';
import type LinkToAnotherRecordColumn from '~/models/LinkToAnotherRecordColumn';
import type LookupColumn from '~/models/LookupColumn';
import type { NcContext } from '~/interface/config';
import { NcError } from '~/helpers/catchError';
import getAst from '~/helpers/getAst';
import { Model, View } from '~/models';
@ -25,14 +26,17 @@ export interface OldPathParams {
viewName?: string;
}
export async function getViewAndModelByAliasOrId(param: {
baseName: string;
tableName: string;
viewName?: string;
}) {
const base = await Base.getWithInfoByTitleOrId(param.baseName);
export async function getViewAndModelByAliasOrId(
context: NcContext,
param: {
baseName: string;
tableName: string;
viewName?: string;
},
) {
const base = await Base.getWithInfoByTitleOrId(context, param.baseName);
const model = await Model.getByAliasOrId({
const model = await Model.getByAliasOrId(context, {
base_id: base.id,
aliasOrId: param.tableName,
});
@ -41,7 +45,7 @@ export async function getViewAndModelByAliasOrId(param: {
const view =
param.viewName &&
(await View.getByTitleOrId({
(await View.getByTitleOrId(context, {
titleOrId: param.viewName,
fk_model_id: model.id,
}));
@ -49,11 +53,11 @@ export async function getViewAndModelByAliasOrId(param: {
return { model, view };
}
export async function extractXlsxData(view: View, req) {
const source = await Source.get(view.source_id);
export async function extractXlsxData(context: NcContext, view: View, req) {
const source = await Source.get(context, view.source_id);
await view.getModelWithInfo();
await view.getColumns();
await view.getModelWithInfo(context);
await view.getColumns(context);
view.model.columns = view.columns
.filter((c) => c.show)
@ -63,13 +67,13 @@ export async function extractXlsxData(view: View, req) {
)
.filter((column) => !isSystemColumn(column) || view.show_system_fields);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: view.model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
});
const { offset, dbRows, elapsed } = await getDbRows({
const { offset, dbRows, elapsed } = await getDbRows(context, {
baseModel,
view,
siteUrl: (req as any).ncSiteUrl,
@ -83,12 +87,12 @@ export async function extractXlsxData(view: View, req) {
return { offset, dbRows, elapsed, data };
}
export async function extractCsvData(view: View, req) {
const source = await Source.get(view.source_id);
export async function extractCsvData(context: NcContext, view: View, req) {
const source = await Source.get(context, view.source_id);
const fields = req.query.fields;
await view.getModelWithInfo();
await view.getColumns();
await view.getModelWithInfo(context);
await view.getColumns(context);
view.model.columns = view.columns
.filter((c) => c.show)
@ -98,13 +102,13 @@ export async function extractCsvData(view: View, req) {
)
.filter((column) => !isSystemColumn(column) || view.show_system_fields);
const baseModel = await Model.getBaseModelSQL({
const baseModel = await Model.getBaseModelSQL(context, {
id: view.model.id,
viewId: view?.id,
dbDriver: await NcConnectionMgrv2.get(source),
});
const { offset, dbRows, elapsed } = await getDbRows({
const { offset, dbRows, elapsed } = await getDbRows(context, {
baseModel,
view,
query: req.query,
@ -136,15 +140,18 @@ export async function extractCsvData(view: View, req) {
return { offset, dbRows, elapsed, data };
}
export async function serializeCellValue({
value,
column,
siteUrl,
}: {
column?: Column;
value: any;
siteUrl: string;
}) {
export async function serializeCellValue(
context: NcContext,
{
value,
column,
siteUrl,
}: {
column?: Column;
value: any;
siteUrl: string;
},
) {
if (!column) {
return value;
}
@ -187,12 +194,12 @@ export async function serializeCellValue({
}
case UITypes.Lookup:
{
const colOptions = await column.getColOptions<LookupColumn>();
const lookupColumn = await colOptions.getLookupColumn();
const colOptions = await column.getColOptions<LookupColumn>(context);
const lookupColumn = await colOptions.getLookupColumn(context);
return (
await Promise.all(
[...(Array.isArray(value) ? value : [value])].map(async (v) =>
serializeCellValue({
serializeCellValue(context, {
value: v,
column: lookupColumn,
siteUrl,
@ -205,9 +212,9 @@ export async function serializeCellValue({
case UITypes.LinkToAnotherRecord:
{
const colOptions =
await column.getColOptions<LinkToAnotherRecordColumn>();
const relatedModel = await colOptions.getRelatedTable();
await relatedModel.getColumns();
await column.getColOptions<LinkToAnotherRecordColumn>(context);
const relatedModel = await colOptions.getRelatedTable(context);
await relatedModel.getColumns(context);
return [...(Array.isArray(value) ? value : [value])]
.map((v) => {
return v[relatedModel.displayValue?.title];
@ -224,10 +231,11 @@ export async function serializeCellValue({
}
export async function getColumnByIdOrName(
context: NcContext,
columnNameOrId: string,
model: Model,
) {
const column = (await model.getColumns()).find(
const column = (await model.getColumns(context)).find(
(c) =>
c.title === columnNameOrId ||
c.id === columnNameOrId ||
@ -239,12 +247,15 @@ export async function getColumnByIdOrName(
return column;
}
export async function getDbRows(param: {
baseModel: BaseModelSqlv2;
view: View;
query: any;
siteUrl: string;
}) {
export async function getDbRows(
context: NcContext,
param: {
baseModel: BaseModelSqlv2;
view: View;
query: any;
siteUrl: string;
},
) {
const { baseModel, view, query = {}, siteUrl } = param;
let offset = +query.offset || 0;
const limit = 100;
@ -269,7 +280,7 @@ export async function getDbRows(param: {
temp = process.hrtime(startTime),
elapsed = temp[0] * 1000 + temp[1] / 1000000
) {
const { ast, dependencyFields } = await getAst({
const { ast, dependencyFields } = await getAst(context, {
query: query,
includePkByDefault: false,
model: view.model,
@ -292,7 +303,7 @@ export async function getDbRows(param: {
for (const column of view.model.columns) {
if (isSystemColumn(column) && !view.show_system_fields) continue;
dbRow[column.title] = await serializeCellValue({
dbRow[column.title] = await serializeCellValue(context, {
value: row[column.title],
column,
siteUrl,

6
packages/nocodb/src/helpers/exportImportHelpers.ts

@ -1,16 +1,18 @@
import type { Source } from '~/models';
import type { NcContext } from '~/interface/config';
export async function generateBaseIdMap(
context: NcContext,
source: Source,
idMap: Map<string, string>,
) {
idMap.set(source.base_id, source.base_id);
idMap.set(source.id, `${source.base_id}::${source.id}`);
const models = await source.getModels();
const models = await source.getModels(context);
for (const md of models) {
idMap.set(md.id, `${source.base_id}::${source.id}::${md.id}`);
await md.getColumns();
await md.getColumns(context);
for (const column of md.columns) {
idMap.set(column.id, `${idMap.get(md.id)}::${column.id}`);
}

2
packages/nocodb/src/helpers/extractProps.ts

@ -6,7 +6,7 @@ export function extractProps<T extends Record<string, any>>(
): Partial<T> {
// todo: throw error if no props found
return props.reduce((o, key) => {
if (key in body) o[key] = body[key];
if (key in body && body[key] !== undefined) o[key] = body[key];
return o;
}, {});
}

4
packages/nocodb/src/helpers/formulaFnHelper.ts

@ -1,5 +1,6 @@
import { UITypes } from 'nocodb-sdk';
import { convertDateFormat } from './convertDateFormat';
import type { NcContext } from '~/interface/config';
import Column from '~/models/Column';
export function getWeekdayByText(v: string) {
@ -27,6 +28,7 @@ export function getWeekdayByIndex(idx: number): string {
}
export async function convertDateFormatForConcat(
context: NcContext,
o,
columnIdToUidt,
query,
@ -38,7 +40,7 @@ export async function convertDateFormatForConcat(
columnIdToUidt[o.name] === UITypes.Date
) {
const meta = (
await Column.get({
await Column.get(context, {
colId: o.name,
})
).meta;

4
packages/nocodb/src/helpers/formulaHelpers.ts

@ -2,9 +2,11 @@ import jsep from 'jsep';
import { UITypes } from 'nocodb-sdk';
import type FormulaColumn from '../models/FormulaColumn';
import type { Column } from '~/models';
import type { NcContext } from '~/interface/config';
import Noco from '~/Noco';
export async function getFormulasReferredTheColumn(
context: NcContext,
{
column,
columns,
@ -29,7 +31,7 @@ export async function getFormulasReferredTheColumn(
const columns = await columnsPromise;
if (c.uidt !== UITypes.Formula) return columns;
const formula = await c.getColOptions<FormulaColumn>(ncMeta);
const formula = await c.getColOptions<FormulaColumn>(context, ncMeta);
if (fn(jsep(formula.formula))) {
columns.push(c);

124
packages/nocodb/src/helpers/getAst.ts

@ -12,6 +12,7 @@ import type {
LookupColumn,
Model,
} from '~/models';
import type { NcContext } from '~/interface/config';
import { NcError } from '~/helpers/catchError';
import {
CalendarRange,
@ -21,32 +22,35 @@ import {
View,
} from '~/models';
const getAst = async ({
query,
extractOnlyPrimaries = false,
includePkByDefault = true,
model,
view,
dependencyFields = {
...(query || {}),
nested: { ...(query?.nested || {}) },
fieldsSet: new Set(),
const getAst = async (
context: NcContext,
{
query,
extractOnlyPrimaries = false,
includePkByDefault = true,
model,
view,
dependencyFields = {
...(query || {}),
nested: { ...(query?.nested || {}) },
fieldsSet: new Set(),
},
getHiddenColumn = query?.['getHiddenColumn'],
throwErrorIfInvalidParams = false,
extractOnlyRangeFields = false,
}: {
query?: RequestQuery;
extractOnlyPrimaries?: boolean;
includePkByDefault?: boolean;
model: Model;
view?: View;
dependencyFields?: DependantFields;
getHiddenColumn?: boolean;
throwErrorIfInvalidParams?: boolean;
// Used for calendar view
extractOnlyRangeFields?: boolean;
},
getHiddenColumn = query?.['getHiddenColumn'],
throwErrorIfInvalidParams = false,
extractOnlyRangeFields = false,
}: {
query?: RequestQuery;
extractOnlyPrimaries?: boolean;
includePkByDefault?: boolean;
model: Model;
view?: View;
dependencyFields?: DependantFields;
getHiddenColumn?: boolean;
throwErrorIfInvalidParams?: boolean;
// Used for calendar view
extractOnlyRangeFields?: boolean;
}) => {
) => {
// set default values of dependencyFields and nested
dependencyFields.nested = dependencyFields.nested || {};
dependencyFields.fieldsSet = dependencyFields.fieldsSet || new Set();
@ -54,15 +58,15 @@ const getAst = async ({
let coverImageId;
let dependencyFieldsForCalenderView;
if (view && view.type === ViewTypes.GALLERY) {
const gallery = await GalleryView.get(view.id);
const gallery = await GalleryView.get(context, view.id);
coverImageId = gallery.fk_cover_image_col_id;
} else if (view && view.type === ViewTypes.KANBAN) {
const kanban = await KanbanView.get(view.id);
const kanban = await KanbanView.get(context, view.id);
coverImageId = kanban.fk_cover_image_col_id;
} else if (view && view.type === ViewTypes.CALENDAR) {
// const calendar = await CalendarView.get(view.id);
// coverImageId = calendar.fk_cover_image_col_id;
const calenderRanges = await CalendarRange.read(view.id);
const calenderRanges = await CalendarRange.read(context, view.id);
if (calenderRanges) {
dependencyFieldsForCalenderView = calenderRanges.ranges
.flatMap((obj) =>
@ -72,7 +76,7 @@ const getAst = async ({
}
}
if (!model.columns?.length) await model.getColumns();
if (!model.columns?.length) await model.getColumns(context);
// extract only pk and pv
if (extractOnlyPrimaries) {
@ -83,10 +87,12 @@ const getAst = async ({
...(model.displayValue ? { [model.displayValue.title]: 1 } : {}),
};
await Promise.all(
model.primaryKeys.map((c) => extractDependencies(c, dependencyFields)),
model.primaryKeys.map((c) =>
extractDependencies(context, c, dependencyFields),
),
);
await extractDependencies(model.displayValue, dependencyFields);
await extractDependencies(context, model.displayValue, dependencyFields);
return { ast, dependencyFields, parsedQuery: dependencyFields };
}
@ -102,6 +108,7 @@ const getAst = async ({
await Promise.all(
(dependencyFieldsForCalenderView || []).map((f) =>
extractDependencies(
context,
model.columns.find((c) => c.id === f),
dependencyFields,
),
@ -115,8 +122,8 @@ const getAst = async ({
if (fields && fields !== '*') {
fields = Array.isArray(fields) ? fields : fields.split(',');
if (throwErrorIfInvalidParams) {
const colAliasMap = await model.getColAliasMapping();
const aliasColMap = await model.getAliasColObjMap();
const colAliasMap = await model.getColAliasMapping(context);
const aliasColMap = await model.getAliasColObjMap(context);
const invalidFields = fields.filter(
(f) => !colAliasMap[f] && !aliasColMap[f],
);
@ -130,7 +137,7 @@ const getAst = async ({
let allowedCols = null;
if (view) {
allowedCols = (await View.getColumns(view.id)).reduce(
allowedCols = (await View.getColumns(context, view.id)).reduce(
(o, c) => ({
...o,
[c.fk_column_id]: c.show || (c instanceof GridViewColumn && c.group_by),
@ -154,10 +161,10 @@ const getAst = async ({
if (nestedFields && nestedFields !== '*') {
if (col.uidt === UITypes.LinkToAnotherRecord) {
const model = await col
.getColOptions<LinkToAnotherRecordColumn>()
.then((colOpt) => colOpt.getRelatedTable());
.getColOptions<LinkToAnotherRecordColumn>(context)
.then((colOpt) => colOpt.getRelatedTable(context));
const { ast } = await getAst({
const { ast } = await getAst(context, {
model,
query: query?.nested?.[col.title],
dependencyFields: (dependencyFields.nested[col.title] =
@ -180,11 +187,11 @@ const getAst = async ({
}
} else if (col.uidt === UITypes.LinkToAnotherRecord) {
const model = await col
.getColOptions<LinkToAnotherRecordColumn>()
.then((colOpt) => colOpt.getRelatedTable());
.getColOptions<LinkToAnotherRecordColumn>(context)
.then((colOpt) => colOpt.getRelatedTable(context));
value = (
await getAst({
await getAst(context, {
model,
query: query?.nested?.[col.title],
extractOnlyPrimaries: nestedFields !== '*',
@ -221,7 +228,8 @@ const getAst = async ({
isRequested = value;
}
if (isRequested || col.pk) await extractDependencies(col, dependencyFields);
if (isRequested || col.pk)
await extractDependencies(context, col, dependencyFields);
return {
...(await obj),
@ -233,6 +241,7 @@ const getAst = async ({
};
const extractDependencies = async (
context: NcContext,
column: Column,
dependencyFields: DependantFields = {
nested: {},
@ -241,10 +250,10 @@ const extractDependencies = async (
) => {
switch (column.uidt) {
case UITypes.Lookup:
await extractLookupDependencies(column, dependencyFields);
await extractLookupDependencies(context, column, dependencyFields);
break;
case UITypes.LinkToAnotherRecord:
await extractRelationDependencies(column, dependencyFields);
await extractRelationDependencies(context, column, dependencyFields);
break;
default:
dependencyFields.fieldsSet.add(column.title);
@ -253,17 +262,19 @@ const extractDependencies = async (
};
const extractLookupDependencies = async (
context: NcContext,
lookUpColumn: Column<LookupColumn>,
dependencyFields: DependantFields = {
nested: {},
fieldsSet: new Set(),
},
) => {
const lookupColumnOpts = await lookUpColumn.getColOptions();
const relationColumn = await lookupColumnOpts.getRelationColumn();
await extractRelationDependencies(relationColumn, dependencyFields);
const lookupColumnOpts = await lookUpColumn.getColOptions(context);
const relationColumn = await lookupColumnOpts.getRelationColumn(context);
await extractRelationDependencies(context, relationColumn, dependencyFields);
await extractDependencies(
await lookupColumnOpts.getLookupColumn(),
context,
await lookupColumnOpts.getLookupColumn(context),
(dependencyFields.nested[relationColumn.title] = dependencyFields.nested[
relationColumn.title
] || {
@ -274,34 +285,43 @@ const extractLookupDependencies = async (
};
const extractRelationDependencies = async (
context: NcContext,
relationColumn: Column<LinkToAnotherRecordColumn>,
dependencyFields: DependantFields = {
nested: {},
fieldsSet: new Set(),
},
) => {
const relationColumnOpts = await relationColumn.getColOptions();
const relationColumnOpts = await relationColumn.getColOptions(context);
switch (relationColumnOpts.type) {
case RelationTypes.HAS_MANY:
dependencyFields.fieldsSet.add(
await relationColumnOpts.getParentColumn().then((col) => col.title),
await relationColumnOpts
.getParentColumn(context)
.then((col) => col.title),
);
break;
case RelationTypes.BELONGS_TO:
case RelationTypes.MANY_TO_MANY:
dependencyFields.fieldsSet.add(
await relationColumnOpts.getChildColumn().then((col) => col.title),
await relationColumnOpts
.getChildColumn(context)
.then((col) => col.title),
);
break;
case RelationTypes.ONE_TO_ONE:
if (relationColumn.meta?.bt) {
dependencyFields.fieldsSet.add(
await relationColumnOpts.getChildColumn().then((col) => col.title),
await relationColumnOpts
.getChildColumn(context)
.then((col) => col.title),
);
} else {
dependencyFields.fieldsSet.add(
await relationColumnOpts.getParentColumn().then((col) => col.title),
await relationColumnOpts
.getParentColumn(context)
.then((col) => col.title),
);
}
break;

27
packages/nocodb/src/helpers/initAdminFromEnv.ts

@ -8,7 +8,7 @@ import isEmail from 'validator/lib/isEmail';
import NocoCache from '~/cache/NocoCache';
import Noco from '~/Noco';
import { BaseUser, User } from '~/models';
import { CacheScope, MetaTable } from '~/utils/globals';
import { CacheScope, MetaTable, RootScopes } from '~/utils/globals';
import { randomTokenString } from '~/services/users/helpers';
const rolesLevel = { owner: 0, creator: 1, editor: 2, commenter: 3, viewer: 4 };
@ -91,7 +91,11 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
);
const email_verification_token = uuidv4();
// TODO improve this
const superUsers = await ncMeta.metaList2(null, null, MetaTable.USERS);
const superUsers = await ncMeta.metaList2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.USERS,
);
let superUserPresent = false;
@ -113,14 +117,9 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
if (existingUserWithNewEmail?.id) {
// get all base access belongs to the existing account
// and migrate to the admin account
const existingUserProjects = await ncMeta.metaList2(
null,
null,
MetaTable.PROJECT_USERS,
{
condition: { fk_user_id: existingUserWithNewEmail.id },
},
);
const existingUserProjects = await ncMeta
.knexConnection(MetaTable.PROJECT_USERS)
.where({ fk_user_id: existingUserWithNewEmail.id });
for (const existingUserProject of existingUserProjects) {
const userProject = await BaseUser.get(
@ -137,6 +136,10 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
rolesLevel[existingUserProject.roles]
) {
await BaseUser.update(
{
workspace_id: existingUserProject.workspace_id,
base_id: existingUserProject.base_id,
},
userProject.base_id,
user.id,
existingUserProject.roles,
@ -163,8 +166,8 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
// delete existing user
await ncMeta.metaDelete(
null,
null,
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.USERS,
existingUserWithNewEmail.id,
);

134
packages/nocodb/src/helpers/populateMeta.ts

@ -8,6 +8,7 @@ import type LinkToAnotherRecordColumn from '~/models/LinkToAnotherRecordColumn';
import type Source from '~/models/Source';
import type Base from '~/models/Base';
import type PGClient from '~/db/sql-client/lib/pg/PgClient';
import type { NcContext } from '~/interface/config';
import mapDefaultDisplayValue from '~/helpers/mapDefaultDisplayValue';
import getColumnUiType from '~/helpers/getColumnUiType';
import getTableNameAlias, { getColumnNameAlias } from '~/helpers/getTableName';
@ -46,16 +47,19 @@ export const IGNORE_TABLES = [
];
async function isMMRelationExist(
context: NcContext,
model: Model,
assocModel: Model,
belongsToCol: Column<LinkToAnotherRecordColumn>,
) {
let isExist = false;
const colChildOpt =
await belongsToCol.getColOptions<LinkToAnotherRecordColumn>();
for (const col of await model.getColumns()) {
await belongsToCol.getColOptions<LinkToAnotherRecordColumn>(context);
for (const col of await model.getColumns(context)) {
if (col.uidt === UITypes.LinkToAnotherRecord) {
const colOpt = await col.getColOptions<LinkToAnotherRecordColumn>();
const colOpt = await col.getColOptions<LinkToAnotherRecordColumn>(
context,
);
if (
colOpt &&
colOpt.type === RelationTypes.MANY_TO_MANY &&
@ -73,10 +77,11 @@ async function isMMRelationExist(
// @ts-ignore
export async function extractAndGenerateManyToManyRelations(
context: NcContext,
modelsArr: Array<Model>,
) {
for (const assocModel of modelsArr) {
await assocModel.getColumns();
await assocModel.getColumns(context);
// check if table is a Bridge table(or Associative Table) by checking
// number of foreign keys and columns
@ -84,7 +89,9 @@ export async function extractAndGenerateManyToManyRelations(
const belongsToCols: Column<LinkToAnotherRecordColumn>[] = [];
for (const col of assocModel.columns) {
if (col.uidt == UITypes.LinkToAnotherRecord) {
const colOpt = await col.getColOptions<LinkToAnotherRecordColumn>();
const colOpt = await col.getColOptions<LinkToAnotherRecordColumn>(
context,
);
if (colOpt?.type === RelationTypes.BELONGS_TO) belongsToCols.push(col);
}
}
@ -99,26 +106,28 @@ export async function extractAndGenerateManyToManyRelations(
belongsToCols.some((c) => c.colOptions?.fk_child_column_id === pk.id),
)
) {
const modelA = await belongsToCols[0].colOptions.getRelatedTable();
const modelB = await belongsToCols[1].colOptions.getRelatedTable();
const modelA = await belongsToCols[0].colOptions.getRelatedTable(context);
const modelB = await belongsToCols[1].colOptions.getRelatedTable(context);
await modelA.getColumns();
await modelB.getColumns();
await modelA.getColumns(context);
await modelB.getColumns(context);
// check tableA already have the relation or not
const isRelationAvailInA = await isMMRelationExist(
context,
modelA,
assocModel,
belongsToCols[0],
);
const isRelationAvailInB = await isMMRelationExist(
context,
modelB,
assocModel,
belongsToCols[1],
);
if (!isRelationAvailInA) {
await Column.insert<LinkToAnotherRecordColumn>({
await Column.insert<LinkToAnotherRecordColumn>(context, {
title: getUniqueColumnAliasName(
modelA.columns,
pluralize(modelB.title),
@ -140,7 +149,7 @@ export async function extractAndGenerateManyToManyRelations(
});
}
if (!isRelationAvailInB) {
await Column.insert<LinkToAnotherRecordColumn>({
await Column.insert<LinkToAnotherRecordColumn>(context, {
title: getUniqueColumnAliasName(
modelB.columns,
pluralize(modelA.title),
@ -162,17 +171,19 @@ export async function extractAndGenerateManyToManyRelations(
});
}
await Model.markAsMmTable(assocModel.id, true);
await Model.markAsMmTable(context, assocModel.id, true);
// mark has many relation associated with mm as system field in both table
for (const btCol of [belongsToCols[0], belongsToCols[1]]) {
const colOpt = await btCol.colOptions;
const model = await colOpt.getRelatedTable();
const model = await colOpt.getRelatedTable(context);
for (const col of await model.getColumns()) {
for (const col of await model.getColumns(context)) {
if (!isLinksOrLTAR(col.uidt)) continue;
const colOpt1 = await col.getColOptions<LinkToAnotherRecordColumn>();
const colOpt1 = await col.getColOptions<LinkToAnotherRecordColumn>(
context,
);
if (!colOpt1 || colOpt1.type !== RelationTypes.HAS_MANY) continue;
if (
@ -181,17 +192,19 @@ export async function extractAndGenerateManyToManyRelations(
)
continue;
await Column.markAsSystemField(col.id);
await Column.markAsSystemField(context, col.id);
break;
}
}
} else {
if (assocModel.mm) await Model.markAsMmTable(assocModel.id, false);
if (assocModel.mm)
await Model.markAsMmTable(context, assocModel.id, false);
}
}
}
export async function populateMeta(
context: NcContext,
source: Source,
base: Base,
logger?: (message: string) => void,
@ -325,12 +338,17 @@ export async function populateMeta(
// await Model.insert(base.id, base.id, meta);
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(base.id, source.id, {
table_name: table.tn || table.table_name,
title: table.title,
type: table.type || 'table',
order: table.order,
});
models2[table.table_name] = await Model.insert(
context,
base.id,
source.id,
{
table_name: table.tn || table.table_name,
title: table.title,
type: table.type || 'table',
order: table.order,
},
);
// table crud apis
info.apiCount += 5;
@ -346,7 +364,7 @@ export async function populateMeta(
}
}
await Column.insert({
await Column.insert(context, {
uidt: column.uidt || getColumnUiType(source, column),
fk_model_id: models2[table.tn].id,
...column,
@ -370,20 +388,20 @@ export async function populateMeta(
const rel = column.hm || column.bt;
const rel_column_id = (await models2?.[rel.tn]?.getColumns())?.find(
(c) => c.column_name === rel.cn,
)?.id;
const rel_column_id = (
await models2?.[rel.tn]?.getColumns(context)
)?.find((c) => c.column_name === rel.cn)?.id;
const tnId = models2?.[rel.tn]?.id;
const ref_rel_column_id = (
await models2?.[rel.rtn]?.getColumns()
await models2?.[rel.rtn]?.getColumns(context)
)?.find((c) => c.column_name === rel.rcn)?.id;
const rtnId = models2?.[rel.rtn]?.id;
try {
await Column.insert<LinkToAnotherRecordColumn>({
await Column.insert<LinkToAnotherRecordColumn>(context, {
base_id: base.id,
db_alias: source.id,
fk_model_id: models2[table.tn].id,
@ -419,7 +437,7 @@ export async function populateMeta(
/* handle xc_tables update in parallel */
await NcHelp.executeOperations(tableMetasInsert, source.type);
await NcHelp.executeOperations(virtualColumnsInsert, source.type);
await extractAndGenerateManyToManyRelations(Object.values(models2));
await extractAndGenerateManyToManyRelations(context, Object.values(models2));
let views: Array<{ order: number; table_name: string; title: string }> = (
await sqlClient.viewList({
@ -456,13 +474,18 @@ export async function populateMeta(
mapDefaultDisplayValue(columns);
/* create nc_models and its rows if it doesn't exists */
models2[table.table_name] = await Model.insert(base.id, source.id, {
table_name: table.table_name,
title: getTableNameAlias(table.table_name, base.prefix, source),
// todo: sanitize
type: ModelTypes.VIEW,
order: table.order,
});
models2[table.table_name] = await Model.insert(
context,
base.id,
source.id,
{
table_name: table.table_name,
title: getTableNameAlias(table.table_name, base.prefix, source),
// todo: sanitize
type: ModelTypes.VIEW,
order: table.order,
},
);
let colOrder = 1;
@ -470,7 +493,7 @@ export async function populateMeta(
info.apiCount += 2;
for (const column of columns) {
await Column.insert({
await Column.insert(context, {
fk_model_id: models2[table.table_name].id,
...column,
title: getColumnNameAlias(column.cn, source),
@ -484,13 +507,16 @@ export async function populateMeta(
await NcHelp.executeOperations(viewMetasInsert, source.type);
// fix pv column for created grid views
const models = await Model.list({ base_id: base.id, source_id: source.id });
const models = await Model.list(context, {
base_id: base.id,
source_id: source.id,
});
for (const model of models) {
const views = await model.getViews();
const views = await model.getViews(context);
for (const view of views) {
if (view.type === ViewTypes.GRID) {
await View.fixPVColumnForView(view.id);
await View.fixPVColumnForView(context, view.id);
}
}
}
@ -506,14 +532,15 @@ export async function populateMeta(
}
export async function populateRollupColumnAndHideLTAR(
context: NcContext,
source: Source,
base: Base,
) {
for (const model of await Model.list({
for (const model of await Model.list(context, {
base_id: base.id,
source_id: source.id,
})) {
const columns = await model.getColumns();
const columns = await model.getColumns(context);
const hmAndMmLTARColumns = columns.filter(
(c) =>
c.uidt === UITypes.LinkToAnotherRecord &&
@ -521,20 +548,21 @@ export async function populateRollupColumnAndHideLTAR(
!c.system,
);
const views = await model.getViews();
const views = await model.getViews(context);
for (const column of hmAndMmLTARColumns) {
const relatedModel = await column
.getColOptions<LinkToAnotherRecordColumn>()
.then((colOpt) => colOpt.getRelatedTable());
await relatedModel.getColumns();
.getColOptions<LinkToAnotherRecordColumn>(context)
.then((colOpt) => colOpt.getRelatedTable(context));
await relatedModel.getColumns(context);
const pkId =
relatedModel.primaryKey?.id || (await relatedModel.getColumns())[0]?.id;
relatedModel.primaryKey?.id ||
(await relatedModel.getColumns(context))[0]?.id;
await Column.insert<RollupColumn>({
await Column.insert<RollupColumn>(context, {
uidt: UITypes.Links,
title: getUniqueColumnAliasName(
await model.getColumns(),
await model.getColumns(context),
`${relatedModel.title}`,
),
fk_rollup_column_id: pkId,
@ -547,10 +575,10 @@ export async function populateRollupColumnAndHideLTAR(
},
});
const viewCol = await GridViewColumn.list(views[0].id).then((cols) =>
cols.find((c) => c.fk_column_id === column.id),
const viewCol = await GridViewColumn.list(context, views[0].id).then(
(cols) => cols.find((c) => c.fk_column_id === column.id),
);
await GridViewColumn.update(viewCol.id, { show: false });
await GridViewColumn.update(context, viewCol.id, { show: false });
}
}
}

56
packages/nocodb/src/helpers/populateSamplePayload.ts

@ -5,9 +5,11 @@ import type {
LookupColumn,
SelectOption,
} from '~/models';
import type { NcContext } from '~/interface/config';
import { Column, Model, View } from '~/models';
export async function populateSamplePayload(
context: NcContext,
viewOrModel: View | Model,
includeNested = false,
operation = 'insert',
@ -16,14 +18,15 @@ export async function populateSamplePayload(
let columns: Column[] = [];
let model: Model;
if (viewOrModel instanceof View) {
const viewColumns = await viewOrModel.getColumns();
const viewColumns = await viewOrModel.getColumns(context);
for (const col of viewColumns) {
if (col.show) columns.push(await Column.get({ colId: col.fk_column_id }));
if (col.show)
columns.push(await Column.get(context, { colId: col.fk_column_id }));
}
model = await viewOrModel.getModel();
await model.getColumns();
model = await viewOrModel.getModel(context);
await model.getColumns(context);
} else if (viewOrModel instanceof Model) {
columns = await viewOrModel.getColumns();
columns = await viewOrModel.getColumns(context);
model = viewOrModel;
}
@ -37,13 +40,14 @@ export async function populateSamplePayload(
if (operation === 'delete' && model.primaryKey?.title !== column.title)
continue;
out[column.title] = await getSampleColumnValue(column);
out[column.title] = await getSampleColumnValue(context, column);
}
return out;
}
export async function populateSamplePayloadV2(
context: NcContext,
viewOrModel: View | Model,
includeNested = false,
operation = 'insert',
@ -53,18 +57,19 @@ export async function populateSamplePayloadV2(
let columns: Column[] = [];
let model: Model;
if (viewOrModel instanceof View) {
const viewColumns = await viewOrModel.getColumns();
const viewColumns = await viewOrModel.getColumns(context);
for (const col of viewColumns) {
if (col.show) columns.push(await Column.get({ colId: col.fk_column_id }));
if (col.show)
columns.push(await Column.get(context, { colId: col.fk_column_id }));
}
model = await viewOrModel.getModel();
await model.getColumns();
model = await viewOrModel.getModel(context);
await model.getColumns(context);
} else if (viewOrModel instanceof Model) {
columns = await viewOrModel.getColumns();
columns = await viewOrModel.getColumns(context);
model = viewOrModel;
}
await model.getViews();
await model.getViews(context);
const samplePayload = {
type: `${scope}.after.${operation}`,
@ -84,7 +89,7 @@ export async function populateSamplePayloadV2(
)
continue;
rows[column.title] = await getSampleColumnValue(column);
rows[column.title] = await getSampleColumnValue(context, column);
}
let prevRows;
@ -105,7 +110,10 @@ export async function populateSamplePayloadV2(
return samplePayload;
}
async function getSampleColumnValue(column: Column): Promise<any> {
async function getSampleColumnValue(
context: NcContext,
column: Column,
): Promise<any> {
switch (column.uidt) {
case UITypes.ID:
{
@ -114,9 +122,12 @@ async function getSampleColumnValue(column: Column): Promise<any> {
break;
case UITypes.LinkToAnotherRecord:
{
const colOpt = await column.getColOptions<LinkToAnotherRecordColumn>();
const colOpt = await column.getColOptions<LinkToAnotherRecordColumn>(
context,
);
const sampleVal = await populateSamplePayload(
await colOpt.getRelatedTable(),
context,
await colOpt.getRelatedTable(context),
);
if (colOpt.type !== RelationTypes.BELONGS_TO) {
return undefined;
@ -133,12 +144,13 @@ async function getSampleColumnValue(column: Column): Promise<any> {
break;
case UITypes.Lookup:
{
const colOpt = await column.getColOptions<LookupColumn>();
const colOpt = await column.getColOptions<LookupColumn>(context);
const relColOpt = await colOpt
.getRelationColumn()
.then((r) => r.getColOptions<LinkToAnotherRecordColumn>());
.getRelationColumn(context)
.then((r) => r.getColOptions<LinkToAnotherRecordColumn>(context));
const sampleVal = await getSampleColumnValue(
await colOpt.getLookupColumn(),
context,
await colOpt.getLookupColumn(context),
);
return relColOpt.type === RelationTypes.BELONGS_TO
? sampleVal
@ -174,7 +186,7 @@ async function getSampleColumnValue(column: Column): Promise<any> {
break;
case UITypes.MultiSelect:
{
const colOpt = await column.getColOptions<SelectOption[]>();
const colOpt = await column.getColOptions<SelectOption[]>(context);
return (
colOpt?.[0]?.title ||
column?.dtxp?.split(',')?.[0]?.replace(/^['"]|['"]$/g, '')
@ -183,7 +195,7 @@ async function getSampleColumnValue(column: Column): Promise<any> {
break;
case UITypes.SingleSelect:
{
const colOpt = await column.getColOptions<SelectOption[]>();
const colOpt = await column.getColOptions<SelectOption[]>(context);
return (
colOpt?.[0]?.title ||
column?.dtxp?.split(',')?.[0]?.replace(/^['"]|['"]$/g, '')

10
packages/nocodb/src/helpers/syncMigration.ts

@ -5,7 +5,10 @@ export default async function syncMigration(base: Base): Promise<void> {
for (const source of await base.getSources()) {
try {
/* create sql-migrator */
const migrator = new KnexMigratorv2(base);
const migrator = new KnexMigratorv2(
{ workspace_id: base.fk_workspace_id, base_id: base.id },
base,
);
await migrator.init(source);
@ -26,7 +29,10 @@ export async function syncBaseMigration(
): Promise<void> {
try {
/* create sql-migrator */
const migrator = new KnexMigratorv2(base);
const migrator = new KnexMigratorv2(
{ workspace_id: base.fk_workspace_id, base_id: base.id },
base,
);
await migrator.init(source);

48
packages/nocodb/src/helpers/webhookHelpers.ts

@ -11,6 +11,7 @@ import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import NcPluginMgrv2 from './NcPluginMgrv2';
import type { HookLogType } from 'nocodb-sdk';
import type { Column, FormView, Hook, Model, View } from '~/models';
import type { NcContext } from '~/interface/config';
import { Filter, HookLog, Source } from '~/models';
dayjs.extend(isBetween);
@ -40,6 +41,7 @@ export function parseBody(template: string, data: any): string {
}
export async function validateCondition(
context: NcContext,
filters: Filter[],
data: any,
{
@ -58,14 +60,15 @@ export async function validateCondition(
let res;
if (filter.is_group) {
res = await validateCondition(
filter.children || (await filter.getChildren()),
context,
filter.children || (await filter.getChildren(context)),
data,
{
client,
},
);
} else {
const column = await filter.getColumn();
const column = await filter.getColumn(context);
const field = column.title;
let val = data[field];
if (
@ -457,17 +460,20 @@ export function axiosRequestMake(_apiMeta, _user, data) {
return req;
}
export async function invokeWebhook(param: {
hook: Hook;
model: Model;
view: View;
prevData;
newData;
user;
testFilters?;
throwErrorOnFailure?: boolean;
testHook?: boolean;
}) {
export async function invokeWebhook(
context: NcContext,
param: {
hook: Hook;
model: Model;
view: View;
prevData;
newData;
user;
testFilters?;
throwErrorOnFailure?: boolean;
testHook?: boolean;
},
) {
const {
hook,
model,
@ -483,7 +489,7 @@ export async function invokeWebhook(param: {
let hookLog: HookLogType;
const startTime = process.hrtime();
const source = await Source.get(model.source_id);
const source = await Source.get(context, model.source_id);
let notification;
try {
notification =
@ -499,7 +505,7 @@ export async function invokeWebhook(param: {
}
if (hook.condition && !testHook) {
const filters = testFilters || (await hook.getFilters());
const filters = testFilters || (await hook.getFilters(context));
if (isBulkOperation) {
const filteredData = [];
@ -521,7 +527,8 @@ export async function invokeWebhook(param: {
if (
await validateCondition(
testFilters || (await hook.getFilters()),
context,
testFilters || (await hook.getFilters(context)),
data,
{ client: source?.type },
)
@ -539,13 +546,16 @@ export async function invokeWebhook(param: {
if (
prevData &&
filters.length &&
(await validateCondition(filters, prevData, { client: source?.type }))
(await validateCondition(context, filters, prevData, {
client: source?.type,
}))
) {
return;
}
if (
!(await validateCondition(
testFilters || (await hook.getFilters()),
context,
testFilters || (await hook.getFilters(context)),
newData,
{ client: source?.type },
))
@ -678,7 +688,7 @@ export async function invokeWebhook(param: {
hookLog.execution_time = parseHrtimeToMilliSeconds(
process.hrtime(startTime),
);
HookLog.insert({ ...hookLog, test_call: testHook });
HookLog.insert(context, { ...hookLog, test_call: testHook });
}
}
}

2
packages/nocodb/src/interface/Jobs.ts

@ -1,3 +1,4 @@
import type { NcContext } from '~/interface/config';
export const JOBS_QUEUE = 'jobs';
export enum JobTypes {
@ -44,6 +45,7 @@ export enum InstanceCommands {
}
export interface HandleWebhookJobData {
context: NcContext;
hookId: string;
modelId: string;
viewId: string;

24
packages/nocodb/src/interface/config.ts

@ -1,10 +1,7 @@
import type { UserType } from 'nocodb-sdk';
import type { ReqId } from 'pino-http';
import type { Handler } from 'express';
import type { Handler, Request } from 'express';
import type * as e from 'express';
import type { Knex } from 'knex';
import type { User } from '~/models';
import type { IncomingHttpHeaders } from 'http';
export interface Route {
path: string;
@ -324,12 +321,23 @@ export interface AppConfig {
dashboardPath: string;
}
export interface NcRequest {
id?: ReqId;
user?: UserType | User;
export interface NcContext {
org_id?: string;
workspace_id: string;
base_id: string;
}
export interface NcRequest extends Partial<Request> {
context: NcContext;
ncWorkspaceId?: string;
ncBaseId?: string;
headers?: Record<string, string | undefined> | IncomingHttpHeaders;
user: UserType & {
base_roles?: Record<string, boolean>;
workspace_roles?: Record<string, boolean>;
provider?: string;
};
ncSiteUrl: string;
dashboardUrl: string;
clientIp?: string;
query?: Record<string, any>;
}

453
packages/nocodb/src/meta/meta.service.ts

@ -11,7 +11,7 @@ import XcMigrationSource from '~/meta/migrations/XcMigrationSource';
import XcMigrationSourcev2 from '~/meta/migrations/XcMigrationSourcev2';
import { XKnex } from '~/db/CustomKnex';
import { NcConfig } from '~/utils/nc-config';
import { MetaTable } from '~/utils/globals';
import { MetaTable, RootScopes, RootScopeTables } from '~/utils/globals';
import { NcError } from '~/helpers/catchError';
dayjs.extend(utc);
dayjs.extend(timezone);
@ -53,65 +53,53 @@ export class MetaService {
return this.knexConnection;
}
public contextCondition(
query: Knex.QueryBuilder,
workspace_id: string,
base_id: string,
target: string,
) {
if (workspace_id === base_id) {
return;
}
if (target !== MetaTable.PROJECT) {
query.where('base_id', base_id);
} else {
query.where('id', base_id);
}
}
/***
* Get single record from meta data
* @param base_id - Base id
* @param dbAlias - Database alias
* @param workspace_id - Workspace id
* @param base_id - Base alias
* @param target - Table name
* @param idOrCondition - If string, will get the record with the given id. If object, will get the record with the given condition.
* @param fields - Fields to be selected
*/
public async metaGet(
workspace_id: string,
base_id: string,
dbAlias: string,
target: string,
idOrCondition: string | { [p: string]: any },
fields?: string[],
// xcCondition?
): Promise<any> {
const query = this.connection(target);
// if (xcCondition) {
// query.condition(xcCondition);
// }
if (fields?.length) {
query.select(...fields);
}
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
if (dbAlias !== null && dbAlias !== undefined) {
query.where('db_alias', dbAlias);
}
if (!idOrCondition) {
return query.first();
}
if (typeof idOrCondition !== 'object') {
query.where('id', idOrCondition);
} else {
query.where(idOrCondition);
}
// console.log(query.toQuery())
return query.first();
return this.metaGet2(workspace_id, base_id, target, idOrCondition, fields);
}
/***
* Insert record into meta data
* @param base_id - Base id
* @param fk_workspace_id - Base id
* @param dbAlias - Database alias
* @param target - Table name
* @param data - Data to be inserted
* @param ignoreIdGeneration - If true, will not generate id for the record
*/
public async metaInsert2(
workspace_id: string,
base_id: string,
source_id: string,
target: string,
data: any,
ignoreIdGeneration?: boolean,
@ -122,8 +110,31 @@ export class MetaService {
? {}
: { id: data?.id || (await this.genNanoid(target)) }),
};
if (source_id !== null) insertObj.source_id = source_id;
if (base_id !== null) insertObj.base_id = base_id;
if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
insertObj.base_id = base_id;
}
await this.knexConnection(target).insert({
...insertObj,
@ -135,15 +146,15 @@ export class MetaService {
/***
* Insert multiple records into meta data
* @param base_id - Base id
* @param source_id - Source id
* @param workspace_id - Workspace id
* @param base_id - Source id
* @param target - Table name
* @param data - Data to be inserted
* @param ignoreIdGeneration - If true, will not generate id for the record
*/
public async bulkMetaInsert(
workspace_id: string,
base_id: string,
source_id: string,
target: string,
data: any | any[],
ignoreIdGeneration?: boolean,
@ -160,8 +171,29 @@ export class MetaService {
updated_at: at,
};
if (source_id !== null) commonProps.source_id = source_id;
if (base_id !== null) commonProps.base_id = base_id;
if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
commonProps.base_id = base_id;
}
for (const d of Array.isArray(data) ? data : [data]) {
const id = d?.id || (await this.genNanoid(target));
@ -226,71 +258,6 @@ export class MetaService {
// using nanoid to avoid collision with existing ids when duplicating
return `${prefix}${nanoidv2()}`;
}
/***
* Get paginated list of meta data
* @param baseId - Base id
* @param dbAlias - Database alias
* @param target - Table name
* @param args.condition - Condition to be applied
* @param args.limit - Limit of records
* @param args.offset - Offset of records
* @param args.xcCondition - Additional nested or complex condition to be added to the query.
* @param args.fields - Fields to be selected
* @param args.sort - Sort field and direction
* @returns {Promise<{list: any[]; count: number}>} - List of records and count
* */
public async metaPaginatedList(
baseId: string,
dbAlias: string,
target: string,
args?: {
condition?: { [key: string]: any };
limit?: number;
offset?: number;
xcCondition?: Condition;
fields?: string[];
sort?: { field: string; desc?: boolean };
},
): Promise<{ list: any[]; count: number }> {
const query = this.knexConnection(target);
const countQuery = this.knexConnection(target);
if (baseId !== null && baseId !== undefined) {
query.where('base_id', baseId);
countQuery.where('base_id', baseId);
}
if (dbAlias !== null && dbAlias !== undefined) {
query.where('db_alias', dbAlias);
countQuery.where('db_alias', dbAlias);
}
if (args?.condition) {
query.where(args.condition);
countQuery.where(args.condition);
}
if (args?.limit) {
query.limit(args.limit);
}
if (args?.sort) {
query.orderBy(args.sort.field, args.sort.desc ? 'desc' : 'asc');
}
if (args?.offset) {
query.offset(args.offset);
}
if (args?.xcCondition) {
(query as any)
.condition(args.xcCondition)(countQuery as any)
.condition(args.xcCondition);
}
if (args?.fields?.length) {
query.select(...args.fields);
}
return {
list: await query,
count: Object.values(await countQuery.count().first())?.[0] as any,
};
}
// private connection: XKnex;
// todo: need to fix
@ -298,16 +265,16 @@ export class MetaService {
/***
* Delete meta data
* @param workspace_id - Workspace id
* @param base_id - Base id
* @param dbAlias - Database alias
* @param target - Table name
* @param idOrCondition - If string, will delete the record with the given id. If object, will delete the record with the given condition.
* @param xcCondition - Additional nested or complex condition to be added to the query.
* @param force - If true, will not check if a condition is present in the query builder and will execute the query as is.
*/
public async metaDelete(
workspace_id: string,
base_id: string,
dbAlias: string,
target: string,
idOrCondition: string | { [p: string]: any },
xcCondition?: Condition,
@ -315,11 +282,27 @@ export class MetaService {
): Promise<void> {
const query = this.knexConnection(target);
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
if (dbAlias !== null && dbAlias !== undefined) {
query.where('db_alias', dbAlias);
if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
}
if (typeof idOrCondition !== 'object') {
@ -337,21 +320,53 @@ export class MetaService {
this.checkConditionPresent(query, 'delete');
}
// Apply context condition
this.contextCondition(query, workspace_id, base_id, target);
return query.del();
}
/***
* Delete meta data with condition (USE WITH CAUTION)
* @param target - Table name
* @param idOrCondition - If string, will delete the record with the given id. If object, will delete the record with the given condition.
* @param xcCondition - Additional nested or complex condition to be added to the query.
*/
public async metaDeleteAll(
target: string,
idOrCondition: string | { [p: string]: any },
xcCondition?: Condition,
): Promise<void> {
const query = this.knexConnection(target);
if (typeof idOrCondition !== 'object') {
query.where('id', idOrCondition);
} else if (idOrCondition) {
query.where(idOrCondition);
}
if (xcCondition) {
query.condition(xcCondition, {});
}
// Check if a condition is present in the query builder and throw an error if not.
this.checkConditionPresent(query, 'delete');
return query.del();
}
/***
* Get meta data
* @param workspace_id - Workspace id
* @param base_id - Base id
* @param sourceId - Source id
* @param target - Table name
* @param idOrCondition - If string, will get the record with the given id. If object, will get the record with the given condition.
* @param fields - Fields to be selected
* @param xcCondition - Additional nested or complex condition to be added to the query.
*/
public async metaGet2(
workspace_id: string,
base_id: string,
sourceId: string,
target: string,
idOrCondition: string | { [p: string]: any },
fields?: string[],
@ -367,11 +382,31 @@ export class MetaService {
query.select(...fields);
}
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
if (sourceId !== null && sourceId !== undefined) {
query.where('source_id', sourceId);
if (workspace_id === RootScopes.BYPASS && base_id === RootScopes.BYPASS) {
// bypass
} else if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
this.contextCondition(query, workspace_id, base_id, target);
}
if (!idOrCondition) {
@ -404,72 +439,10 @@ export class MetaService {
return (+(await query.first())?.order || 0) + 1;
}
public async metaInsert(
base_id: string,
dbAlias: string,
target: string,
data: any,
): Promise<any> {
return this.knexConnection(target).insert({
db_alias: dbAlias,
base_id,
...data,
created_at: this.now(),
updated_at: this.now(),
});
}
public async metaList(
base_id: string,
_dbAlias: string,
target: string,
args?: {
condition?: { [p: string]: any };
limit?: number;
offset?: number;
xcCondition?: Condition;
fields?: string[];
orderBy?: { [key: string]: 'asc' | 'desc' };
},
): Promise<any[]> {
const query = this.knexConnection(target);
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
/* if (dbAlias !== null && dbAlias !== undefined) {
query.where('db_alias', dbAlias);
}*/
if (args?.condition) {
query.where(args.condition);
}
if (args?.limit) {
query.limit(args.limit);
}
if (args?.offset) {
query.offset(args.offset);
}
if (args?.xcCondition) {
(query as any).condition(args.xcCondition);
}
if (args?.orderBy) {
for (const [col, dir] of Object.entries(args.orderBy)) {
query.orderBy(col, dir);
}
}
if (args?.fields?.length) {
query.select(...args.fields);
}
return query;
}
/***
* Get list of meta data
* @param workspace_id - Workspace id
* @param base_id - Base id
* @param dbAlias - Database alias
* @param target - Table name
* @param args.condition - Condition to be applied
* @param args.limit - Limit of records
@ -480,8 +453,8 @@ export class MetaService {
* @returns {Promise<any[]>} - List of records
* */
public async metaList2(
workspace_id: string,
base_id: string,
dbAlias: string,
target: string,
args?: {
condition?: { [p: string]: any };
@ -494,11 +467,29 @@ export class MetaService {
): Promise<any[]> {
const query = this.knexConnection(target);
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
if (dbAlias !== null && dbAlias !== undefined) {
query.where('source_id', dbAlias);
if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
this.contextCondition(query, workspace_id, base_id, target);
}
if (args?.condition) {
@ -528,8 +519,8 @@ export class MetaService {
/***
* Get count of meta data
* @param workspace_id - Workspace id
* @param base_id - Base id
* @param dbAlias - Database alias
* @param target - Table name
* @param args.condition - Condition to be applied
* @param args.xcCondition - Additional nested or complex condition to be added to the query.
@ -537,8 +528,8 @@ export class MetaService {
* @returns {Promise<number>} - Count of records
* */
public async metaCount(
workspace_id: string,
base_id: string,
dbAlias: string,
target: string,
args?: {
condition?: { [p: string]: any };
@ -548,11 +539,31 @@ export class MetaService {
): Promise<number> {
const query = this.knexConnection(target);
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
if (dbAlias !== null && dbAlias !== undefined) {
query.where('source_id', dbAlias);
if (workspace_id === RootScopes.BYPASS && base_id === RootScopes.BYPASS) {
// bypass
} else if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
this.contextCondition(query, workspace_id, base_id, target);
}
if (args?.condition) {
@ -570,8 +581,8 @@ export class MetaService {
/***
* Update meta data
* @param workspace_id - Workspace id
* @param base_id - Base id
* @param dbAlias - Database alias
* @param target - Table name
* @param data - Data to be updated
* @param idOrCondition - If string, will update the record with the given id. If object, will update the record with the given condition.
@ -579,8 +590,8 @@ export class MetaService {
* @param force - If true, will not check if a condition is present in the query builder and will execute the query as is.
*/
public async metaUpdate(
workspace_id: string,
base_id: string,
dbAlias: string,
target: string,
data: any,
idOrCondition?: string | { [p: string]: any },
@ -588,11 +599,28 @@ export class MetaService {
force = false,
): Promise<any> {
const query = this.knexConnection(target);
if (base_id !== null && base_id !== undefined) {
query.where('base_id', base_id);
}
if (dbAlias !== null && dbAlias !== undefined) {
query.where('db_alias', dbAlias);
if (workspace_id === base_id) {
if (!Object.values(RootScopes).includes(workspace_id as RootScopes)) {
NcError.metaError({
message: 'Invalid scope',
sql: '',
});
}
if (!RootScopeTables[workspace_id].includes(target)) {
NcError.metaError({
message: 'Table not accessible from this scope',
sql: '',
});
}
} else {
if (!base_id) {
NcError.metaError({
message: 'Base ID is required',
sql: '',
});
}
}
delete data.created_at;
@ -612,6 +640,9 @@ export class MetaService {
this.checkConditionPresent(query, 'update');
}
// Apply context condition
this.contextCondition(query, workspace_id, base_id, target);
return await query;
}
@ -696,10 +727,18 @@ export class MetaService {
);
}
private isMssql(): boolean {
return this.connection.clientType() === 'mssql';
}
public now(): any {
return dayjs()
.utc()
.format(this.isMySQL() ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ');
.format(
this.isMySQL() || this.isMssql()
? 'YYYY-MM-DD HH:mm:ss'
: 'YYYY-MM-DD HH:mm:ssZ',
);
}
public async init(): Promise<boolean> {
@ -719,7 +758,7 @@ export class MetaService {
*
* @param queryBuilder - The Knex QueryBuilder instance to check.
*/
private checkConditionPresent(
protected checkConditionPresent(
queryBuilder: Knex.QueryBuilder,
operation: 'delete' | 'update',
) {

4
packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts

@ -36,6 +36,7 @@ import * as nc_046_comment_mentions from '~/meta/migrations/v2/nc_046_comment_me
import * as nc_047_comment_migration from '~/meta/migrations/v2/nc_047_comment_migration';
import * as nc_048_view_links from '~/meta/migrations/v2/nc_048_view_links';
import * as nc_049_clear_notifications from '~/meta/migrations/v2/nc_049_clear_notifications';
import * as nc_050_tenant_isolation from '~/meta/migrations/v2/nc_050_tenant_isolation';
// Create a custom migration source class
export default class XcMigrationSourcev2 {
@ -83,6 +84,7 @@ export default class XcMigrationSourcev2 {
'nc_047_comment_migration',
'nc_048_view_links',
'nc_049_clear_notifications',
'nc_050_tenant_isolation',
]);
}
@ -168,6 +170,8 @@ export default class XcMigrationSourcev2 {
return nc_048_view_links;
case 'nc_049_clear_notifications':
return nc_049_clear_notifications;
case 'nc_050_tenant_isolation':
return nc_050_tenant_isolation;
}
}
}

409
packages/nocodb/src/meta/migrations/v2/nc_050_tenant_isolation.ts

@ -0,0 +1,409 @@
import type { Knex } from 'knex';
import { MetaTable } from '~/utils/globals';
/*
Add base_id to:
nc_calendar_view_range_v2: MetaTable.CALENDAR_VIEW_RANGE
nc_col_barcode_v2: MetaTable.COL_BARCODE
nc_col_formula_v2: MetaTable.COL_FORMULA
nc_col_lookup_v2: MetaTable.COL_LOOKUP
nc_col_qrcode_v2: MetaTable.COL_QRCODE
nc_col_relations_v2: MetaTable.COL_RELATIONS
nc_col_rollup_v2: MetaTable.COL_ROLLUP
nc_col_select_options_v2: MetaTable.COL_SELECT_OPTIONS
*/
const log = (message: string) => {
console.log(`nc_050_tenant_isolation: ${message}`);
};
let hrTime = process.hrtime();
const logExecutionTime = (message: string) => {
const [seconds, nanoseconds] = process.hrtime(hrTime);
const elapsedSeconds = seconds + nanoseconds / 1e9;
log(`${message} in ${elapsedSeconds}s`);
};
const migrateDataWithJoin = async (
knex: Knex,
table: string,
column: string,
joinTable: string,
joinColumn: string,
destinationColumn: string,
sourceColumn: string,
) => {
const sourceType = knex.client.driverName;
switch (sourceType) {
case 'pg':
await knex.raw(`
UPDATE ${table}
SET ${destinationColumn} = ${joinTable}.${sourceColumn}
FROM ${joinTable}
WHERE ${table}.${column} = ${joinTable}.${joinColumn}
`);
break;
case 'mysql':
case 'mysql2':
await knex.raw(`
UPDATE ${table}
JOIN ${joinTable}
ON ${table}.${column} = ${joinTable}.${joinColumn}
SET ${table}.${destinationColumn} = ${joinTable}.${sourceColumn}
`);
break;
case 'sqlite3':
await knex.raw(`
UPDATE ${table}
SET ${destinationColumn} = (
SELECT ${joinTable}.${sourceColumn}
FROM ${joinTable}
WHERE ${table}.${column} = ${joinTable}.${joinColumn}
)
`);
break;
case 'mssql':
await knex.raw(`
UPDATE ${table}
SET ${destinationColumn} = ${joinTable}.${sourceColumn}
FROM ${table}
JOIN ${joinTable}
ON ${table}.${column} = ${joinTable}.${joinColumn}
`);
break;
default:
throw new Error(`Unsupported database: ${sourceType}`);
}
};
const listIndexesOnColumn = async (
knex: Knex,
table: string,
column: string,
) => {
const sourceType = knex.client.driverName;
switch (sourceType) {
case 'pg': {
const indexes = await knex.raw(
`
SELECT
t.relname AS table_name,
i.relname AS index_name,
a.attname AS column_name
FROM
pg_class t,
pg_class i,
pg_index ix,
pg_attribute a
WHERE
t.oid = ix.indrelid
AND i.oid = ix.indexrelid
AND a.attrelid = t.oid
AND a.attnum = ANY(ix.indkey)
AND t.relkind = 'r'
AND t.relname = ?
AND a.attname = ?;
`,
[table, column],
);
return indexes.rows.map((row: any) => row.index_name);
}
case 'mysql':
case 'mysql2': {
const indexes = await knex.raw(
`
SELECT
INDEX_NAME
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
TABLE_SCHEMA = ?
AND TABLE_NAME = ?
AND COLUMN_NAME = ?
`,
[knex.client.database(), table, column],
);
return indexes[0].map((row: any) => row.INDEX_NAME);
}
case 'sqlite3': {
const indexes = await knex.raw(
`
PRAGMA index_list(??)
`,
[table],
);
return indexes
.map((row: any) => row.name)
.filter(
(name: string) =>
name.includes(column) ||
(column === 'base_id' && name.includes('project_id')),
);
}
case 'mssql': {
const indexes = await knex.raw(
`
SELECT
i.name AS index_name
FROM
sys.indexes i
JOIN
sys.index_columns ic
ON
i.object_id = ic.object_id
AND i.index_id = ic.index_id
JOIN
sys.columns c
ON
ic.object_id = c.object_id
AND ic.column_id = c.column_id
WHERE
i.object_id = OBJECT_ID(?)
AND c.name = ?
`,
[table, column],
);
return indexes.map((row: any) => row.index_name);
}
default:
throw new Error(`Unsupported database: ${sourceType}`);
}
};
const up = async (knex: Knex) => {
log('Migration started');
log('Adding missing base_id columns');
const addBaseId = [
MetaTable.CALENDAR_VIEW_RANGE,
MetaTable.COL_BARCODE,
MetaTable.COL_FORMULA,
MetaTable.COL_LOOKUP,
MetaTable.COL_QRCODE,
MetaTable.COL_RELATIONS,
MetaTable.COL_ROLLUP,
MetaTable.COL_SELECT_OPTIONS,
];
hrTime = process.hrtime();
for (const table of addBaseId) {
if (!(await knex.schema.hasColumn(table, 'base_id'))) {
await knex.schema.alterTable(table, (table) => {
table.string('base_id', 20);
});
}
}
logExecutionTime('Added missing base_id columns');
// Migrate data
log('Migrating data');
/*
nc_calendar_view_range_v2 only fk_view_id is available - join with nc_views_v2 on id to get base_id
nc_col_barcode_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
nc_col_formula_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
nc_col_lookup_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
nc_col_qrcode_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
nc_col_relations_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
nc_col_rollup_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
nc_col_select_options_v2 only fk_column_id is available - join with nc_columns_v2 on id to get base_id
*/
// Migrate base_id
const migrateBaseId = [
MetaTable.CALENDAR_VIEW_RANGE,
MetaTable.COL_BARCODE,
MetaTable.COL_FORMULA,
MetaTable.COL_LOOKUP,
MetaTable.COL_QRCODE,
MetaTable.COL_RELATIONS,
MetaTable.COL_ROLLUP,
MetaTable.COL_SELECT_OPTIONS,
];
for (const table of migrateBaseId) {
hrTime = process.hrtime();
if (table === MetaTable.CALENDAR_VIEW_RANGE) {
await migrateDataWithJoin(
knex,
MetaTable.CALENDAR_VIEW_RANGE,
'fk_view_id',
MetaTable.VIEWS,
'id',
'base_id',
'base_id',
);
} else {
await migrateDataWithJoin(
knex,
table,
'fk_column_id',
MetaTable.COLUMNS,
'id',
'base_id',
'base_id',
);
}
logExecutionTime(`Migrated base_id for ${table}`);
}
// Drop existing base_id indexes
const dropBaseIdIndexes = [
MetaTable.AUDIT,
MetaTable.BASES,
MetaTable.MODELS,
MetaTable.PROJECT_USERS,
MetaTable.SYNC_SOURCE,
MetaTable.EXTENSIONS,
];
log('Dropping existing base_id indexes');
hrTime = process.hrtime();
for (const table of dropBaseIdIndexes) {
const indexes: string[] = await listIndexesOnColumn(knex, table, 'base_id');
for (const index of indexes) {
log(`Dropping index ${index} on ${table}.base_id`);
await knex.schema.alterTable(table, (table) => {
table.dropIndex('base_id', index);
});
}
}
logExecutionTime('Dropped existing base_id indexes');
// Recreate existing source_id indexes as name might clash with base_id (old name for source_id)
const recreateSourceIdIndexes = [MetaTable.MODELS, MetaTable.SYNC_SOURCE];
log('Recreating existing source_id indexes');
hrTime = process.hrtime();
for (const tbl of recreateSourceIdIndexes) {
const indexes: string[] = await listIndexesOnColumn(knex, tbl, 'source_id');
// remove duplicate indexes
const uniqueIndexes = Array.from(new Set(indexes));
for (const index of uniqueIndexes) {
// Recreate only if index name will clash with base_id
if (index !== `${tbl}_base_id_index`) {
continue;
}
log(`Recreating index ${index} on ${tbl}.source_id`);
await knex.schema.alterTable(tbl, (table) => {
table.dropIndex('source_id', `${tbl}_base_id_index`);
table.index('source_id');
});
}
}
logExecutionTime('Recreated existing source_id indexes');
// Add indexes
const addIndexes = [
// MetaTable.API_TOKENS,
MetaTable.AUDIT,
MetaTable.PROJECT_USERS,
MetaTable.CALENDAR_VIEW_COLUMNS,
MetaTable.CALENDAR_VIEW,
MetaTable.COLUMNS,
MetaTable.EXTENSIONS,
MetaTable.FILTER_EXP,
MetaTable.FORM_VIEW_COLUMNS,
MetaTable.FORM_VIEW,
MetaTable.GALLERY_VIEW_COLUMNS,
MetaTable.GALLERY_VIEW,
MetaTable.GRID_VIEW_COLUMNS,
MetaTable.GRID_VIEW,
MetaTable.HOOK_LOGS,
MetaTable.HOOKS,
MetaTable.KANBAN_VIEW_COLUMNS,
MetaTable.KANBAN_VIEW,
MetaTable.MAP_VIEW_COLUMNS,
MetaTable.MAP_VIEW,
MetaTable.MODELS,
MetaTable.SORT,
MetaTable.BASES,
MetaTable.SYNC_LOGS,
MetaTable.SYNC_SOURCE,
MetaTable.VIEWS,
MetaTable.MODEL_ROLE_VISIBILITY,
MetaTable.COMMENTS,
MetaTable.COMMENTS_REACTIONS,
MetaTable.USER_COMMENTS_NOTIFICATIONS_PREFERENCE,
MetaTable.CALENDAR_VIEW_RANGE,
MetaTable.COL_BARCODE,
MetaTable.COL_FORMULA,
MetaTable.COL_LOOKUP,
MetaTable.COL_QRCODE,
MetaTable.COL_RELATIONS,
MetaTable.COL_ROLLUP,
MetaTable.COL_SELECT_OPTIONS,
];
log('Adding indexes');
for (const tbl of addIndexes) {
hrTime = process.hrtime();
await knex.schema.alterTable(tbl, (table) => {
table.index('base_id');
});
logExecutionTime(`Added indexes for ${tbl}`);
}
log('Migration completed');
};
const down = async (knex: Knex) => {
// Drop base_id
await knex.schema.alterTable(MetaTable.CALENDAR_VIEW_RANGE, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_BARCODE, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_FORMULA, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_LOOKUP, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_QRCODE, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_RELATIONS, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_ROLLUP, (table) => {
table.dropColumn('base_id');
});
await knex.schema.alterTable(MetaTable.COL_SELECT_OPTIONS, (table) => {
table.dropColumn('base_id');
});
};
export { up, down };

160
packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts

@ -27,6 +27,7 @@ import {
} from '~/models';
import rolePermissions from '~/utils/acl';
import { NcError } from '~/helpers/catchError';
import { RootScopes } from '~/utils/globals';
export const rolesLabel = {
[OrgUserRoles.SUPER_ADMIN]: 'Super Admin',
@ -59,12 +60,16 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
async use(req, res, next): Promise<any> {
const { params } = req;
const context = {
workspace_id: RootScopes.BYPASS,
base_id: RootScopes.BYPASS,
};
// extract base id based on request path params
if (params.baseName) {
const base = await Base.getByTitleOrId(params.baseName);
const base = await Base.getByTitleOrId(context, params.baseName);
if (base) {
req.ncBaseId = base.id;
res.locals.base = base;
}
}
if (params.baseId) {
@ -72,13 +77,24 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
} else if (params.dashboardId) {
req.ncBaseId = params.dashboardId;
} else if (params.tableId || params.modelId) {
const model = await Model.getByIdOrName({
const model = await Model.getByIdOrName(context, {
id: params.tableId || params.modelId,
});
if (!model) {
NcError.tableNotFound(params.tableId || params.modelId);
}
req.ncBaseId = model?.base_id;
} else if (params.viewId) {
const view =
(await View.get(params.viewId)) || (await Model.get(params.viewId));
(await View.get(context, params.viewId)) ||
(await Model.get(context, params.viewId));
if (!view) {
NcError.viewNotFound(params.viewId);
}
req.ncBaseId = view?.base_id;
} else if (
params.formViewId ||
@ -88,47 +104,137 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
params.calendarViewId
) {
const view = await View.get(
context,
params.formViewId ||
params.gridViewId ||
params.kanbanViewId ||
params.galleryViewId ||
params.calendarViewId,
);
if (!view) {
NcError.viewNotFound(
params.formViewId ||
params.gridViewId ||
params.kanbanViewId ||
params.galleryViewId ||
params.calendarViewId,
);
}
req.ncBaseId = view?.base_id;
} else if (params.publicDataUuid) {
const view = await View.getByUUID(req.params.publicDataUuid);
const view = await View.getByUUID(context, req.params.publicDataUuid);
if (!view) {
NcError.viewNotFound(params.publicDataUuid);
}
req.ncBaseId = view?.base_id;
} else if (params.sharedViewUuid) {
const view = await View.getByUUID(context, req.params.sharedViewUuid);
if (!view) {
NcError.viewNotFound(req.params.sharedViewUuid);
}
req.ncBaseId = view?.base_id;
} else if (params.sharedBaseUuid) {
const base = await Base.getByUuid(context, req.params.sharedBaseUuid);
if (!base) {
NcError.baseNotFound(req.params.sharedBaseUuid);
}
req.ncBaseId = base?.id;
} else if (params.hookId) {
const hook = await Hook.get(params.hookId);
const hook = await Hook.get(context, params.hookId);
if (!hook) {
NcError.genericNotFound('Webhook', params.hookId);
}
req.ncBaseId = hook?.base_id;
} else if (params.gridViewColumnId) {
const gridViewColumn = await GridViewColumn.get(params.gridViewColumnId);
const gridViewColumn = await GridViewColumn.get(
context,
params.gridViewColumnId,
);
if (!gridViewColumn) {
NcError.fieldNotFound(params.gridViewColumnId);
}
req.ncBaseId = gridViewColumn?.base_id;
} else if (params.formViewColumnId) {
const formViewColumn = await FormViewColumn.get(params.formViewColumnId);
const formViewColumn = await FormViewColumn.get(
context,
params.formViewColumnId,
);
if (!formViewColumn) {
NcError.fieldNotFound(params.formViewColumnId);
}
req.ncBaseId = formViewColumn?.base_id;
} else if (params.galleryViewColumnId) {
const galleryViewColumn = await GalleryViewColumn.get(
context,
params.galleryViewColumnId,
);
if (!galleryViewColumn) {
NcError.fieldNotFound(params.galleryViewColumnId);
}
req.ncBaseId = galleryViewColumn?.base_id;
} else if (params.columnId) {
const column = await Column.get({ colId: params.columnId });
const column = await Column.get(context, { colId: params.columnId });
if (!column) {
NcError.fieldNotFound(params.columnId);
}
req.ncBaseId = column?.base_id;
} else if (params.filterId) {
const filter = await Filter.get(params.filterId);
const filter = await Filter.get(context, params.filterId);
if (!filter) {
NcError.genericNotFound('Filter', params.filterId);
}
req.ncBaseId = filter?.base_id;
} else if (params.filterParentId) {
const filter = await Filter.get(params.filterParentId);
const filter = await Filter.get(context, params.filterParentId);
if (!filter) {
NcError.genericNotFound('Filter', params.filterParentId);
}
req.ncBaseId = filter?.base_id;
} else if (params.sortId) {
const sort = await Sort.get(params.sortId);
const sort = await Sort.get(context, params.sortId);
if (!sort) {
NcError.genericNotFound('Sort', params.sortId);
}
req.ncBaseId = sort?.base_id;
} else if (params.syncId) {
const syncSource = await SyncSource.get(req.params.syncId);
const syncSource = await SyncSource.get(context, req.params.syncId);
if (!syncSource) {
NcError.genericNotFound('Sync Source', params.syncId);
}
req.ncBaseId = syncSource.base_id;
} else if (params.extensionId) {
const extension = await Extension.get(req.params.extensionId);
const extension = await Extension.get(context, req.params.extensionId);
if (!extension) {
NcError.genericNotFound('Extension', params.extensionId);
}
req.ncBaseId = extension.base_id;
}
// extract fk_model_id from query params only if it's audit post endpoint
@ -144,9 +250,14 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
req.method === 'POST' &&
req.body?.fk_model_id
) {
const model = await Model.getByIdOrName({
const model = await Model.getByIdOrName(context, {
id: req.body.fk_model_id,
});
if (!model) {
NcError.tableNotFound(req.body.fk_model_id);
}
req.ncBaseId = model?.base_id;
}
// extract fk_model_id from query params only if it's audit get endpoint
@ -162,9 +273,14 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
req.method === 'GET' &&
req.query.fk_model_id
) {
const model = await Model.getByIdOrName({
const model = await Model.getByIdOrName(context, {
id: req.query?.fk_model_id,
});
if (!model) {
NcError.tableNotFound(req.query?.fk_model_id);
}
req.ncBaseId = model?.base_id;
} else if (
[
@ -174,7 +290,12 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
(req.method === 'PATCH' || req.method === 'DELETE') &&
req.params.commentId
) {
const comment = await Comment.get(params.commentId);
const comment = await Comment.get(context, params.commentId);
if (!comment) {
NcError.genericNotFound('Comment', params.commentId);
}
req.ncBaseId = comment?.base_id;
}
// extract base id from query params only if it's userMe endpoint or webhook plugin list
@ -191,6 +312,11 @@ export class ExtractIdsMiddleware implements NestMiddleware, CanActivate {
req.ncBaseId = req.query.base_id;
}
req.context = {
workspace_id: null,
base_id: req.ncBaseId,
};
next();
}

44
packages/nocodb/src/models/ApiToken.ts

@ -5,13 +5,14 @@ import {
CacheGetType,
CacheScope,
MetaTable,
RootScopes,
} from '~/utils/globals';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
export default class ApiToken implements ApiTokenType {
fk_workspace_id?: string;
base_id?: string;
db_alias?: string;
fk_user_id?: string;
description?: string;
permissions?: string;
@ -28,11 +29,17 @@ export default class ApiToken implements ApiTokenType {
ncMeta = Noco.ncMeta,
) {
const token = nanoid(40);
await ncMeta.metaInsert(null, null, MetaTable.API_TOKENS, {
description: apiToken.description,
token,
fk_user_id: apiToken.fk_user_id,
});
await ncMeta.metaInsert2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.API_TOKENS,
{
description: apiToken.description,
token,
fk_user_id: apiToken.fk_user_id,
},
true,
);
return this.getByToken(token).then(async (apiToken) => {
await NocoCache.appendToList(
CacheScope.API_TOKEN,
@ -46,9 +53,14 @@ export default class ApiToken implements ApiTokenType {
static async list(userId: string, ncMeta = Noco.ncMeta) {
// let tokens = await NocoCache.getList(CacheScope.API_TOKEN, []);
// if (!tokens.length) {
const tokens = await ncMeta.metaList(null, null, MetaTable.API_TOKENS, {
condition: { fk_user_id: userId },
});
const tokens = await ncMeta.metaList2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.API_TOKENS,
{
condition: { fk_user_id: userId },
},
);
// await NocoCache.setList(CacheScope.API_TOKEN, [], tokens);
// }
return tokens?.map((t) => new ApiToken(t));
@ -59,7 +71,12 @@ export default class ApiToken implements ApiTokenType {
`${CacheScope.API_TOKEN}:${token}`,
CacheDelDirection.CHILD_TO_PARENT,
);
return await ncMeta.metaDelete(null, null, MetaTable.API_TOKENS, { token });
return await ncMeta.metaDelete(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.API_TOKENS,
{ token },
);
}
static async getByToken(token, ncMeta = Noco.ncMeta) {
@ -70,7 +87,12 @@ export default class ApiToken implements ApiTokenType {
CacheGetType.TYPE_OBJECT,
));
if (!data) {
data = await ncMeta.metaGet(null, null, MetaTable.API_TOKENS, { token });
data = await ncMeta.metaGet(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.API_TOKENS,
{ token },
);
await NocoCache.set(`${CacheScope.API_TOKEN}:${token}`, data);
}
return data && new ApiToken(data);

72
packages/nocodb/src/models/Audit.ts

@ -1,9 +1,8 @@
import { AuditOperationTypes } from 'nocodb-sdk';
import type { AuditType } from 'nocodb-sdk';
import Model from '~/models/Model';
import Noco from '~/Noco';
import { extractProps } from '~/helpers/extractProps';
import { MetaTable } from '~/utils/globals';
import { MetaTable, RootScopes } from '~/utils/globals';
const opTypes = <const>[
'COMMENT',
@ -51,6 +50,7 @@ export default class Audit implements AuditType {
user?: string;
ip?: string;
source_id?: string;
fk_workspace_id?: string;
base_id?: string;
fk_model_id?: string;
row_id?: string;
@ -66,8 +66,8 @@ export default class Audit implements AuditType {
public static async get(auditId: string) {
const audit = await Noco.ncMeta.metaGet2(
null,
null,
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.AUDIT,
auditId,
);
@ -90,6 +90,7 @@ export default class Audit implements AuditType {
'user',
'ip',
'source_id',
'fk_workspace_id',
'base_id',
'row_id',
'fk_model_id',
@ -99,20 +100,13 @@ export default class Audit implements AuditType {
'description',
'details',
]);
if (
(!insertObj.base_id || !insertObj.source_id) &&
insertObj.fk_model_id
) {
const model = await Model.getByIdOrName(
{ id: insertObj.fk_model_id },
ncMeta,
);
insertObj.base_id = model.base_id;
insertObj.source_id = model.source_id;
}
return await ncMeta.metaInsert2(null, null, MetaTable.AUDIT, insertObj);
return await ncMeta.metaInsert2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.AUDIT,
insertObj,
);
};
if (forceAwait) {
@ -153,17 +147,22 @@ export default class Audit implements AuditType {
sourceId?: string;
},
) {
return await Noco.ncMeta.metaList2(null, null, MetaTable.AUDIT, {
condition: {
base_id: baseId,
...(sourceId ? { source_id: sourceId } : {}),
},
orderBy: {
created_at: 'desc',
return await Noco.ncMeta.metaList2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.AUDIT,
{
condition: {
base_id: baseId,
...(sourceId ? { source_id: sourceId } : {}),
},
orderBy: {
created_at: 'desc',
},
limit,
offset,
},
limit,
offset,
});
);
}
static async baseAuditCount(
@ -183,14 +182,19 @@ export default class Audit implements AuditType {
}
static async sourceAuditList(sourceId: string, { limit = 25, offset = 0 }) {
return await Noco.ncMeta.metaList2(null, null, MetaTable.AUDIT, {
condition: { source_id: sourceId },
orderBy: {
created_at: 'desc',
return await Noco.ncMeta.metaList2(
RootScopes.ROOT,
RootScopes.ROOT,
MetaTable.AUDIT,
{
condition: { source_id: sourceId },
orderBy: {
created_at: 'desc',
},
limit,
offset,
},
limit,
offset,
});
);
}
static async sourceAuditCount(sourceId: string) {

49
packages/nocodb/src/models/BarcodeColumn.ts

@ -1,11 +1,15 @@
import type { NcContext } from '~/interface/config';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
import { extractProps } from '~/helpers/extractProps';
import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals';
import { Column } from '~/models/index';
import { NcError } from '~/helpers/catchError';
export default class BarcodeColumn {
id: string;
fk_workspace_id?: string;
fk_base_id?: string;
fk_column_id: string;
fk_barcode_value_column_id: string;
barcode_format: string;
@ -15,6 +19,7 @@ export default class BarcodeColumn {
}
public static async insert(
context: NcContext,
barcodeColumn: Partial<BarcodeColumn>,
ncMeta = Noco.ncMeta,
) {
@ -23,12 +28,34 @@ export default class BarcodeColumn {
'fk_barcode_value_column_id',
'barcode_format',
]);
await ncMeta.metaInsert2(null, null, MetaTable.COL_BARCODE, insertObj);
return this.read(barcodeColumn.fk_column_id, ncMeta);
const column = await Column.get(
context,
{
colId: insertObj.fk_column_id,
},
ncMeta,
);
if (!column) {
NcError.fieldNotFound(insertObj.fk_column_id);
}
await ncMeta.metaInsert2(
context.workspace_id,
context.base_id,
MetaTable.COL_BARCODE,
insertObj,
);
return this.read(context, barcodeColumn.fk_column_id, ncMeta);
}
public static async read(columnId: string, ncMeta = Noco.ncMeta) {
public static async read(
context: NcContext,
columnId: string,
ncMeta = Noco.ncMeta,
) {
let column =
columnId &&
(await NocoCache.get(
@ -37,8 +64,8 @@ export default class BarcodeColumn {
));
if (!column) {
column = await ncMeta.metaGet2(
null, //,
null, //model.db_alias,
context.workspace_id,
context.base_id,
MetaTable.COL_BARCODE,
{ fk_column_id: columnId },
);
@ -48,9 +75,13 @@ export default class BarcodeColumn {
return column ? new BarcodeColumn(column) : null;
}
async getValueColumn() {
return Column.get({
colId: this.fk_barcode_value_column_id,
});
async getValueColumn(context: NcContext, ncMeta = Noco.ncMeta) {
return Column.get(
context,
{
colId: this.fk_barcode_value_column_id,
},
ncMeta,
);
}
}

232
packages/nocodb/src/models/Base.ts

@ -1,5 +1,6 @@
import type { BaseType, BoolType, MetaType } from 'nocodb-sdk';
import type { DB_TYPES } from '~/utils/globals';
import type { NcContext } from '~/interface/config';
import { BaseUser, Source } from '~/models';
import Noco from '~/Noco';
import {
@ -7,6 +8,7 @@ import {
CacheGetType,
CacheScope,
MetaTable,
RootScopes,
} from '~/utils/globals';
import { extractProps } from '~/helpers/extractProps';
import NocoCache from '~/cache/NocoCache';
@ -15,6 +17,7 @@ import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2';
export default class Base implements BaseType {
public id: string;
public fk_workspace_id?: string;
public title: string;
public prefix: string;
public status: string;
@ -67,14 +70,20 @@ export default class Base implements BaseType {
}
const { id: baseId } = await ncMeta.metaInsert2(
null,
null,
RootScopes.BASE,
RootScopes.BASE,
MetaTable.PROJECT,
insertObj,
);
const context = {
workspace_id: (base as any).fk_workspace_id,
base_id: baseId,
};
for (const source of base.sources) {
await Source.createBase(
context,
{
type: source.config?.client as (typeof DB_TYPES)[number],
...source,
@ -85,7 +94,7 @@ export default class Base implements BaseType {
}
await NocoCache.del(CacheScope.INSTANCE_META);
return this.getWithInfo(baseId, ncMeta).then(async (base) => {
return this.getWithInfo(context, baseId, ncMeta).then(async (base) => {
await NocoCache.appendToList(
CacheScope.PROJECT,
[],
@ -105,25 +114,30 @@ export default class Base implements BaseType {
let { list: baseList } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !baseList.length) {
baseList = await ncMeta.metaList2(null, null, MetaTable.PROJECT, {
xcCondition: {
_or: [
{
deleted: {
eq: false,
baseList = await ncMeta.metaList2(
RootScopes.BASE,
RootScopes.BASE,
MetaTable.PROJECT,
{
xcCondition: {
_or: [
{
deleted: {
eq: false,
},
},
},
{
deleted: {
eq: null,
{
deleted: {
eq: null,
},
},
},
],
],
},
orderBy: {
order: 'asc',
},
},
orderBy: {
order: 'asc',
},
});
);
await NocoCache.setList(CacheScope.PROJECT, [], baseList);
}
@ -150,7 +164,11 @@ export default class Base implements BaseType {
}
// @ts-ignore
static async get(baseId: string, ncMeta = Noco.ncMeta): Promise<Base> {
static async get(
context: NcContext,
baseId: string,
ncMeta = Noco.ncMeta,
): Promise<Base> {
let baseData =
baseId &&
(await NocoCache.get(
@ -158,10 +176,15 @@ export default class Base implements BaseType {
CacheGetType.TYPE_OBJECT,
));
if (!baseData) {
baseData = await ncMeta.metaGet2(null, null, MetaTable.PROJECT, {
id: baseId,
deleted: false,
});
baseData = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
{
id: baseId,
deleted: false,
},
);
if (baseData) {
baseData.meta = parseMetaProp(baseData);
await NocoCache.set(`${CacheScope.PROJECT}:${baseId}`, baseData);
@ -175,12 +198,17 @@ export default class Base implements BaseType {
}
async getSources(ncMeta = Noco.ncMeta): Promise<Source[]> {
return (this.sources = await Source.list({ baseId: this.id }, ncMeta));
return (this.sources = await Source.list(
{ workspace_id: this.fk_workspace_id, base_id: this.id },
{ baseId: this.id },
ncMeta,
));
}
// todo: hide credentials
// @ts-ignore
static async getWithInfo(
context: NcContext,
baseId: string,
ncMeta = Noco.ncMeta,
): Promise<Base> {
@ -192,10 +220,15 @@ export default class Base implements BaseType {
));
if (!baseData) {
baseData = await ncMeta.metaGet2(null, null, MetaTable.PROJECT, {
id: baseId,
deleted: false,
});
baseData = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
{
id: baseId,
deleted: false,
},
);
if (baseData) {
baseData.meta = parseMetaProp(baseData);
await NocoCache.set(`${CacheScope.PROJECT}:${baseId}`, baseData);
@ -222,8 +255,12 @@ export default class Base implements BaseType {
}
// @ts-ignore
static async softDelete(baseId: string, ncMeta = Noco.ncMeta): Promise<any> {
await this.clearConnectionPool(baseId, ncMeta);
static async softDelete(
context: NcContext,
baseId: string,
ncMeta = Noco.ncMeta,
): Promise<any> {
await this.clearConnectionPool(context, baseId, ncMeta);
// get existing cache
const key = `${CacheScope.PROJECT}:${baseId}`;
@ -250,8 +287,8 @@ export default class Base implements BaseType {
// set meta
return await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
{ deleted: true },
baseId,
@ -260,6 +297,7 @@ export default class Base implements BaseType {
// @ts-ignore
static async update(
context: NcContext,
baseId: string,
base: Partial<Base>,
ncMeta = Noco.ncMeta,
@ -318,8 +356,8 @@ export default class Base implements BaseType {
// set meta
return await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
updateObj,
baseId,
@ -327,21 +365,29 @@ export default class Base implements BaseType {
}
// Todo: Remove the base entry from the connection pool in NcConnectionMgrv2
static async delete(baseId, ncMeta = Noco.ncMeta): Promise<any> {
let base = await this.get(baseId);
const users = await BaseUser.getUsersList({
base_id: baseId,
});
static async delete(
context: NcContext,
baseId,
ncMeta = Noco.ncMeta,
): Promise<any> {
let base = await this.get(context, baseId, ncMeta);
const users = await BaseUser.getUsersList(
context,
{
base_id: baseId,
},
ncMeta,
);
for (const user of users) {
await BaseUser.delete(baseId, user.id);
await BaseUser.delete(context, baseId, user.id, ncMeta);
}
const sources = await Source.list({ baseId });
const sources = await Source.list(context, { baseId }, ncMeta);
for (const source of sources) {
await source.delete(ncMeta);
await source.delete(context, ncMeta);
}
base = await this.get(baseId);
base = await this.get(context, baseId, ncMeta);
if (base) {
// delete <scope>:<uuid>
@ -360,14 +406,24 @@ export default class Base implements BaseType {
CacheDelDirection.CHILD_TO_PARENT,
);
await ncMeta.metaDelete(null, null, MetaTable.AUDIT, {
base_id: baseId,
});
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.AUDIT,
{
base_id: baseId,
},
);
return await ncMeta.metaDelete(null, null, MetaTable.PROJECT, baseId);
return await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
baseId,
);
}
static async getByUuid(uuid, ncMeta = Noco.ncMeta) {
static async getByUuid(context: NcContext, uuid, ncMeta = Noco.ncMeta) {
const baseId =
uuid &&
(await NocoCache.get(
@ -376,9 +432,14 @@ export default class Base implements BaseType {
));
let baseData = null;
if (!baseId) {
baseData = await Noco.ncMeta.metaGet2(null, null, MetaTable.PROJECT, {
uuid,
});
baseData = await Noco.ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
{
uuid,
},
);
if (baseData) {
baseData.meta = parseMetaProp(baseData);
await NocoCache.set(
@ -387,13 +448,17 @@ export default class Base implements BaseType {
);
}
} else {
return this.get(baseId);
return this.get(context, baseId, ncMeta);
}
return baseData?.id && this.get(baseData?.id, ncMeta);
return baseData?.id && this.get(context, baseData?.id, ncMeta);
}
static async getWithInfoByTitle(title: string, ncMeta = Noco.ncMeta) {
const base = await this.getByTitle(title, ncMeta);
static async getWithInfoByTitle(
context: NcContext,
title: string,
ncMeta = Noco.ncMeta,
) {
const base = await this.getByTitle(context, title, ncMeta);
if (base) {
await base.getSources(ncMeta);
}
@ -401,7 +466,11 @@ export default class Base implements BaseType {
return base;
}
static async getByTitle(title: string, ncMeta = Noco.ncMeta) {
static async getByTitle(
context: NcContext,
title: string,
ncMeta = Noco.ncMeta,
) {
const baseId =
title &&
(await NocoCache.get(
@ -410,10 +479,15 @@ export default class Base implements BaseType {
));
let baseData = null;
if (!baseId) {
baseData = await Noco.ncMeta.metaGet2(null, null, MetaTable.PROJECT, {
title,
deleted: false,
});
baseData = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
{
title,
deleted: false,
},
);
if (baseData) {
baseData.meta = parseMetaProp(baseData);
await NocoCache.set(
@ -422,12 +496,16 @@ export default class Base implements BaseType {
);
}
} else {
return this.get(baseId);
return this.get(context, baseId, ncMeta);
}
return baseData?.id && this.get(baseData?.id, ncMeta);
return baseData?.id && this.get(context, baseData?.id, ncMeta);
}
static async getByTitleOrId(titleOrId: string, ncMeta = Noco.ncMeta) {
static async getByTitleOrId(
context: NcContext,
titleOrId: string,
ncMeta = Noco.ncMeta,
) {
const baseId =
titleOrId &&
(await NocoCache.get(
@ -436,9 +514,9 @@ export default class Base implements BaseType {
));
let baseData = null;
if (!baseId) {
baseData = await Noco.ncMeta.metaGet2(
null,
null,
baseData = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.PROJECT,
{
deleted: false,
@ -470,13 +548,17 @@ export default class Base implements BaseType {
);
}
} else {
return this.get(baseId);
return this.get(context, baseId, ncMeta);
}
return baseData?.id && this.get(baseData?.id, ncMeta);
return baseData?.id && this.get(context, baseData?.id, ncMeta);
}
static async getWithInfoByTitleOrId(titleOrId: string, ncMeta = Noco.ncMeta) {
const base = await this.getByTitleOrId(titleOrId, ncMeta);
static async getWithInfoByTitleOrId(
context: NcContext,
titleOrId: string,
ncMeta = Noco.ncMeta,
) {
const base = await this.getByTitleOrId(context, titleOrId, ncMeta);
// parse meta
base.meta = parseMetaProp(base);
@ -488,8 +570,12 @@ export default class Base implements BaseType {
return base;
}
static async clearConnectionPool(baseId: string, ncMeta = Noco.ncMeta) {
const base = await this.get(baseId, ncMeta);
static async clearConnectionPool(
context: NcContext,
baseId: string,
ncMeta = Noco.ncMeta,
) {
const base = await this.get(context, baseId, ncMeta);
if (base) {
const sources = await base.getSources(ncMeta);
for (const source of sources) {

84
packages/nocodb/src/models/BaseUser.ts

@ -1,12 +1,14 @@
import { ProjectRoles } from 'nocodb-sdk';
import type { BaseType } from 'nocodb-sdk';
import type User from '~/models/User';
import type { NcContext } from '~/interface/config';
import Base from '~/models/Base';
import {
CacheDelDirection,
CacheGetType,
CacheScope,
MetaTable,
RootScopes,
} from '~/utils/globals';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
@ -14,6 +16,7 @@ import { extractProps } from '~/helpers/extractProps';
import { parseMetaProp } from '~/utils/modelUtils';
export default class BaseUser {
fk_workspace_id?: string;
base_id: string;
fk_user_id: string;
roles?: string;
@ -27,6 +30,7 @@ export default class BaseUser {
}
public static async bulkInsert(
context: NcContext,
baseUsers: Partial<BaseUser>[],
ncMeta = Noco.ncMeta,
) {
@ -34,9 +38,13 @@ export default class BaseUser {
extractProps(baseUser, ['fk_user_id', 'base_id', 'roles']),
);
if (!insertObj.length) {
return;
}
const bulkData = await ncMeta.bulkMetaInsert(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.PROJECT_USERS,
insertObj,
true,
@ -68,6 +76,7 @@ export default class BaseUser {
}
public static async insert(
context: NcContext,
baseUser: Partial<BaseUser>,
ncMeta = Noco.ncMeta,
) {
@ -78,14 +87,14 @@ export default class BaseUser {
]);
const { base_id, fk_user_id } = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.PROJECT_USERS,
insertObj,
true,
);
const res = await this.get(base_id, fk_user_id, ncMeta);
const res = await this.get(context, base_id, fk_user_id, ncMeta);
await NocoCache.appendToList(
CacheScope.BASE_USER,
@ -97,9 +106,14 @@ export default class BaseUser {
}
// public static async update(id, user: Partial<BaseUser>, ncMeta = Noco.ncMeta) {
// // return await ncMeta.metaUpdate(null, null, MetaTable.USERS, id, insertObj);
// // return await ncMeta.metaUpdate(context.workspace_id, context.base_id, insertObj);
// }
static async get(baseId: string, userId: string, ncMeta = Noco.ncMeta) {
static async get(
context: NcContext,
baseId: string,
userId: string,
ncMeta = Noco.ncMeta,
) {
let baseUser =
baseId &&
userId &&
@ -148,6 +162,7 @@ export default class BaseUser {
}
public static async getUsersList(
context: NcContext,
{
base_id,
mode = 'full',
@ -226,6 +241,7 @@ export default class BaseUser {
}
public static async getUsersCount(
context: NcContext,
{
base_id,
query,
@ -257,6 +273,7 @@ export default class BaseUser {
}
static async updateRoles(
context: NcContext,
baseId,
userId,
roles: string,
@ -264,8 +281,8 @@ export default class BaseUser {
) {
// set meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.PROJECT_USERS,
{
roles,
@ -284,6 +301,7 @@ export default class BaseUser {
}
static async update(
context: NcContext,
baseId,
userId,
baseUser: Partial<BaseUser>,
@ -292,24 +310,35 @@ export default class BaseUser {
const updateObj = extractProps(baseUser, ['starred', 'hidden', 'order']);
// set meta
await ncMeta.metaUpdate(null, null, MetaTable.PROJECT_USERS, updateObj, {
fk_user_id: userId,
base_id: baseId,
});
await ncMeta.metaUpdate(
context.workspace_id,
context.base_id,
MetaTable.PROJECT_USERS,
updateObj,
{
fk_user_id: userId,
base_id: baseId,
},
);
await NocoCache.update(
`${CacheScope.BASE_USER}:${baseId}:${userId}`,
updateObj,
);
return await this.get(baseId, userId, ncMeta);
return await this.get(context, baseId, userId, ncMeta);
}
static async delete(baseId: string, userId: string, ncMeta = Noco.ncMeta) {
static async delete(
context: NcContext,
baseId: string,
userId: string,
ncMeta = Noco.ncMeta,
) {
// delete meta
const response = await ncMeta.metaDelete(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.PROJECT_USERS,
{
fk_user_id: userId,
@ -330,9 +359,14 @@ export default class BaseUser {
userId: string,
ncMeta = Noco.ncMeta,
): Promise<BaseUser[]> {
return await ncMeta.metaList2(null, null, MetaTable.PROJECT_USERS, {
condition: { fk_user_id: userId },
});
return await ncMeta.metaList2(
RootScopes.BASE,
RootScopes.BASE,
MetaTable.PROJECT_USERS,
{
condition: { fk_user_id: userId },
},
);
}
static async getProjectsList(
@ -434,17 +468,21 @@ export default class BaseUser {
}
static async updateOrInsert(
context: NcContext,
baseId,
userId,
baseUser: Partial<BaseUser>,
ncMeta = Noco.ncMeta,
) {
const existingProjectUser = await this.get(baseId, userId, ncMeta);
const existingProjectUser = await this.get(context, baseId, userId, ncMeta);
if (existingProjectUser) {
return await this.update(baseId, userId, baseUser, ncMeta);
return await this.update(context, baseId, userId, baseUser, ncMeta);
} else {
return await this.insert({ base_id: baseId, fk_user_id: userId });
return await this.insert(context, {
base_id: baseId,
fk_user_id: userId,
});
}
}
}

58
packages/nocodb/src/models/CalendarRange.ts

@ -1,4 +1,5 @@
import type { CalendarRangeType } from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
import { extractProps } from '~/helpers/extractProps';
@ -7,6 +8,8 @@ import { CacheDelDirection, CacheScope, MetaTable } from '~/utils/globals';
export default class CalendarRange implements CalendarRangeType {
id?: string;
fk_from_column_id?: string;
fk_workspace_id?: string;
base_id?: string;
fk_view_id?: string;
constructor(data: Partial<CalendarRange>) {
@ -14,23 +17,27 @@ export default class CalendarRange implements CalendarRangeType {
}
public static async bulkInsert(
context: NcContext,
data: Partial<CalendarRange>[],
ncMeta = Noco.ncMeta,
) {
let insertObj = [];
const calRanges: {
fk_from_column_id?: string;
fk_view_id?: string;
}[] = [];
for (const d of data) {
const tempObj = extractProps(d, ['fk_from_column_id', 'fk_view_id']);
insertObj.push(tempObj);
calRanges.push(tempObj);
}
if (!insertObj.length) return false;
if (!calRanges.length) return false;
insertObj = insertObj[0];
const insertObj = calRanges[0];
const insertData = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_RANGE,
insertObj,
);
@ -54,7 +61,11 @@ export default class CalendarRange implements CalendarRangeType {
return true;
}
public static async read(fk_view_id: string, ncMeta = Noco.ncMeta) {
public static async read(
context: NcContext,
fk_view_id: string,
ncMeta = Noco.ncMeta,
) {
const cachedList = await NocoCache.getList(CacheScope.CALENDAR_VIEW_RANGE, [
fk_view_id,
]);
@ -62,8 +73,8 @@ export default class CalendarRange implements CalendarRangeType {
const { isNoneList } = cachedList;
if (!isNoneList && !ranges.length) {
ranges = await ncMeta.metaList2(
null, //,
null, //model.db_alias,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_RANGE,
{ condition: { fk_view_id } },
);
@ -84,12 +95,13 @@ export default class CalendarRange implements CalendarRangeType {
}
public static async find(
context: NcContext,
fk_view_id: string,
ncMeta = Noco.ncMeta,
): Promise<CalendarRange> {
const data = await ncMeta.metaGet2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_RANGE,
{
fk_view_id,
@ -100,22 +112,28 @@ export default class CalendarRange implements CalendarRangeType {
}
public static async IsColumnBeingUsedAsRange(
context: NcContext,
columnId: string,
ncMeta = Noco.ncMeta,
) {
return (
(
await ncMeta.metaList2(null, null, MetaTable.CALENDAR_VIEW_RANGE, {
xcCondition: {
_or: [
{
fk_from_column_id: {
eq: columnId,
await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_RANGE,
{
xcCondition: {
_or: [
{
fk_from_column_id: {
eq: columnId,
},
},
},
],
],
},
},
})
)
).length > 0
);
}

61
packages/nocodb/src/models/CalendarView.ts

@ -1,6 +1,6 @@
import type { BoolType, MetaType } from 'nocodb-sdk';
import type { CalendarType } from 'nocodb-sdk';
import View from '~/models/View';
import type { NcContext } from '~/interface/config';
import { extractProps } from '~/helpers/extractProps';
import { prepareForDb, prepareForResponse } from '~/utils/modelUtils';
import NocoCache from '~/cache/NocoCache';
@ -11,6 +11,7 @@ import CalendarRange from '~/models/CalendarRange';
export default class CalendarView implements CalendarType {
fk_view_id: string;
title: string;
fk_workspace_id?: string;
base_id?: string;
source_id?: string;
meta?: MetaType;
@ -27,7 +28,11 @@ export default class CalendarView implements CalendarType {
Object.assign(this, data);
}
public static async get(viewId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
) {
let view =
viewId &&
(await NocoCache.get(
@ -35,17 +40,22 @@ export default class CalendarView implements CalendarType {
CacheGetType.TYPE_OBJECT,
));
if (view) {
const calendarRange = await CalendarRange.read(viewId, ncMeta);
const calendarRange = await CalendarRange.read(context, viewId, ncMeta);
if (calendarRange) {
view.calendar_range = calendarRange.ranges;
} else {
view.calendar_range = [];
}
} else {
view = await ncMeta.metaGet2(null, null, MetaTable.CALENDAR_VIEW, {
fk_view_id: viewId,
});
const calendarRange = await CalendarRange.read(viewId);
view = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW,
{
fk_view_id: viewId,
},
);
const calendarRange = await CalendarRange.read(context, viewId, ncMeta);
if (view && calendarRange) {
view.calendar_range = calendarRange.ranges;
}
@ -55,7 +65,11 @@ export default class CalendarView implements CalendarType {
return view && new CalendarView(view);
}
static async insert(view: Partial<CalendarView>, ncMeta = Noco.ncMeta) {
static async insert(
context: NcContext,
view: Partial<CalendarView>,
ncMeta = Noco.ncMeta,
) {
const insertObj = {
base_id: view.base_id,
source_id: view.source_id,
@ -63,25 +77,19 @@ export default class CalendarView implements CalendarType {
meta: view.meta,
};
const viewRef = await View.get(view.fk_view_id);
if (!(view.base_id && view.source_id)) {
insertObj.base_id = viewRef.base_id;
insertObj.source_id = viewRef.source_id;
}
await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW,
insertObj,
true,
);
return this.get(view.fk_view_id, ncMeta);
return this.get(context, view.fk_view_id, ncMeta);
}
static async update(
context: NcContext,
calendarId: string,
body: Partial<CalendarView>,
ncMeta = Noco.ncMeta,
@ -89,25 +97,32 @@ export default class CalendarView implements CalendarType {
const updateObj = extractProps(body, ['fk_cover_image_col_id', 'meta']);
if (body.calendar_range) {
await ncMeta.metaDelete(null, null, MetaTable.CALENDAR_VIEW_RANGE, {
fk_view_id: calendarId,
});
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_RANGE,
{
fk_view_id: calendarId,
},
);
// if calendar range is updated, delete cache
await NocoCache.del(`${CacheScope.CALENDAR_VIEW}:${calendarId}`);
await CalendarRange.bulkInsert(
context,
body.calendar_range.map((range) => {
return {
fk_view_id: calendarId,
...range,
};
}),
ncMeta,
);
}
// update meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW,
prepareForDb(updateObj),
{

52
packages/nocodb/src/models/CalendarViewColumn.ts

@ -1,4 +1,5 @@
import type { BoolType, MetaType } from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import View from '~/models/View';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
@ -8,9 +9,10 @@ import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals';
export default class CalendarViewColumn {
id?: string;
fk_workspace_id?: string;
base_id?: string;
fk_view_id?: string;
fk_column_id?: string;
base_id?: string;
source_id?: string;
show?: BoolType;
underline?: BoolType;
@ -23,7 +25,11 @@ export default class CalendarViewColumn {
Object.assign(this, data);
}
public static async get(calendarViewColumnId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
calendarViewColumnId: string,
ncMeta = Noco.ncMeta,
) {
let viewColumn =
calendarViewColumnId &&
(await NocoCache.get(
@ -32,8 +38,8 @@ export default class CalendarViewColumn {
));
if (!viewColumn) {
viewColumn = await ncMeta.metaGet2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_COLUMNS,
calendarViewColumnId,
);
@ -52,6 +58,7 @@ export default class CalendarViewColumn {
}
static async insert(
context: NcContext,
column: Partial<CalendarViewColumn>,
ncMeta = Noco.ncMeta,
) {
@ -73,25 +80,24 @@ export default class CalendarViewColumn {
},
);
if (!(insertObj.base_id && insertObj.source_id)) {
const viewRef = await View.get(insertObj.fk_view_id, ncMeta);
insertObj.base_id = viewRef.base_id;
insertObj.source_id = viewRef.source_id;
}
const { id } = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_COLUMNS,
insertObj,
);
{
const view = await View.get(column.fk_view_id, ncMeta);
await View.clearSingleQueryCache(view.fk_model_id, [view], ncMeta);
const view = await View.get(context, column.fk_view_id, ncMeta);
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[view],
ncMeta,
);
}
return this.get(id, ncMeta).then(async (viewColumn) => {
return this.get(context, id, ncMeta).then(async (viewColumn) => {
await NocoCache.appendToList(
CacheScope.CALENDAR_VIEW_COLUMN,
[column.fk_view_id],
@ -102,6 +108,7 @@ export default class CalendarViewColumn {
}
public static async list(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
): Promise<CalendarViewColumn[]> {
@ -113,8 +120,8 @@ export default class CalendarViewColumn {
const { isNoneList } = cachedList;
if (!isNoneList && !viewColumns.length) {
viewColumns = await ncMeta.metaList2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_COLUMNS,
{
condition: {
@ -145,6 +152,7 @@ export default class CalendarViewColumn {
}
static async update(
context: NcContext,
columnId: string,
body: Partial<CalendarViewColumn>,
ncMeta = Noco.ncMeta,
@ -159,8 +167,8 @@ export default class CalendarViewColumn {
// update meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.CALENDAR_VIEW_COLUMNS,
updateObj,
columnId,
@ -173,9 +181,9 @@ export default class CalendarViewColumn {
// on view column update, delete any optimised single query cache
{
const viewCol = await this.get(columnId, ncMeta);
const view = await View.get(viewCol.fk_view_id, ncMeta);
await View.clearSingleQueryCache(view.fk_model_id, [view]);
const viewCol = await this.get(context, columnId, ncMeta);
const view = await View.get(context, viewCol.fk_view_id, ncMeta);
await View.clearSingleQueryCache(context, view.fk_model_id, [view]);
}
return res;

462
packages/nocodb/src/models/Column.ts

File diff suppressed because it is too large Load Diff

69
packages/nocodb/src/models/Comment.ts

@ -1,9 +1,11 @@
import type { CommentType } from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import Noco from '~/Noco';
import { MetaTable } from '~/utils/globals';
import { prepareForDb } from '~/utils/modelUtils';
import { extractProps } from '~/helpers/extractProps';
import Model from '~/models/Model';
import { NcError } from '~/helpers/catchError';
export default class Comment implements CommentType {
id?: string;
@ -12,6 +14,7 @@ export default class Comment implements CommentType {
comment?: string;
parent_comment_id?: string;
source_id?: string;
fk_workspace_id?: string;
base_id?: string;
created_by?: string;
resolved_by?: string;
@ -23,10 +26,14 @@ export default class Comment implements CommentType {
Object.assign(this, comment);
}
public static async get(commentId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
commentId: string,
ncMeta = Noco.ncMeta,
) {
const comment = await ncMeta.metaGet2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.COMMENTS,
commentId,
);
@ -35,6 +42,7 @@ export default class Comment implements CommentType {
}
public static async list(
context: NcContext,
{
row_id,
fk_model_id,
@ -57,7 +65,11 @@ export default class Comment implements CommentType {
return commentList.map((comment) => new Comment(comment));
}
public static async insert(comment: Partial<Comment>, ncMeta = Noco.ncMeta) {
public static async insert(
context: NcContext,
comment: Partial<Comment>,
ncMeta = Noco.ncMeta,
) {
const insertObj = extractProps(comment, [
'id',
'fk_model_id',
@ -71,19 +83,20 @@ export default class Comment implements CommentType {
'created_by_email',
]);
if ((!insertObj.base_id || !insertObj.source_id) && insertObj.fk_model_id) {
if (!insertObj.fk_model_id) NcError.tableNotFound(insertObj.fk_model_id);
if (!insertObj.source_id) {
const model = await Model.getByIdOrName(
context,
{ id: insertObj.fk_model_id },
ncMeta,
);
insertObj.base_id = model.base_id;
insertObj.source_id = model.source_id;
}
const res = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.COMMENTS,
prepareForDb(insertObj),
);
@ -91,6 +104,7 @@ export default class Comment implements CommentType {
return res;
}
public static async update(
context: NcContext,
commentId: string,
comment: Partial<Comment>,
ncMeta = Noco.ncMeta,
@ -102,20 +116,24 @@ export default class Comment implements CommentType {
]);
await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.COMMENTS,
prepareForDb(updateObj),
commentId,
);
return Comment.get(commentId, ncMeta);
return Comment.get(context, commentId, ncMeta);
}
static async delete(commentId: string, ncMeta = Noco.ncMeta) {
static async delete(
context: NcContext,
commentId: string,
ncMeta = Noco.ncMeta,
) {
await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.COMMENTS,
{ is_deleted: true },
commentId,
@ -124,10 +142,14 @@ export default class Comment implements CommentType {
return true;
}
static async deleteRowComments(fk_model_id: string, ncMeta = Noco.ncMeta) {
static async deleteRowComments(
context: NcContext,
fk_model_id: string,
ncMeta = Noco.ncMeta,
) {
return ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.COMMENTS,
{
is_deleted: true,
@ -138,10 +160,13 @@ export default class Comment implements CommentType {
);
}
public static async commentsCount(args: {
ids: string[];
fk_model_id: string;
}) {
public static async commentsCount(
context: NcContext,
args: {
ids: string[];
fk_model_id: string;
},
) {
const audits = await Noco.ncMeta
.knex(MetaTable.COMMENTS)
.count('id', { as: 'count' })

58
packages/nocodb/src/models/Extension.ts

@ -1,3 +1,4 @@
import type { NcContext } from '~/interface/config';
import { prepareForDb, prepareForResponse } from '~/utils/modelUtils';
import Noco from '~/Noco';
import { extractProps } from '~/helpers/extractProps';
@ -11,6 +12,7 @@ import NocoCache from '~/cache/NocoCache';
export default class Extension {
id?: string;
fk_workspace_id?: string;
base_id?: string;
fk_user_id?: string;
extension_id?: string;
@ -23,7 +25,11 @@ export default class Extension {
Object.assign(this, extension);
}
public static async get(extensionId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
extensionId: string,
ncMeta = Noco.ncMeta,
) {
let extension = await NocoCache.get(
`${CacheScope.EXTENSION}:${extensionId}`,
CacheGetType.TYPE_OBJECT,
@ -31,8 +37,8 @@ export default class Extension {
if (!extension) {
extension = await ncMeta.metaGet2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.EXTENSIONS,
extensionId,
);
@ -46,19 +52,24 @@ export default class Extension {
return extension && new Extension(extension);
}
static async list(baseId: string, ncMeta = Noco.ncMeta) {
static async list(context: NcContext, baseId: string, ncMeta = Noco.ncMeta) {
const cachedList = await NocoCache.getList(CacheScope.EXTENSION, [baseId]);
let { list: extensionList } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !extensionList.length) {
extensionList = await ncMeta.metaList(null, null, MetaTable.EXTENSIONS, {
condition: {
base_id: baseId,
},
orderBy: {
created_at: 'asc',
extensionList = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.EXTENSIONS,
{
condition: {
base_id: baseId,
},
orderBy: {
created_at: 'asc',
},
},
});
);
if (extensionList) {
extensionList = extensionList.map((extension) =>
@ -74,6 +85,7 @@ export default class Extension {
}
public static async insert(
context: NcContext,
extension: Partial<Extension>,
ncMeta = Noco.ncMeta,
) {
@ -95,13 +107,13 @@ export default class Extension {
}
const { id } = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.EXTENSIONS,
prepareForDb(insertObj, ['kv_store', 'meta']),
);
return this.get(id, ncMeta).then(async (res) => {
return this.get(context, id, ncMeta).then(async (res) => {
await NocoCache.appendToList(
CacheScope.EXTENSION,
[extension.base_id],
@ -112,12 +124,12 @@ export default class Extension {
}
public static async update(
context: NcContext,
extensionId: string,
extension: Partial<Extension>,
ncMeta = Noco.ncMeta,
) {
const updateObj = extractProps(extension, [
'base_id',
'fk_user_id',
'extension_id',
'title',
@ -128,8 +140,8 @@ export default class Extension {
// set meta
await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.EXTENSIONS,
prepareForDb(updateObj, ['kv_store', 'meta']),
extensionId,
@ -140,13 +152,17 @@ export default class Extension {
prepareForResponse(updateObj, ['kv_store', 'meta']),
);
return this.get(extensionId, ncMeta);
return this.get(context, extensionId, ncMeta);
}
static async delete(extensionId: any, ncMeta = Noco.ncMeta) {
static async delete(
context: NcContext,
extensionId: any,
ncMeta = Noco.ncMeta,
) {
const res = await ncMeta.metaDelete(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.EXTENSIONS,
extensionId,
);

306
packages/nocodb/src/models/Filter.ts

@ -1,6 +1,7 @@
import { UITypes } from 'nocodb-sdk';
import type { BoolType, FilterType } from 'nocodb-sdk';
import type { COMPARISON_OPS, COMPARISON_SUB_OPS } from '~/utils/globals';
import type { NcContext } from '~/interface/config';
import Model from '~/models/Model';
import Column from '~/models/Column';
import Hook from '~/models/Hook';
@ -19,6 +20,7 @@ import { extractProps } from '~/helpers/extractProps';
export default class Filter implements FilterType {
id: string;
fk_workspace_id?: string;
fk_model_id?: string;
fk_view_id?: string;
fk_hook_id?: string;
@ -51,10 +53,17 @@ export default class Filter implements FilterType {
return filter && new Filter(filter);
}
public async getModel(ncMeta = Noco.ncMeta): Promise<Model> {
public async getModel(
context: NcContext,
ncMeta = Noco.ncMeta,
): Promise<Model> {
return this.fk_view_id
? (await View.get(this.fk_view_id, ncMeta)).getModel(ncMeta)
? (await View.get(context, this.fk_view_id, ncMeta)).getModel(
context,
ncMeta,
)
: Model.getByIdOrName(
context,
{
id: this.fk_model_id,
},
@ -63,6 +72,7 @@ export default class Filter implements FilterType {
}
public static async insert(
context: NcContext,
filter: Partial<FilterType> & { order?: number },
ncMeta = Noco.ncMeta,
) {
@ -94,29 +104,36 @@ export default class Filter implements FilterType {
[referencedModelColName]: filter[referencedModelColName],
});
if (!(filter.base_id && filter.source_id)) {
if (!filter.source_id) {
let model: { base_id?: string; source_id?: string };
if (filter.fk_view_id) {
model = await View.get(filter.fk_view_id, ncMeta);
model = await View.get(context, filter.fk_view_id, ncMeta);
} else if (filter.fk_hook_id) {
model = await Hook.get(filter.fk_hook_id, ncMeta);
model = await Hook.get(context, filter.fk_hook_id, ncMeta);
} else if (filter.fk_link_col_id) {
model = await Column.get({ colId: filter.fk_link_col_id }, ncMeta);
model = await Column.get(
context,
{ colId: filter.fk_link_col_id },
ncMeta,
);
} else if (filter.fk_column_id) {
model = await Column.get({ colId: filter.fk_column_id }, ncMeta);
model = await Column.get(
context,
{ colId: filter.fk_column_id },
ncMeta,
);
} else {
NcError.invalidFilter(JSON.stringify(filter));
}
if (model != null) {
insertObj.base_id = model.base_id;
insertObj.source_id = model.source_id;
}
}
const row = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
insertObj,
);
@ -124,6 +141,7 @@ export default class Filter implements FilterType {
await Promise.all(
filter.children.map((f) =>
this.insert(
context,
{
...f,
fk_parent_id: row.id,
@ -134,10 +152,11 @@ export default class Filter implements FilterType {
),
);
}
return await this.redisPostInsert(row.id, filter, ncMeta);
return await this.redisPostInsert(context, row.id, filter, ncMeta);
}
static async redisPostInsert(
context: NcContext,
id,
filter: Partial<FilterType>,
ncMeta = Noco.ncMeta,
@ -151,7 +170,12 @@ export default class Filter implements FilterType {
let value = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (!value) {
/* get from db */
value = await ncMeta.metaGet2(null, null, MetaTable.FILTER_EXP, id);
value = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
id,
);
/* store in redis */
await NocoCache.set(key, value).then(async () => {
@ -219,16 +243,26 @@ export default class Filter implements FilterType {
{
// if not a view filter then no need to delete
if (filter.fk_view_id) {
const view = await View.get(filter.fk_view_id, ncMeta);
const view = await View.get(context, filter.fk_view_id, ncMeta);
await View.clearSingleQueryCache(view.fk_model_id, [view], ncMeta);
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[view],
ncMeta,
);
}
}
return this.castType(value);
}
static async update(id, filter: Partial<Filter>, ncMeta = Noco.ncMeta) {
static async update(
context: NcContext,
id,
filter: Partial<Filter>,
ncMeta = Noco.ncMeta,
) {
const updateObj = extractProps(filter, [
'fk_column_id',
'comparison_op',
@ -245,8 +279,8 @@ export default class Filter implements FilterType {
// set meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
updateObj,
id,
@ -256,11 +290,12 @@ export default class Filter implements FilterType {
// on update delete any optimised single query cache
{
const filter = await this.get(id, ncMeta);
const filter = await this.get(context, id, ncMeta);
// if not a view filter then no need to delete
if (filter.fk_view_id) {
const view = await View.get(filter.fk_view_id, ncMeta);
const view = await View.get(context, filter.fk_view_id, ncMeta);
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[{ id: filter.fk_view_id }],
ncMeta,
@ -271,14 +306,19 @@ export default class Filter implements FilterType {
return res;
}
static async delete(id: string, ncMeta = Noco.ncMeta) {
const filter = await this.get(id, ncMeta);
static async delete(context: NcContext, id: string, ncMeta = Noco.ncMeta) {
const filter = await this.get(context, id, ncMeta);
const deleteRecursively = async (filter: Filter) => {
if (!filter) return;
for (const f of (await filter?.getChildren()) || [])
for (const f of (await filter?.getChildren(context, ncMeta)) || [])
await deleteRecursively(f);
await ncMeta.metaDelete(null, null, MetaTable.FILTER_EXP, filter.id);
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
filter.id,
);
await NocoCache.deepDel(
`${CacheScope.FILTER_EXP}:${filter.id}`,
CacheDelDirection.CHILD_TO_PARENT,
@ -290,9 +330,10 @@ export default class Filter implements FilterType {
{
// if not a view filter then no need to delete
if (filter.fk_view_id) {
const view = await View.get(filter.fk_view_id, ncMeta);
const view = await View.get(context, filter.fk_view_id, ncMeta);
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[{ id: filter.fk_view_id }],
ncMeta,
@ -301,23 +342,35 @@ export default class Filter implements FilterType {
}
}
public getColumn(): Promise<Column> {
public getColumn(context: NcContext, ncMeta = Noco.ncMeta): Promise<Column> {
if (!this.fk_column_id) return null;
return Column.get({
colId: this.fk_column_id,
});
return Column.get(
context,
{
colId: this.fk_column_id,
},
ncMeta,
);
}
public async getGroup(ncMeta = Noco.ncMeta): Promise<Filter> {
public async getGroup(
context: NcContext,
ncMeta = Noco.ncMeta,
): Promise<Filter> {
if (!this.fk_parent_id) return null;
let filterObj = await NocoCache.get(
`${CacheScope.FILTER_EXP}:${this.fk_parent_id}`,
2,
);
if (!filterObj) {
filterObj = await ncMeta.metaGet2(null, null, MetaTable.FILTER_EXP, {
id: this.fk_parent_id,
});
filterObj = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
id: this.fk_parent_id,
},
);
await NocoCache.set(
`${CacheScope.FILTER_EXP}:${this.fk_parent_id}`,
filterObj,
@ -326,7 +379,10 @@ export default class Filter implements FilterType {
return this.castType(filterObj);
}
public async getChildren(ncMeta = Noco.ncMeta): Promise<Filter[]> {
public async getChildren(
context: NcContext,
ncMeta = Noco.ncMeta,
): Promise<Filter[]> {
if (this.children) return this.children;
if (!this.is_group) return null;
const cachedList = await NocoCache.getList(CacheScope.FILTER_EXP, [
@ -335,14 +391,19 @@ export default class Filter implements FilterType {
let { list: childFilters } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !childFilters.length) {
childFilters = await ncMeta.metaList2(null, null, MetaTable.FILTER_EXP, {
condition: {
fk_parent_id: this.id,
},
orderBy: {
order: 'asc',
childFilters = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition: {
fk_parent_id: this.id,
},
orderBy: {
order: 'asc',
},
},
});
);
await NocoCache.setList(CacheScope.FILTER_EXP, [this.id], childFilters);
}
return childFilters && childFilters.map((f) => this.castType(f));
@ -356,8 +417,8 @@ export default class Filter implements FilterType {
// if (!viewId) return null;
//
// const filterObj = await ncMeta.metaGet2(
// null,
// null,
// context.workspace_id,
// context.base_id,
// MetaTable.FILTER_EXP,
// { fk_view_id: viewId, fk_parent_id: null }
// );
@ -365,6 +426,7 @@ export default class Filter implements FilterType {
// }
public static async getFilterObject(
context: NcContext,
{
viewId,
hookId,
@ -392,12 +454,17 @@ export default class Filter implements FilterType {
condition.fk_link_col_id = linkColId;
}
filters = await ncMeta.metaList2(null, null, MetaTable.FILTER_EXP, {
condition,
orderBy: {
order: 'asc',
filters = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition,
orderBy: {
order: 'asc',
},
},
});
);
await NocoCache.setList(
CacheScope.FILTER_EXP,
@ -423,7 +490,7 @@ export default class Filter implements FilterType {
grouped[filter._fk_parent_id] = grouped[filter._fk_parent_id] || [];
grouped[filter._fk_parent_id].push(filter);
idFilterMapping[filter.id] = filter;
filter.column = await new Filter(filter).getColumn();
filter.column = await new Filter(filter).getColumn(context, ncMeta);
if (filter.column?.uidt === UITypes.LinkToAnotherRecord) {
}
}
@ -436,14 +503,23 @@ export default class Filter implements FilterType {
return result;
}
static async deleteAll(viewId: string, ncMeta = Noco.ncMeta) {
const filter = await this.getFilterObject({ viewId }, ncMeta);
static async deleteAll(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
) {
const filter = await this.getFilterObject(context, { viewId }, ncMeta);
const deleteRecursively = async (filter) => {
if (!filter) return;
for (const f of filter?.children || []) await deleteRecursively(f);
if (filter.id) {
await ncMeta.metaDelete(null, null, MetaTable.FILTER_EXP, filter.id);
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
filter.id,
);
await NocoCache.deepDel(
`${CacheScope.FILTER_EXP}:${filter.id}`,
CacheDelDirection.CHILD_TO_PARENT,
@ -454,19 +530,33 @@ export default class Filter implements FilterType {
// on update delete any optimised single query cache
{
const view = await View.get(viewId, ncMeta);
await View.clearSingleQueryCache(view.fk_model_id, [view], ncMeta);
const view = await View.get(context, viewId, ncMeta);
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[view],
ncMeta,
);
}
}
static async deleteAllByHook(hookId: string, ncMeta = Noco.ncMeta) {
const filter = await this.getFilterObject({ hookId }, ncMeta);
static async deleteAllByHook(
context: NcContext,
hookId: string,
ncMeta = Noco.ncMeta,
) {
const filter = await this.getFilterObject(context, { hookId }, ncMeta);
const deleteRecursively = async (filter) => {
if (!filter) return;
for (const f of filter?.children || []) await deleteRecursively(f);
if (filter.id) {
await ncMeta.metaDelete(null, null, MetaTable.FILTER_EXP, filter.id);
await ncMeta.metaDelete(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
filter.id,
);
await NocoCache.deepDel(
`${CacheScope.FILTER_EXP}:${filter.id}`,
CacheDelDirection.CHILD_TO_PARENT,
@ -476,7 +566,11 @@ export default class Filter implements FilterType {
await deleteRecursively(filter);
}
public static async get(id: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
id: string,
ncMeta = Noco.ncMeta,
) {
let filterObj =
id &&
(await NocoCache.get(
@ -484,15 +578,21 @@ export default class Filter implements FilterType {
CacheGetType.TYPE_OBJECT,
));
if (!filterObj) {
filterObj = await ncMeta.metaGet2(null, null, MetaTable.FILTER_EXP, {
id,
});
filterObj = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
id,
},
);
await NocoCache.set(`${CacheScope.FILTER_EXP}:${id}`, filterObj);
}
return this.castType(filterObj);
}
static async rootFilterList(
context: NcContext,
{ viewId }: { viewId: any },
ncMeta = Noco.ncMeta,
) {
@ -500,12 +600,17 @@ export default class Filter implements FilterType {
let { list: filterObjs } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !filterObjs.length) {
filterObjs = await ncMeta.metaList2(null, null, MetaTable.FILTER_EXP, {
condition: { fk_view_id: viewId },
orderBy: {
order: 'asc',
filterObjs = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition: { fk_view_id: viewId },
orderBy: {
order: 'asc',
},
},
});
);
await NocoCache.setList(CacheScope.FILTER_EXP, [viewId], filterObjs);
}
return filterObjs
@ -514,6 +619,7 @@ export default class Filter implements FilterType {
}
static async rootFilterListByHook(
context: NcContext,
{ hookId }: { hookId: any },
ncMeta = Noco.ncMeta,
) {
@ -521,12 +627,17 @@ export default class Filter implements FilterType {
let { list: filterObjs } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !filterObjs.length) {
filterObjs = await ncMeta.metaList2(null, null, MetaTable.FILTER_EXP, {
condition: { fk_hook_id: hookId },
orderBy: {
order: 'asc',
filterObjs = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition: { fk_hook_id: hookId },
orderBy: {
order: 'asc',
},
},
});
);
await NocoCache.setList(CacheScope.FILTER_EXP, [hookId], filterObjs);
}
return filterObjs
@ -535,6 +646,7 @@ export default class Filter implements FilterType {
}
static async parentFilterList(
context: NcContext,
{
parentId,
}: {
@ -548,21 +660,27 @@ export default class Filter implements FilterType {
let { list: filterObjs } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !filterObjs.length) {
filterObjs = await ncMeta.metaList2(null, null, MetaTable.FILTER_EXP, {
condition: {
fk_parent_id: parentId,
// fk_view_id: viewId,
},
orderBy: {
order: 'asc',
filterObjs = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition: {
fk_parent_id: parentId,
// fk_view_id: viewId,
},
orderBy: {
order: 'asc',
},
},
});
);
await NocoCache.setList(CacheScope.FILTER_EXP, [parentId], filterObjs);
}
return filterObjs?.map((f) => this.castType(f));
}
static async parentFilterListByHook(
context: NcContext,
{
hookId,
parentId,
@ -579,15 +697,20 @@ export default class Filter implements FilterType {
let { list: filterObjs } = cachedList;
const { isNoneList } = cachedList;
if (!isNoneList && !filterObjs.length) {
filterObjs = await ncMeta.metaList2(null, null, MetaTable.FILTER_EXP, {
condition: {
fk_parent_id: parentId,
fk_hook_id: hookId,
},
orderBy: {
order: 'asc',
filterObjs = await ncMeta.metaList2(
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition: {
fk_parent_id: parentId,
fk_hook_id: hookId,
},
orderBy: {
order: 'asc',
},
},
});
);
await NocoCache.setList(
CacheScope.FILTER_EXP,
[hookId, parentId],
@ -597,10 +720,14 @@ export default class Filter implements FilterType {
return filterObjs?.map((f) => this.castType(f));
}
static async hasEmptyOrNullFilters(baseId: string, ncMeta = Noco.ncMeta) {
static async hasEmptyOrNullFilters(
context: NcContext,
baseId: string,
ncMeta = Noco.ncMeta,
) {
const emptyOrNullFilterObjs = await ncMeta.metaList2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FILTER_EXP,
{
condition: {
@ -636,6 +763,7 @@ export default class Filter implements FilterType {
}
static async rootFilterListByLink(
_context: NcContext,
{ columnId: _columnId }: { columnId: any },
_ncMeta = Noco.ncMeta,
) {

64
packages/nocodb/src/models/FormView.ts

@ -4,6 +4,7 @@ import type {
FormType,
MetaType,
} from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import { PresignedUrl } from '~/models';
import FormViewColumn from '~/models/FormViewColumn';
import View from '~/models/View';
@ -37,6 +38,7 @@ export default class FormView implements FormViewType {
fk_view_id: string;
columns?: FormViewColumn[];
fk_workspace_id?: string;
base_id?: string;
source_id?: string;
meta?: MetaType;
@ -45,7 +47,11 @@ export default class FormView implements FormViewType {
Object.assign(this, data);
}
public static async get(viewId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
) {
let view =
viewId &&
(await NocoCache.get(
@ -53,9 +59,14 @@ export default class FormView implements FormViewType {
CacheGetType.TYPE_OBJECT,
));
if (!view) {
view = await ncMeta.metaGet2(null, null, MetaTable.FORM_VIEW, {
fk_view_id: viewId,
});
view = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW,
{
fk_view_id: viewId,
},
);
if (view) {
view.meta = deserializeJSON(view.meta);
@ -74,7 +85,11 @@ export default class FormView implements FormViewType {
return view && new FormView(view);
}
static async insert(view: Partial<FormView>, ncMeta = Noco.ncMeta) {
static async insert(
context: NcContext,
view: Partial<FormView>,
ncMeta = Noco.ncMeta,
) {
const insertObj = extractProps(view, [
'fk_view_id',
'base_id',
@ -105,17 +120,24 @@ export default class FormView implements FormViewType {
);
}
if (!(view.base_id && view.source_id)) {
const viewRef = await View.get(view.fk_view_id);
insertObj.base_id = viewRef.base_id;
const viewRef = await View.get(context, view.fk_view_id, ncMeta);
if (!view.source_id) {
insertObj.source_id = viewRef.source_id;
}
await ncMeta.metaInsert2(null, null, MetaTable.FORM_VIEW, insertObj, true);
await ncMeta.metaInsert2(
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW,
insertObj,
true,
);
return this.get(view.fk_view_id, ncMeta);
return this.get(context, view.fk_view_id, ncMeta);
}
static async update(
context: NcContext,
formId: string,
body: Partial<FormView>,
ncMeta = Noco.ncMeta,
@ -146,8 +168,8 @@ export default class FormView implements FormViewType {
// update meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW,
prepareForDb(updateObj),
{
@ -163,13 +185,21 @@ export default class FormView implements FormViewType {
return res;
}
async getColumns(ncMeta = Noco.ncMeta) {
return (this.columns = await FormViewColumn.list(this.fk_view_id, ncMeta));
async getColumns(context: NcContext, ncMeta = Noco.ncMeta) {
return (this.columns = await FormViewColumn.list(
context,
this.fk_view_id,
ncMeta,
));
}
static async getWithInfo(formViewId: string, ncMeta = Noco.ncMeta) {
const form = await this.get(formViewId, ncMeta);
await form.getColumns(ncMeta);
static async getWithInfo(
context: NcContext,
formViewId: string,
ncMeta = Noco.ncMeta,
) {
const form = await this.get(context, formViewId, ncMeta);
await form.getColumns(context, ncMeta);
return form;
}

40
packages/nocodb/src/models/FormViewColumn.ts

@ -4,6 +4,7 @@ import type {
MetaType,
StringOrNullType,
} from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import View from '~/models/View';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
@ -16,6 +17,7 @@ export default class FormViewColumn implements FormColumnType {
id?: string;
fk_view_id?: string;
fk_column_id?: string;
fk_workspace_id?: string;
base_id?: string;
source_id?: string;
label?: StringOrNullType;
@ -32,7 +34,11 @@ export default class FormViewColumn implements FormColumnType {
Object.assign(this, data);
}
public static async get(formViewColumnId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
formViewColumnId: string,
ncMeta = Noco.ncMeta,
) {
let viewColumn =
formViewColumnId &&
(await NocoCache.get(
@ -41,8 +47,8 @@ export default class FormViewColumn implements FormColumnType {
));
if (!viewColumn) {
viewColumn = await ncMeta.metaGet2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW_COLUMNS,
formViewColumnId,
);
@ -60,7 +66,11 @@ export default class FormViewColumn implements FormColumnType {
return viewColumn && new FormViewColumn(viewColumn);
}
static async insert(column: Partial<FormViewColumn>, ncMeta = Noco.ncMeta) {
static async insert(
context: NcContext,
column: Partial<FormViewColumn>,
ncMeta = Noco.ncMeta,
) {
const insertObj = extractProps(column, [
'fk_view_id',
'fk_column_id',
@ -86,20 +96,20 @@ export default class FormViewColumn implements FormColumnType {
insertObj.meta = serializeJSON(insertObj.meta);
}
if (!(insertObj.base_id && insertObj.source_id)) {
const viewRef = await View.get(insertObj.fk_view_id, ncMeta);
insertObj.base_id = viewRef.base_id;
const viewRef = await View.get(context, insertObj.fk_view_id, ncMeta);
if (!insertObj.source_id) {
insertObj.source_id = viewRef.source_id;
}
const { id } = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW_COLUMNS,
insertObj,
);
return this.get(id, ncMeta).then(async (viewColumn) => {
return this.get(context, id, ncMeta).then(async (viewColumn) => {
await NocoCache.appendToList(
CacheScope.FORM_VIEW_COLUMN,
[column.fk_view_id],
@ -110,6 +120,7 @@ export default class FormViewColumn implements FormColumnType {
}
public static async list(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
): Promise<FormViewColumn[]> {
@ -120,8 +131,8 @@ export default class FormViewColumn implements FormColumnType {
const { isNoneList } = cachedList;
if (!isNoneList && !viewColumns.length) {
viewColumns = await ncMeta.metaList2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW_COLUMNS,
{
condition: {
@ -152,6 +163,7 @@ export default class FormViewColumn implements FormColumnType {
}
static async update(
context: NcContext,
columnId: string,
body: Partial<FormViewColumn>,
ncMeta = Noco.ncMeta,
@ -169,8 +181,8 @@ export default class FormViewColumn implements FormColumnType {
// update meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.FORM_VIEW_COLUMNS,
prepareForDb(updateObj),
columnId,

36
packages/nocodb/src/models/FormulaColumn.ts

@ -1,3 +1,4 @@
import type { NcContext } from '~/interface/config';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
import { extractProps } from '~/helpers/extractProps';
@ -7,6 +8,8 @@ import { parseMetaProp, stringifyMetaProp } from '~/utils/modelUtils';
export default class FormulaColumn {
formula: string;
formula_raw: string;
fk_workspace_id?: string;
base_id?: string;
fk_column_id: string;
error: string;
private parsed_tree?: any;
@ -18,6 +21,7 @@ export default class FormulaColumn {
}
public static async insert(
context: NcContext,
formulaColumn: Partial<FormulaColumn> & { parsed_tree?: any },
ncMeta = Noco.ncMeta,
) {
@ -31,12 +35,21 @@ export default class FormulaColumn {
insertObj.parsed_tree = stringifyMetaProp(insertObj, 'parsed_tree');
await ncMeta.metaInsert2(null, null, MetaTable.COL_FORMULA, insertObj);
await ncMeta.metaInsert2(
context.workspace_id,
context.base_id,
MetaTable.COL_FORMULA,
insertObj,
);
return this.read(formulaColumn.fk_column_id, ncMeta);
return this.read(context, formulaColumn.fk_column_id, ncMeta);
}
public static async read(columnId: string, ncMeta = Noco.ncMeta) {
public static async read(
context: NcContext,
columnId: string,
ncMeta = Noco.ncMeta,
) {
let column =
columnId &&
(await NocoCache.get(
@ -45,8 +58,8 @@ export default class FormulaColumn {
));
if (!column) {
column = await ncMeta.metaGet2(
null, //,
null, //model.db_alias,
context.workspace_id,
context.base_id,
MetaTable.COL_FORMULA,
{ fk_column_id: columnId },
);
@ -62,6 +75,7 @@ export default class FormulaColumn {
id: string;
static async update(
context: NcContext,
columnId: string,
formula: Partial<FormulaColumn> & { parsed_tree?: any },
ncMeta = Noco.ncMeta,
@ -77,9 +91,15 @@ export default class FormulaColumn {
if ('parsed_tree' in updateObj)
updateObj.parsed_tree = stringifyMetaProp(updateObj, 'parsed_tree');
// set meta
await ncMeta.metaUpdate(null, null, MetaTable.COL_FORMULA, updateObj, {
fk_column_id: columnId,
});
await ncMeta.metaUpdate(
context.workspace_id,
context.base_id,
MetaTable.COL_FORMULA,
updateObj,
{
fk_column_id: columnId,
},
);
await NocoCache.update(`${CacheScope.COL_FORMULA}:${columnId}`, updateObj);
}

51
packages/nocodb/src/models/GalleryView.ts

@ -5,6 +5,7 @@ import type {
GalleryType,
MetaType,
} from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import View from '~/models/View';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
@ -28,6 +29,7 @@ export default class GalleryView implements GalleryType {
show_all_fields?: BoolType;
fk_cover_image_col_id?: string;
fk_workspace_id?: string;
base_id?: string;
source_id?: string;
@ -38,7 +40,11 @@ export default class GalleryView implements GalleryType {
Object.assign(this, data);
}
public static async get(viewId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
) {
let view =
viewId &&
(await NocoCache.get(
@ -46,19 +52,28 @@ export default class GalleryView implements GalleryType {
CacheGetType.TYPE_OBJECT,
));
if (!view) {
view = await ncMeta.metaGet2(null, null, MetaTable.GALLERY_VIEW, {
fk_view_id: viewId,
});
view = await ncMeta.metaGet2(
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW,
{
fk_view_id: viewId,
},
);
await NocoCache.set(`${CacheScope.GALLERY_VIEW}:${viewId}`, view);
}
return view && new GalleryView(view);
}
static async insert(view: Partial<GalleryView>, ncMeta = Noco.ncMeta) {
const columns = await View.get(view.fk_view_id, ncMeta)
.then((v) => v?.getModel(ncMeta))
.then((m) => m.getColumns(ncMeta));
static async insert(
context: NcContext,
view: Partial<GalleryView>,
ncMeta = Noco.ncMeta,
) {
const columns = await View.get(context, view.fk_view_id, ncMeta)
.then((v) => v?.getModel(context, ncMeta))
.then((m) => m.getColumns(context, ncMeta));
const insertObj = extractProps(view, [
'base_id',
@ -77,24 +92,25 @@ export default class GalleryView implements GalleryType {
view?.fk_cover_image_col_id ||
columns?.find((c) => c.uidt === UITypes.Attachment)?.id;
if (!(view.base_id && view.source_id)) {
const viewRef = await View.get(view.fk_view_id);
insertObj.base_id = viewRef.base_id;
const viewRef = await View.get(context, insertObj.fk_view_id, ncMeta);
if (!insertObj.source_id) {
insertObj.source_id = viewRef.source_id;
}
await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW,
insertObj,
true,
);
return this.get(view.fk_view_id, ncMeta);
return this.get(context, view.fk_view_id, ncMeta);
}
static async update(
context: NcContext,
galleryId: string,
body: Partial<GalleryView>,
ncMeta = Noco.ncMeta,
@ -103,8 +119,8 @@ export default class GalleryView implements GalleryType {
// update meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW,
prepareForDb(updateObj),
{
@ -117,10 +133,11 @@ export default class GalleryView implements GalleryType {
prepareForResponse(updateObj),
);
const view = await View.get(galleryId);
const view = await View.get(context, galleryId, ncMeta);
// on update, delete any optimised single query cache
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[{ id: galleryId }],
ncMeta,

50
packages/nocodb/src/models/GalleryViewColumn.ts

@ -1,4 +1,5 @@
import type { BoolType } from 'nocodb-sdk';
import type { NcContext } from '~/interface/config';
import View from '~/models/View';
import Noco from '~/Noco';
import NocoCache from '~/cache/NocoCache';
@ -13,6 +14,7 @@ export default class GalleryViewColumn {
fk_view_id: string;
fk_column_id: string;
fk_workspace_id?: string;
base_id?: string;
source_id?: string;
@ -20,7 +22,11 @@ export default class GalleryViewColumn {
Object.assign(this, data);
}
public static async get(galleryViewColumnId: string, ncMeta = Noco.ncMeta) {
public static async get(
context: NcContext,
galleryViewColumnId: string,
ncMeta = Noco.ncMeta,
) {
let view =
galleryViewColumnId &&
(await NocoCache.get(
@ -29,8 +35,8 @@ export default class GalleryViewColumn {
));
if (!view) {
view = await ncMeta.metaGet2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW_COLUMNS,
galleryViewColumnId,
);
@ -42,6 +48,7 @@ export default class GalleryViewColumn {
return view && new GalleryViewColumn(view);
}
static async insert(
context: NcContext,
column: Partial<GalleryViewColumn>,
ncMeta = Noco.ncMeta,
) {
@ -60,26 +67,31 @@ export default class GalleryViewColumn {
},
);
if (!(column.base_id && column.source_id)) {
const viewRef = await View.get(column.fk_view_id, ncMeta);
insertObj.base_id = viewRef.base_id;
const viewRef = await View.get(context, insertObj.fk_view_id, ncMeta);
if (!insertObj.source_id) {
insertObj.source_id = viewRef.source_id;
}
const { id } = await ncMeta.metaInsert2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW_COLUMNS,
insertObj,
);
// on new view column, delete any optimised single query cache
{
const view = await View.get(column.fk_view_id, ncMeta);
await View.clearSingleQueryCache(view.fk_model_id, [view], ncMeta);
const view = await View.get(context, column.fk_view_id, ncMeta);
await View.clearSingleQueryCache(
context,
view.fk_model_id,
[view],
ncMeta,
);
}
return this.get(id, ncMeta).then(async (viewColumn) => {
return this.get(context, id, ncMeta).then(async (viewColumn) => {
await NocoCache.appendToList(
CacheScope.GALLERY_VIEW_COLUMN,
[column.fk_view_id],
@ -90,6 +102,7 @@ export default class GalleryViewColumn {
}
public static async list(
context: NcContext,
viewId: string,
ncMeta = Noco.ncMeta,
): Promise<GalleryViewColumn[]> {
@ -100,8 +113,8 @@ export default class GalleryViewColumn {
const { isNoneList } = cachedList;
if (!isNoneList && !views.length) {
views = await ncMeta.metaList2(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW_COLUMNS,
{
condition: {
@ -123,6 +136,7 @@ export default class GalleryViewColumn {
}
static async update(
context: NcContext,
columnId: string,
body: Partial<GalleryViewColumn>,
ncMeta = Noco.ncMeta,
@ -131,8 +145,8 @@ export default class GalleryViewColumn {
// set meta
const res = await ncMeta.metaUpdate(
null,
null,
context.workspace_id,
context.base_id,
MetaTable.GALLERY_VIEW_COLUMNS,
updateObj,
columnId,
@ -144,9 +158,9 @@ export default class GalleryViewColumn {
// on view column update, delete any optimised single query cache
{
const viewCol = await this.get(columnId, ncMeta);
const view = await View.get(viewCol.fk_view_id, ncMeta);
await View.clearSingleQueryCache(view.fk_model_id, [view]);
const viewCol = await this.get(context, columnId, ncMeta);
const view = await View.get(context, viewCol.fk_view_id, ncMeta);
await View.clearSingleQueryCache(context, view.fk_model_id, [view]);
}
return res;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save