Browse Source

Merge pull request #5222 from nocodb/feat/ajv-validation-followup

refactor: ajv validation & swagger
pull/5273/head
Pranav C 2 years ago committed by GitHub
parent
commit
cdafba4c49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  2. 20
      packages/noco-docs/content/en/developer-resources/rest-apis.md
  3. 4387
      packages/nocodb-sdk/src/lib/Api.ts
  4. 2
      packages/nocodb-sdk/src/lib/globals.ts
  5. 25
      packages/nocodb/package-lock.json
  6. 4
      packages/nocodb/package.json
  7. 4
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/genRollupSelectv2.ts
  8. 2
      packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts
  9. 16
      packages/nocodb/src/lib/meta/helpers/getColumnPropsFromUIDT.ts
  10. 5
      packages/nocodb/src/lib/models/ApiToken.ts
  11. 47
      packages/nocodb/src/lib/models/Audit.ts
  12. 22
      packages/nocodb/src/lib/models/Base.ts
  13. 6
      packages/nocodb/src/lib/models/Filter.ts
  14. 1
      packages/nocodb/src/lib/models/FormView.ts
  15. 28
      packages/nocodb/src/lib/models/FormViewColumn.ts
  16. 3
      packages/nocodb/src/lib/models/GalleryViewColumn.ts
  17. 4
      packages/nocodb/src/lib/models/GridViewColumn.ts
  18. 2
      packages/nocodb/src/lib/models/Hook.ts
  19. 2
      packages/nocodb/src/lib/models/HookFilter.ts
  20. 3
      packages/nocodb/src/lib/models/KanbanViewColumn.ts
  21. 3
      packages/nocodb/src/lib/models/LookupColumn.ts
  22. 4
      packages/nocodb/src/lib/models/MapViewColumn.ts
  23. 10
      packages/nocodb/src/lib/models/Model.ts
  24. 4
      packages/nocodb/src/lib/models/Project.ts
  25. 4
      packages/nocodb/src/lib/models/ProjectUser.ts
  26. 19
      packages/nocodb/src/lib/models/RollupColumn.ts
  27. 6
      packages/nocodb/src/lib/models/SelectOption.ts
  28. 4
      packages/nocodb/src/lib/models/View.ts
  29. 2
      packages/nocodb/src/lib/services/attachmentService.ts
  30. 2
      packages/nocodb/src/lib/services/formViewService.ts
  31. 1
      packages/nocodb/src/lib/services/projectUserService.ts
  32. 2
      packages/nocodb/src/lib/services/syncService/helpers/job.ts
  33. 15
      packages/nocodb/src/lib/services/viewColumnService.ts
  34. 14
      packages/nocodb/src/lib/services/viewService.ts
  35. 40
      packages/nocodb/src/lib/version-upgrader/ncProjectUpgraderV2_0090000.ts
  36. 45
      packages/nocodb/src/run/redoc.ts
  37. 12043
      packages/nocodb/src/schema/swagger.json
  38. 12
      packages/nocodb/tests/unit/factory/project.ts
  39. 10
      packages/nocodb/tests/unit/rest/tests/project.test.ts

4
packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue

@ -90,7 +90,7 @@ const filterUpdateCondition = (filter: FilterType, i: number) => {
// since `blank`, `empty`, `null` doesn't require value,
// hence remove the previous value
filter.value = null
filter.comparison_sub_op = ''
filter.comparison_sub_op = null
} else if ([UITypes.Date, UITypes.DateTime].includes(col.uidt as UITypes)) {
// for date / datetime,
// the input type could be decimal or datepicker / datetime picker
@ -177,7 +177,7 @@ const selectFilterField = (filter: Filter, index: number) => {
}
} else {
// reset
filter.comparison_sub_op = ''
filter.comparison_sub_op = null
}
// reset filter value as well

20
packages/noco-docs/content/en/developer-resources/rest-apis.md

@ -120,14 +120,20 @@ Currently, the default value for {orgs} is <b>noco</b>. Users will be able to ch
| Meta | Patch | dbView | update | /api/v1/db/meta/tables/{tableId} |
| Meta | Delete| dbView | delete | /api/v1/db/meta/tables/{tableId} |
| Meta | Post | dbView | reorder | /api/v1/db/meta/tables/{tableId}/reorder |
| Meta | Post | dbView | formCreate | /api/v1/db/meta/forms |
| Meta | Patch | dbView | formUpdate | /api/v1/db/meta/forms/{formId} |
| Meta | Get | dbView | formRead | /api/v1/db/meta/forms/{formId} |
| Meta | Post | dbView | formCreate | /api/v1/db/meta/tables/{tableId}/forms |
| Meta | Patch | dbView | formUpdate | /api/v1/db/meta/forms/{formViewId} |
| Meta | Get | dbView | formRead | /api/v1/db/meta/forms/{formViewId} |
| Meta | Patch | dbView | formColumnUpdate | /api/v1/db/meta/form-columns/{formViewColumnId} |
| Meta | Post | dbView | galleryCreate | /api/v1/db/meta/galleries |
| Meta | Patch | dbView | galleryUpdate | /api/v1/db/meta/galleries/{galleriesId} |
| Meta | Get | dbView | galleryRead | /api/v1/db/meta/galleries/{galleriesId} |
| Meta | Post | dbView | gridCreate | /api/v1/db/meta/tables/${tableId}/grids |
| Meta | Post | dbView | galleryCreate | /api/v1/db/meta/tables/{tableId}/galleries |
| Meta | Patch | dbView | galleryUpdate | /api/v1/db/meta/galleries/{galleryViewId} |
| Meta | Get | dbView | galleryRead | /api/v1/db/meta/galleries/{galleryViewId} |
| Meta | Post | dbView | kanbanCreate | /api/v1/db/meta/tables/{tableId}/kanbans |
| Meta | Patch | dbView | kanbanUpdate | /api/v1/db/meta/kanban/{kanbanViewId} |
| Meta | Get | dbView | kanbanRead | /api/v1/db/meta/kanbans/{kanbanViewId} |
| Meta | Post | dbView | mapCreate | /api/v1/db/meta/tables/{tableId}/maps |
| Meta | Patch | dbView | mapUpdate | /api/v1/db/meta/maps/{mapViewId} |
| Meta | Get | dbView | mapRead | /api/v1/db/meta/maps/{mapViewId} |
| Meta | Post | dbView | gridCreate | /api/v1/db/meta/tables/{tableId}/grids |
| Meta | Get | dbView | gridColumnsList | /api/v1/db/meta/grids/{gridId}/grid-columns |
| Meta | Patch | dbView | gridColumnUpdate | /api/v1/db/meta/grid-columns/{columnId} |
| Meta | Patch | dbView | update | /api/v1/db/meta/views/{viewId} |

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

File diff suppressed because it is too large Load Diff

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

@ -55,7 +55,7 @@ export enum AuditOperationSubTypes {
EXPORT_TO_ZIP = 'EXPORT_TO_ZIP',
UPDATED = 'UPDATED',
SIGNIN = 'SIGNIN',
SIGN = 'SIGN',
SIGNUP = 'SIGNUP',
PASSWORD_RESET = 'PASSWORD_RESET',
PASSWORD_FORGOT = 'PASSWORD_FORGOT',
PASSWORD_CHANGE = 'PASSWORD_CHANGE',

25
packages/nocodb/package-lock.json generated

@ -14,6 +14,7 @@
"@sentry/node": "^6.3.5",
"airtable": "^0.11.3",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"archiver": "^5.0.2",
"auto-bind": "^4.0.0",
"aws-sdk": "^2.829.0",
@ -2261,6 +2262,22 @@
"ajv": ">=5.0.0"
}
},
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
@ -20919,6 +20936,14 @@
"dev": true,
"requires": {}
},
"ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"requires": {
"ajv": "^8.0.0"
}
},
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",

4
packages/nocodb/package.json

@ -43,7 +43,8 @@
"watch:run:pg": "cross-env NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG --log-error --project tsconfig.json\"",
"run": "ts-node src/run/docker",
"watch:try": "nodemon -e ts,js -w ./src -x \"ts-node src/run/try --log-error --project tsconfig.json\"",
"example:docker": "ts-node src/run/docker.ts"
"example:docker": "ts-node src/run/docker.ts",
"redoc": "nodemon -e ts,json -w ./src/schema -x \"ts-node src/run/redoc --log-error --project tsconfig.json\""
},
"engines": {
"node": ">=8.9"
@ -54,6 +55,7 @@
"@sentry/node": "^6.3.5",
"airtable": "^0.11.3",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"archiver": "^5.0.2",
"auto-bind": "^4.0.0",
"aws-sdk": "^2.829.0",

4
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/genRollupSelectv2.ts

@ -29,7 +29,7 @@ export default async function ({
case RelationTypes.HAS_MANY:
return {
builder: knex(`${childModel?.table_name} as ${refTableAlias}`)
[columnOptions.rollup_function]?.(
[columnOptions.rollup_function as string]?.(
knex.ref(`${refTableAlias}.${rollupColumn.column_name}`)
)
.where(
@ -47,7 +47,7 @@ export default async function ({
return {
builder: knex(`${parentModel?.table_name} as ${refTableAlias}`)
[columnOptions.rollup_function]?.(
[columnOptions.rollup_function as string]?.(
knex.ref(`${refTableAlias}.${rollupColumn.column_name}`)
)
.innerJoin(

2
packages/nocodb/src/lib/meta/api/helpers/apiHelpers.ts

@ -1,5 +1,6 @@
import { NextFunction, Request, Response } from 'express';
import Ajv, { ErrorObject } from 'ajv';
import addFormats from 'ajv-formats';
// @ts-ignore
import swagger from '../../../../schema/swagger.json';
import { NcError } from '../../helpers/catchError';
@ -12,6 +13,7 @@ export function parseHrtimeToSeconds(hrtime) {
const ajv = new Ajv({ strictSchema: false, strict: false }); // Initialize AJV
ajv.addSchema(swagger, 'swagger.json');
addFormats(ajv);
// A middleware generator to validate the request body
export const getAjvValidatorMw = (schema) => {

16
packages/nocodb/src/lib/meta/helpers/getColumnPropsFromUIDT.ts

@ -1,14 +1,20 @@
import { ColumnReqType, SqlUIColumn, SqlUiFactory, UITypes } from 'nocodb-sdk';
import {
ColumnReqType,
NormalColumnRequestType,
SqlUiFactory,
UITypes,
} from 'nocodb-sdk';
import Base from '../../models/Base';
import Column from '../../models/Column';
export default function getColumnPropsFromUIDT(
column: SqlUIColumn & { uidt: UITypes } & ColumnReqType,
column: ColumnReqType & { altered?: number },
base: Base
) {
const sqlUi = SqlUiFactory.create(base.getConnectionConfig());
const colProp = sqlUi.getDataTypeForUiType(
column,
column as Column,
column?.['meta']?.['ag'] ? 'AG' : 'AI'
);
const newColumn = {
@ -28,9 +34,9 @@ export default function getColumnPropsFromUIDT(
if (
column &&
selectTypes.includes(newColumn.uidt) &&
selectTypes.includes(column.uidt)
selectTypes.includes(column.uidt as UITypes)
) {
newColumn.dtxp = column.dtxp;
newColumn.dtxp = (column as NormalColumnRequestType).dtxp;
}
newColumn.altered = column.altered || 2;

5
packages/nocodb/src/lib/models/ApiToken.ts

@ -7,8 +7,9 @@ import {
import Noco from '../Noco';
import { nanoid } from 'nanoid';
import NocoCache from '../cache/NocoCache';
import type { ApiTokenType } from 'nocodb-sdk';
export default class ApiToken {
export default class ApiToken implements ApiTokenType {
project_id?: string;
db_alias?: string;
fk_user_id?: string;
@ -18,7 +19,7 @@ export default class ApiToken {
expiry?: string;
enabled?: boolean;
constructor(audit: Partial<ApiToken>) {
constructor(audit: Partial<ApiToken | ApiTokenType>) {
Object.assign(this, audit);
}

47
packages/nocodb/src/lib/models/Audit.ts

@ -4,6 +4,49 @@ import Noco from '../Noco';
import Model from './Model';
import { extractProps } from '../meta/helpers/extractProps';
const opTypes = <const>[
'COMMENT',
'DATA',
'PROJECT',
'VIRTUAL_RELATION',
'RELATION',
'TABLE_VIEW',
'TABLE',
'VIEW',
'META',
'WEBHOOKS',
'AUTHENTICATION',
'TABLE_COLUMN',
'ORG_USER',
];
const opSubTypes = <const>[
'UPDATE',
'INSERT',
'BULK_INSERT',
'BULK_UPDATE',
'BULK_DELETE',
'LINK_RECORD',
'UNLINK_RECORD',
'DELETE',
'CREATED',
'DELETED',
'RENAMED',
'IMPORT_FROM_ZIP',
'EXPORT_TO_FS',
'EXPORT_TO_ZIP',
'UPDATED',
'SIGNIN',
'SIGNUP',
'PASSWORD_RESET',
'PASSWORD_FORGOT',
'PASSWORD_CHANGE',
'EMAIL_VERIFICATION',
'ROLES_MANAGEMENT',
'INVITE',
'RESEND_INVITE',
];
export default class Audit implements AuditType {
id?: string;
user?: string;
@ -12,8 +55,8 @@ export default class Audit implements AuditType {
project_id?: string;
fk_model_id?: string;
row_id?: string;
op_type?: string;
op_sub_type?: string;
op_type?: typeof opTypes[number];
op_sub_type?: typeof opSubTypes[number];
status?: string;
description?: string;
details?: string;

22
packages/nocodb/src/lib/models/Base.ts

@ -14,16 +14,24 @@ import { extractProps } from '../meta/helpers/extractProps';
import { NcError } from '../meta/helpers/catchError';
import SyncSource from './SyncSource';
export const DB_TYPES = <const>[
'mysql2',
'sqlite3',
'mysql',
'mssql',
'snowflake',
'oracledb',
'pg',
];
// todo: hide credentials
export default class Base implements BaseType {
id?: string;
project_id?: string;
alias?: string;
type?: string;
type?: typeof DB_TYPES[number];
is_meta?: BoolType;
config?: any;
created_at?: any;
updated_at?: any;
config?: string;
inflection_column?: string;
inflection_table?: string;
order?: number;
@ -43,8 +51,6 @@ export default class Base implements BaseType {
'config',
'type',
'is_meta',
'created_at',
'updated_at',
'inflection_column',
'inflection_table',
'order',
@ -81,8 +87,6 @@ export default class Base implements BaseType {
base: BaseType & {
id: string;
projectId: string;
created_at?;
updated_at?;
},
ncMeta = Noco.ncMeta
) {
@ -101,8 +105,6 @@ export default class Base implements BaseType {
'config',
'type',
'is_meta',
'created_at',
'updated_at',
'inflection_column',
'inflection_table',
'order',

6
packages/nocodb/src/lib/models/Filter.ts

@ -71,7 +71,7 @@ export const COMPARISON_SUB_OPS = <const>[
...IS_WITHIN_COMPARISON_SUB_OPS,
];
export default class Filter {
export default class Filter implements FilterType {
id: string;
fk_model_id?: string;
@ -85,7 +85,7 @@ export default class Filter {
value?: string;
logical_op?: string;
logical_op?: 'and' | 'or' | 'not';
is_group?: BoolType;
children?: Filter[];
project_id?: string;
@ -383,7 +383,7 @@ export default class Filter {
const result: FilterType = {
is_group: true,
children: [],
logical_op: 'AND',
logical_op: 'and',
};
const grouped = {};

1
packages/nocodb/src/lib/models/FormView.ts

@ -25,7 +25,6 @@ export default class FormView implements FormType {
fk_view_id: string;
columns?: FormViewColumn[];
project_id?: string;
base_id?: string;
meta?: string | Record<string, any>;

28
packages/nocodb/src/lib/models/FormViewColumn.ts

@ -1,6 +1,11 @@
import Noco from '../Noco';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import { FormColumnType } from 'nocodb-sdk';
import {
BoolType,
FormColumnType,
MetaType,
StringOrNullType,
} from 'nocodb-sdk';
import { deserializeJSON, serializeJSON } from '../utils/serialize';
import View from './View';
import NocoCache from '../cache/NocoCache';
@ -8,25 +13,23 @@ import { extractProps } from '../meta/helpers/extractProps';
export default class FormViewColumn implements FormColumnType {
id?: string;
label?: string;
help?: string;
description?: string;
required?: boolean;
show?: boolean;
order?: number;
fk_view_id?: string;
fk_column_id?: string;
project_id?: string;
base_id?: string;
meta?: string | Record<string, any>;
label?: StringOrNullType;
help?: StringOrNullType;
description?: StringOrNullType;
required?: BoolType;
uuid?: StringOrNullType;
show?: BoolType;
order?: number;
meta?: MetaType;
constructor(data: FormViewColumn) {
Object.assign(this, data);
}
uuid?: any;
public static async get(formViewColumnId: string, ncMeta = Noco.ncMeta) {
let viewColumn =
formViewColumnId &&
@ -187,7 +190,4 @@ export default class FormViewColumn implements FormColumnType {
columnId
);
}
created_at?: string;
updated_at?: string;
}

3
packages/nocodb/src/lib/models/GalleryViewColumn.ts

@ -3,11 +3,12 @@ import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import View from './View';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';
import { BoolType } from 'nocodb-sdk';
export default class GalleryViewColumn {
id: string;
title?: string;
show?: boolean;
show?: BoolType;
order?: number;
fk_view_id: string;

4
packages/nocodb/src/lib/models/GridViewColumn.ts

@ -1,13 +1,13 @@
import Noco from '../Noco';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import { GridColumnType } from 'nocodb-sdk';
import { BoolType, GridColumnType } from 'nocodb-sdk';
import { extractProps } from '../meta/helpers/extractProps';
import View from './View';
import NocoCache from '../cache/NocoCache';
export default class GridViewColumn implements GridColumnType {
id: string;
show: boolean;
show: BoolType;
order: number;
width?: string;

2
packages/nocodb/src/lib/models/Hook.ts

@ -26,7 +26,7 @@ export default class Hook implements HookType {
url?: string;
headers?: string;
condition?: BoolType;
notification?: string;
notification?: string | object;
retries?: number;
retry_interval?: number;
timeout?: number;

2
packages/nocodb/src/lib/models/HookFilter.ts

@ -254,7 +254,7 @@ export default class Filter {
const result: FilterType = {
is_group: true,
children: [],
logical_op: 'AND',
logical_op: 'and',
};
const grouped = {};

3
packages/nocodb/src/lib/models/KanbanViewColumn.ts

@ -3,11 +3,12 @@ import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import View from './View';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';
import { BoolType } from 'nocodb-sdk';
export default class KanbanViewColumn {
id: string;
title?: string;
show?: boolean;
show?: BoolType;
order?: number;
fk_view_id: string;

3
packages/nocodb/src/lib/models/LookupColumn.ts

@ -3,8 +3,9 @@ import Column from './Column';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';
import type { LookupType } from 'nocodb-sdk';
export default class LookupColumn {
export default class LookupColumn implements LookupType {
fk_relation_column_id: string;
fk_lookup_column_id: string;
fk_column_id: string;

4
packages/nocodb/src/lib/models/MapViewColumn.ts

@ -2,11 +2,11 @@ import Noco from '../Noco';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import View from './View';
import NocoCache from '../cache/NocoCache';
import { BoolType } from 'nocodb-sdk';
export default class MapViewColumn {
id: string;
title?: string;
show?: boolean;
show?: BoolType;
order?: number;
fk_view_id: string;

10
packages/nocodb/src/lib/models/Model.ts

@ -5,9 +5,9 @@ import NocoCache from '../cache/NocoCache';
import { XKnex } from '../db/sql-data-mapper';
import { BaseModelSqlv2 } from '../db/sql-data-mapper/lib/sql/BaseModelSqlv2';
import {
BoolType,
isVirtualCol,
ModelTypes,
BoolType,
TableReqType,
TableType,
UITypes,
@ -27,7 +27,6 @@ import { extractProps } from '../meta/helpers/extractProps';
export default class Model implements TableType {
copy_enabled: BoolType;
created_at: Date | number | string;
base_id: 'db' | string;
deleted: BoolType;
enabled: BoolType;
@ -42,7 +41,6 @@ export default class Model implements TableType {
show_all_fields: boolean;
tags: string;
type: ModelTypes;
updated_at: Date | number | string;
table_name: string;
title: string;
@ -100,8 +98,6 @@ export default class Model implements TableType {
baseId,
model: Partial<TableReqType> & {
mm?: BoolType;
created_at?: any;
updated_at?: any;
type?: ModelTypes;
},
ncMeta = Noco.ncMeta
@ -112,8 +108,6 @@ export default class Model implements TableType {
'mm',
'order',
'type',
'created_at',
'updated_at',
'id',
]);
@ -152,8 +146,6 @@ export default class Model implements TableType {
title: model.title || model.table_name,
is_default: true,
type: ViewTypes.GRID,
created_at: model.created_at,
updated_at: model.updated_at,
},
ncMeta
);

4
packages/nocodb/src/lib/models/Project.ts

@ -1,4 +1,4 @@
import Base from './/Base';
import Base, { DB_TYPES } from './/Base';
import Noco from '../Noco';
import { BoolType, MetaType, ProjectType } from 'nocodb-sdk';
import {
@ -68,7 +68,7 @@ export default class Project implements ProjectType {
for (const base of project.bases) {
await Base.createBase(
{
type: base.config?.client,
type: base.config?.client as typeof DB_TYPES[number],
...base,
projectId,
},

4
packages/nocodb/src/lib/models/ProjectUser.ts

@ -20,15 +20,13 @@ export default class ProjectUser {
}
public static async insert(
projectUser: Partial<ProjectUser & { created_at?: any; updated_at?: any }>,
projectUser: Partial<ProjectUser>,
ncMeta = Noco.ncMeta
) {
const insertObj = extractProps(projectUser, [
'fk_user_id',
'project_id',
'roles',
'created_at',
'updated_at',
]);
const { project_id, fk_user_id } = await ncMeta.metaInsert2(

19
packages/nocodb/src/lib/models/RollupColumn.ts

@ -3,15 +3,24 @@ import Column from './Column';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';
import type { RollupType } from 'nocodb-sdk';
export default class RollupColumn {
export const ROLLUP_FUNCTIONS = <const>[
'count',
'min',
'max',
'avg',
'countDistinct',
'sumDistinct',
'avgDistinct',
];
export default class RollupColumn implements RollupType {
id: string;
fk_column_id;
fk_relation_column_id;
fk_rollup_column_id;
rollup_function: string;
id: string;
rollup_function: typeof ROLLUP_FUNCTIONS[number];
constructor(data: Partial<RollupColumn>) {
Object.assign(this, data);

6
packages/nocodb/src/lib/models/SelectOption.ts

@ -2,8 +2,10 @@ import Noco from '../Noco';
import NocoCache from '../cache/NocoCache';
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals';
import { extractProps } from '../meta/helpers/extractProps';
import type { SelectOptionType } from 'nocodb-sdk';
export default class SelectOption {
export default class SelectOption implements SelectOptionType {
id: string;
title: string;
fk_column_id: string;
color: string;
@ -110,6 +112,4 @@ export default class SelectOption {
return data && new SelectOption(data);
}
id: string;
}

4
packages/nocodb/src/lib/models/View.ts

@ -741,8 +741,8 @@ export default class View implements ViewType {
viewId: string,
fkColId: string,
colData: {
order: number;
show: boolean;
order?: number;
show?: BoolType;
},
ncMeta = Noco.ncMeta
): Promise<

2
packages/nocodb/src/lib/services/attachmentService.ts

@ -11,6 +11,7 @@ export async function upload(param: {
// todo: proper type
files: unknown[];
}) {
// TODO: add getAjvValidatorMw
const filePath = sanitizeUrlPath(param.path?.toString()?.split('/') || ['']);
const destPath = path.join('nc', 'uploads', ...filePath);
@ -59,6 +60,7 @@ export async function uploadViaURL(param: {
size?: string | number;
}[];
}) {
// TODO: add getAjvValidatorMw
const filePath = sanitizeUrlPath(param?.path?.toString()?.split('/') || ['']);
const destPath = path.join('nc', 'uploads', ...filePath);

2
packages/nocodb/src/lib/services/formViewService.ts

@ -12,7 +12,7 @@ export async function formViewCreate(param: {
tableId: string;
body: FormReqType;
}) {
validatePayload('swagger.json#/components/schemas/FormCreateReq', param.body);
validatePayload('swagger.json#/components/schemas/FormReq', param.body);
T.emit('evt', { evt_type: 'vtable:created', show_as: 'form' });
const view = await View.insert({

1
packages/nocodb/src/lib/services/projectUserService.ts

@ -252,7 +252,6 @@ export async function projectUserInviteResend(param: {
NcError.badRequest(`User with id '${param.userId}' not found`);
}
param.projectUser.roles = user.roles;
const invite_token = uuidv4();
await User.update(user.id, {

2
packages/nocodb/src/lib/services/syncService/helpers/job.ts

@ -2078,7 +2078,7 @@ export default async (
const _perfStart = recordPerfStart();
await api.dbTableSort.create(viewId, {
fk_column_id: columnId,
direction: s.sortSet[i].ascending ? 'asc' : 'dsc',
direction: s.sortSet[i].ascending ? 'asc' : 'desc',
});
recordPerfStats(_perfStart, 'dbTableSort.create');
}

15
packages/nocodb/src/lib/services/viewColumnService.ts

@ -1,5 +1,7 @@
import { T } from 'nc-help';
import { validatePayload } from '../meta/api/helpers';
import { View } from '../models';
import type { ViewColumnReqType } from 'nocodb-sdk';
export async function columnList(param: { viewId: string }) {
return await View.getColumns(param.viewId);
@ -7,16 +9,17 @@ export async function columnList(param: { viewId: string }) {
export async function columnAdd(param: {
viewId: string;
columnId: string;
// todo: add proper type for grid column in swagger
column: any;
column: ViewColumnReqType;
}) {
validatePayload(
'swagger.json#/components/schemas/ViewColumnReq',
param.column
);
const viewColumn = await View.insertOrUpdateColumn(
param.viewId,
param.columnId,
{
...param.column,
view_id: param.viewId,
}
param.column
);
T.emit('evt', { evt_type: 'viewColumn:inserted' });

14
packages/nocodb/src/lib/services/viewService.ts

@ -1,4 +1,5 @@
import { SharedViewType, ViewType } from 'nocodb-sdk';
import { SharedViewReqType, ViewReqType } from 'nocodb-sdk';
import { validatePayload } from '../meta/api/helpers';
import { Model, View } from '../models';
import { T } from 'nc-help';
import { xcVisibilityMetaGet } from './modelVisibilityService';
@ -33,8 +34,8 @@ export async function shareView(param: { viewId: string }) {
return await View.share(param.viewId);
}
// todo: type correctly
export async function viewUpdate(param: { viewId: string; view: ViewType }) {
export async function viewUpdate(param: { viewId: string; view: ViewReqType }) {
validatePayload('swagger.json#/components/schemas/ViewReq', param.view);
const result = await View.update(param.viewId, param.view);
T.emit('evt', { evt_type: 'vtable:updated', show_as: result.type });
return result;
@ -48,9 +49,12 @@ export async function viewDelete(param: { viewId: string }) {
export async function shareViewUpdate(param: {
viewId: string;
// todo: type correctly
sharedView: SharedViewType;
sharedView: SharedViewReqType;
}) {
validatePayload(
'swagger.json#/components/schemas/SharedViewReq',
param.sharedView
);
T.emit('evt', { evt_type: 'sharedView:updated' });
return await View.update(param.viewId, param.sharedView);
}

40
packages/nocodb/src/lib/version-upgrader/ncProjectUpgraderV2_0090000.ts

@ -14,7 +14,7 @@ import {
import Column from '../models/Column';
import LinkToAnotherRecordColumn from '../models/LinkToAnotherRecordColumn';
import NcHelp from '../utils/NcHelp';
import RollupColumn from '../models/RollupColumn';
import RollupColumn, { ROLLUP_FUNCTIONS } from '../models/RollupColumn';
import View from '../models/View';
import GridView from '../models/GridView';
import KanbanView from '../models/KanbanView';
@ -92,14 +92,10 @@ async function migrateProjects(
is_meta: !!projectConfig.prefix,
type: d.client,
config: d,
created_at: project.created_at,
updated_at: project.updated_at,
inflection_column: inflection.cn,
inflection_table: inflection.tn,
};
}),
created_at: project.created_at,
updated_at: project.updated_at,
};
const p = await Project.createProject(projectBody, ncMeta);
projectsObj[p.id] = p;
@ -126,8 +122,6 @@ async function migrateProjectUsers(
project_id: projectUser.project_id,
fk_user_id: projectUser.user_id,
roles: projectUser.roles,
created_at: projectUser.created_at,
updated_at: projectUser.updated_at,
},
ncMeta
);
@ -268,8 +262,6 @@ interface ModelMetav1 {
m_to_m_meta: string;
order: number;
view_order: number;
created_at;
updated_at;
}
type ObjModelColumnRefv1 = {
@ -406,8 +398,6 @@ async function migrateProjectModels(
title: modelData.alias,
// todo: sanitize
type: modelData.type === 'table' ? ModelTypes.TABLE : ModelTypes.VIEW,
created_at: modelData.created_at,
updated_at: modelData.updated_at,
mm: !!modelData.mm,
},
ncMeta
@ -613,7 +603,8 @@ async function migrateProjectModels(
const colBody: Partial<RollupColumn & Column> = {
title: columnMeta._cn,
rollup_function: columnMeta.rl.fn,
rollup_function: columnMeta.rl
.fn as typeof ROLLUP_FUNCTIONS[number],
};
colBody.fk_rollup_column_id =
@ -883,14 +874,7 @@ async function migrateProjectModelViews(
queryParams;
const insertObj: Partial<
View &
GridView &
KanbanView &
FormView &
GalleryView & {
created_at;
updated_at;
}
View & GridView & KanbanView & FormView & GalleryView
> = {
title: viewData.title,
show: true,
@ -898,8 +882,6 @@ async function migrateProjectModelViews(
fk_model_id: objModelRef[project.id][viewData.parent_model_title].id,
project_id: project.id,
base_id: baseId,
created_at: viewData.created_at,
updated_at: viewData.updated_at,
};
if (viewData.show_as === 'grid') {
@ -1082,8 +1064,6 @@ async function migrateUIAcl(ctx: MigrateCtxV1, ncMeta: any) {
tn: string;
parent_model_title: string;
project_id: string;
created_at?;
updated_at?;
}> = await ncMeta.metaList(null, null, 'nc_disabled_models_for_role');
for (const acl of uiAclList) {
@ -1116,8 +1096,6 @@ async function migrateUIAcl(ctx: MigrateCtxV1, ncMeta: any) {
role: acl.role,
fk_view_id,
disabled: acl.disabled,
created_at: acl.created_at,
updated_at: acl.updated_at,
},
ncMeta
);
@ -1216,8 +1194,6 @@ async function migratePlugins(ncMeta: any) {
creator: plugin.creator,
creator_website: plugin.creator_website,
price: plugin.price,
created_at: plugin.created_at,
updated_at: plugin.updated_at,
});
}
}
@ -1243,8 +1219,6 @@ async function migrateWebhooks(ctx: MigrateCtxV1, ncMeta: any) {
retry_interval: number;
timeout: number;
active: boolean;
created_at?;
updated_at?;
}> = await ncMeta.metaList(null, null, 'nc_hooks');
for (const hookMeta of hooks) {
@ -1274,8 +1248,6 @@ async function migrateWebhooks(ctx: MigrateCtxV1, ncMeta: any) {
retry_interval: hookMeta.retry_interval,
timeout: hookMeta.timeout,
active: hookMeta.active,
created_at: hookMeta.created_at,
updated_at: hookMeta.updated_at,
},
ncMeta
);
@ -1325,8 +1297,6 @@ async function migrateAutitLog(
status: string;
description: string;
details: string;
created_at: any;
updated_at: any;
}> = await ncMeta.metaList(null, null, 'nc_audit');
for (const audit of audits) {
@ -1343,8 +1313,6 @@ async function migrateAutitLog(
status: audit.status,
description: audit.description,
details: audit.details,
created_at: audit.created_at,
updated_at: audit.updated_at,
};
if (audit.model_name) {

45
packages/nocodb/src/run/redoc.ts

@ -0,0 +1,45 @@
import express from 'express';
import path from 'path';
const pageContent = `<!DOCTYPE html>
<html>
<head>
<title>NocoDB API Documentation</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!--
Redoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<img src="https://static.scarf.sh/a.png?x-pxid=c12a77cc-855e-4602-8a0f-614b2d0da56a"/>
<redoc spec-url='./swagger.json'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"></script>
</script>
</body>
</html>`;
const app = express();
app.get('/', function (_req, res) {
res.send(pageContent);
});
app.use(
'/swagger.json',
express.static(path.join(__dirname, '../schema/swagger.json'))
);
app.listen(3001, function () {
console.log(
'Example app listening on port 3001! Go to http://localhost:3001'
);
});

12043
packages/nocodb/src/schema/swagger.json

File diff suppressed because it is too large Load Diff

12
packages/nocodb/tests/unit/factory/project.ts

@ -4,13 +4,13 @@ import Project from '../../../src/lib/models/Project';
const sakilaProjectConfig = (context) => {
let base;
if(context.sakilaDbConfig.client === 'mysql2'){
if (context.sakilaDbConfig.client === 'mysql2') {
base = {
type: context.sakilaDbConfig.client,
config: {
client: context.sakilaDbConfig.client,
connection: context.sakilaDbConfig.connection
}
connection: context.sakilaDbConfig.connection,
},
};
} else {
base = {
@ -26,7 +26,7 @@ const sakilaProjectConfig = (context) => {
}
base = {
...base,
...base,
inflection_column: 'camelize',
inflection_table: 'camelize',
};
@ -35,7 +35,7 @@ const sakilaProjectConfig = (context) => {
title: 'sakila',
bases: [base],
external: true,
}
};
};
const defaultProjectValue = {
@ -44,7 +44,7 @@ const defaultProjectValue = {
const defaultSharedBaseValue = {
roles: 'viewer',
password: 'test',
password: 'password123',
};
const createSharedBase = async (app, token, project, sharedBaseArgs = {}) => {

10
packages/nocodb/tests/unit/rest/tests/project.test.ts

@ -159,7 +159,7 @@ function projectTest() {
.set('xc-auth', context.token)
.send({
roles: 'viewer',
password: 'test',
password: 'password123',
})
.expect(200);
@ -168,7 +168,7 @@ function projectTest() {
if (
!updatedProject.uuid ||
updatedProject.roles !== 'viewer' ||
updatedProject.password !== 'test'
updatedProject.password !== 'password123'
) {
return new Error('Shared base not configured properly');
}
@ -180,7 +180,7 @@ function projectTest() {
.set('xc-auth', context.token)
.send({
roles: 'commenter',
password: 'test',
password: 'password123',
})
.expect(200);
@ -199,7 +199,7 @@ function projectTest() {
.set('xc-auth', context.token)
.send({
roles: 'commenter',
password: 'test',
password: 'password123',
})
.expect(200);
@ -218,7 +218,7 @@ function projectTest() {
.set('xc-auth', context.token)
.send({
roles: 'editor',
password: 'test',
password: 'password123',
})
.expect(200);
const updatedProject = await Project.getByTitleOrId(project.id);

Loading…
Cancel
Save