mirror of https://github.com/nocodb/nocodb
Raju Udava
1 year ago
committed by
GitHub
13 changed files with 1140 additions and 14 deletions
@ -0,0 +1,28 @@ |
|||||||
|
import { getModelPaths } from './templates/paths'; |
||||||
|
import type { Model } from '~/models'; |
||||||
|
import type { SwaggerColumn } from './getSwaggerColumnMetas'; |
||||||
|
import type { SwaggerView } from './getSwaggerJSONV2'; |
||||||
|
import Noco from '~/Noco'; |
||||||
|
|
||||||
|
export default async function getPaths( |
||||||
|
{ |
||||||
|
model, |
||||||
|
columns, |
||||||
|
views, |
||||||
|
}: { |
||||||
|
model: Model; |
||||||
|
columns: SwaggerColumn[]; |
||||||
|
views: SwaggerView[]; |
||||||
|
}, |
||||||
|
_ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
const swaggerPaths = await getModelPaths({ |
||||||
|
tableName: model.title, |
||||||
|
tableId: model.id, |
||||||
|
views, |
||||||
|
type: model.type, |
||||||
|
columns, |
||||||
|
}); |
||||||
|
|
||||||
|
return swaggerPaths; |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
import { getModelSchemas, getViewSchemas } from './templates/schemas'; |
||||||
|
import type { Base, Model } from '~/models'; |
||||||
|
|
||||||
|
import type { SwaggerColumn } from './getSwaggerColumnMetas'; |
||||||
|
import type { SwaggerView } from './getSwaggerJSONV2'; |
||||||
|
import Noco from '~/Noco'; |
||||||
|
|
||||||
|
export default async function getSchemas( |
||||||
|
{ |
||||||
|
base, |
||||||
|
model, |
||||||
|
columns |
||||||
|
}: { |
||||||
|
base: Base; |
||||||
|
model: Model; |
||||||
|
columns: SwaggerColumn[]; |
||||||
|
views: SwaggerView[]; |
||||||
|
}, |
||||||
|
_ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
const swaggerSchemas = getModelSchemas({ |
||||||
|
tableName: model.title, |
||||||
|
orgs: 'v1', |
||||||
|
baseName: base.title, |
||||||
|
columns, |
||||||
|
}); |
||||||
|
|
||||||
|
return swaggerSchemas; |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
import { UITypes } from 'nocodb-sdk'; |
||||||
|
import type { Base, Column, LinkToAnotherRecordColumn } from '~/models'; |
||||||
|
import SwaggerTypes from '~/db/sql-mgr/code/routers/xc-ts/SwaggerTypes'; |
||||||
|
import Noco from '~/Noco'; |
||||||
|
|
||||||
|
export default async ( |
||||||
|
columns: Column[], |
||||||
|
base: Base, |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
): Promise<SwaggerColumn[]> => { |
||||||
|
const dbType = await base.getBases().then((b) => b?.[0]?.type); |
||||||
|
return Promise.all( |
||||||
|
columns.map(async (c) => { |
||||||
|
const field: SwaggerColumn = { |
||||||
|
title: c.title, |
||||||
|
type: 'object', |
||||||
|
virtual: true, |
||||||
|
column: c, |
||||||
|
}; |
||||||
|
|
||||||
|
switch (c.uidt) { |
||||||
|
case UITypes.LinkToAnotherRecord: |
||||||
|
{ |
||||||
|
const colOpt = await c.getColOptions<LinkToAnotherRecordColumn>( |
||||||
|
ncMeta, |
||||||
|
); |
||||||
|
if (colOpt) { |
||||||
|
const relTable = await colOpt.getRelatedTable(ncMeta); |
||||||
|
field.type = undefined; |
||||||
|
field.$ref = `#/components/schemas/${relTable.title}Request`; |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case UITypes.Formula: |
||||||
|
case UITypes.Lookup: |
||||||
|
field.type = 'object'; |
||||||
|
break; |
||||||
|
case UITypes.Rollup: |
||||||
|
case UITypes.Links: |
||||||
|
field.type = 'number'; |
||||||
|
break; |
||||||
|
case UITypes.Attachment: |
||||||
|
field.type = 'array'; |
||||||
|
field.items = { |
||||||
|
$ref: `#/components/schemas/Attachment`, |
||||||
|
}; |
||||||
|
break; |
||||||
|
default: |
||||||
|
field.virtual = false; |
||||||
|
SwaggerTypes.setSwaggerType(c, field, dbType); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
return field; |
||||||
|
}), |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export interface SwaggerColumn { |
||||||
|
type: any; |
||||||
|
title: string; |
||||||
|
description?: string; |
||||||
|
virtual?: boolean; |
||||||
|
$ref?: any; |
||||||
|
column: Column; |
||||||
|
items?: any; |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
import { ViewTypes } from 'nocodb-sdk'; |
||||||
|
import swaggerBase from './swagger-base.json'; |
||||||
|
import getPaths from './getPaths'; |
||||||
|
import getSchemas from './getSchemas'; |
||||||
|
import getSwaggerColumnMetas from './getSwaggerColumnMetas'; |
||||||
|
import type { |
||||||
|
Base, |
||||||
|
FormViewColumn, |
||||||
|
GalleryViewColumn, |
||||||
|
GridViewColumn, |
||||||
|
Model, |
||||||
|
View, |
||||||
|
} from '~/models'; |
||||||
|
import Noco from '~/Noco'; |
||||||
|
|
||||||
|
export default async function getSwaggerJSONV2( |
||||||
|
base: Base, |
||||||
|
models: Model[], |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
// base swagger object
|
||||||
|
const swaggerObj = { |
||||||
|
...swaggerBase, |
||||||
|
paths: {}, |
||||||
|
components: { |
||||||
|
...swaggerBase.components, |
||||||
|
schemas: { ...swaggerBase.components.schemas }, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
// iterate and populate swagger schema and path for models and views
|
||||||
|
for (const model of models) { |
||||||
|
let paths = {}; |
||||||
|
|
||||||
|
const columns = await getSwaggerColumnMetas( |
||||||
|
await model.getColumns(ncMeta), |
||||||
|
base, |
||||||
|
ncMeta, |
||||||
|
); |
||||||
|
|
||||||
|
const views: SwaggerView[] = []; |
||||||
|
|
||||||
|
for (const view of (await model.getViews(false, ncMeta)) || []) { |
||||||
|
if (view.type !== ViewTypes.GRID) continue; |
||||||
|
views.push({ |
||||||
|
view, |
||||||
|
columns: await view.getColumns(ncMeta), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// skip mm tables
|
||||||
|
if (!model.mm) paths = await getPaths({ model, columns, views }, ncMeta); |
||||||
|
|
||||||
|
const schemas = await getSchemas({ base, model, columns, views }, ncMeta); |
||||||
|
|
||||||
|
Object.assign(swaggerObj.paths, paths); |
||||||
|
Object.assign(swaggerObj.components.schemas, schemas); |
||||||
|
} |
||||||
|
|
||||||
|
return swaggerObj; |
||||||
|
} |
||||||
|
|
||||||
|
export interface SwaggerView { |
||||||
|
view: View; |
||||||
|
columns: Array<GridViewColumn | GalleryViewColumn | FormViewColumn>; |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
{ |
||||||
|
"openapi": "3.0.0", |
||||||
|
"info": { |
||||||
|
"title": "nocodb", |
||||||
|
"version": "2.0" |
||||||
|
}, |
||||||
|
"servers": [ |
||||||
|
{ |
||||||
|
"url": "http://localhost:8080" |
||||||
|
} |
||||||
|
], |
||||||
|
"paths": { |
||||||
|
}, |
||||||
|
"components": { |
||||||
|
"schemas": { |
||||||
|
"Paginated": { |
||||||
|
"title": "Paginated", |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"pageSize": { |
||||||
|
"type": "integer" |
||||||
|
}, |
||||||
|
"totalRows": { |
||||||
|
"type": "integer" |
||||||
|
}, |
||||||
|
"isFirstPage": { |
||||||
|
"type": "boolean" |
||||||
|
}, |
||||||
|
"isLastPage": { |
||||||
|
"type": "boolean" |
||||||
|
}, |
||||||
|
"page": { |
||||||
|
"type": "number" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"Attachment": { |
||||||
|
"title": "Attachment", |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"mimetype": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"size": { |
||||||
|
"type": "integer" |
||||||
|
}, |
||||||
|
"title": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"url": { |
||||||
|
"type": "string" |
||||||
|
}, |
||||||
|
"icon": { |
||||||
|
"type": "string" |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"Groupby": { |
||||||
|
"title": "Groupby", |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"count": { |
||||||
|
"type": "number", |
||||||
|
"description": "count" |
||||||
|
}, |
||||||
|
"column_name": { |
||||||
|
"type": "string", |
||||||
|
"description": "the value of the given column" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"securitySchemes": { |
||||||
|
"xcAuth": { |
||||||
|
"type": "apiKey", |
||||||
|
"in": "header", |
||||||
|
"name": "xc-auth", |
||||||
|
"description": "JWT access token" |
||||||
|
}, |
||||||
|
"xcToken": { |
||||||
|
"type": "apiKey", |
||||||
|
"in": "header", |
||||||
|
"name": "xc-token", |
||||||
|
"description": "API token" |
||||||
|
} |
||||||
|
}, |
||||||
|
"responses": { |
||||||
|
"BadRequest": { |
||||||
|
"description": "BadReqeust", |
||||||
|
"content": { |
||||||
|
"application/json": { |
||||||
|
"schema": { |
||||||
|
"type": "object", |
||||||
|
"properties": { |
||||||
|
"msg": { |
||||||
|
"type": "string", |
||||||
|
"x-stoplight": { |
||||||
|
"id": "p9mk4oi0hbihm" |
||||||
|
}, |
||||||
|
"example": "BadRequest [Error]: <ERROR MESSAGE>" |
||||||
|
} |
||||||
|
}, |
||||||
|
"required": [ |
||||||
|
"msg" |
||||||
|
] |
||||||
|
}, |
||||||
|
"examples": { |
||||||
|
"Example 1": { |
||||||
|
"value": { |
||||||
|
"msg": "BadRequest [Error]: <ERROR MESSAGE>" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"headers": {} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
"security": [ |
||||||
|
{ |
||||||
|
"xcAuth": [] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"xcToken": [] |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
export const csvExportResponseHeader = { |
||||||
|
'nc-export-offset': { |
||||||
|
schema: { |
||||||
|
type: 'integer', |
||||||
|
}, |
||||||
|
description: |
||||||
|
'Offset of next set of data which will be helpful if there is large amount of data. It will returns `-1` if all set of data exported.', |
||||||
|
example: '1000', |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,237 @@ |
|||||||
|
import { isLinksOrLTAR, RelationTypes, UITypes } from 'nocodb-sdk'; |
||||||
|
import type { LinkToAnotherRecordColumn } from '~/models'; |
||||||
|
import type { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||||
|
import type { SwaggerView } from '~/services/api-docs/swaggerV2/getSwaggerJSONV2'; |
||||||
|
|
||||||
|
export const recordIdParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
name: 'recordId', |
||||||
|
in: 'path', |
||||||
|
required: true, |
||||||
|
example: 1, |
||||||
|
description: |
||||||
|
'Primary key of the record you want to read. If the table have composite primary key then combine them by using `___` and pass it as primary key.', |
||||||
|
}; |
||||||
|
export const fieldsParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'fields', |
||||||
|
description: |
||||||
|
'Array of field names or comma separated filed names to include in the response objects. In array syntax pass it like `fields[]=field1&fields[]=field2` or alternately `fields=field1,field2`.', |
||||||
|
}; |
||||||
|
export const sortParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'sort', |
||||||
|
description: |
||||||
|
'Comma separated field names to sort rows, rows will sort in ascending order based on provided columns. To sort in descending order provide `-` prefix along with column name, like `-field`. Example : `sort=field1,-field2`', |
||||||
|
}; |
||||||
|
export const whereParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'where', |
||||||
|
description: |
||||||
|
'This can be used for filtering rows, which accepts complicated where conditions. For more info visit [here](https://docs.nocodb.com/developer-resources/rest-apis#comparison-operators). Example : `where=(field1,eq,value)`', |
||||||
|
}; |
||||||
|
export const limitParam = { |
||||||
|
schema: { |
||||||
|
type: 'number', |
||||||
|
minimum: 1, |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'limit', |
||||||
|
description: |
||||||
|
'The `limit` parameter used for pagination, the response collection size depends on limit value with default value `25` and maximum value `1000`, which can be overridden by environment variables `DB_QUERY_LIMIT_DEFAULT` and `DB_QUERY_LIMIT_MAX` respectively.', |
||||||
|
example: 25, |
||||||
|
}; |
||||||
|
export const offsetParam = { |
||||||
|
schema: { |
||||||
|
type: 'number', |
||||||
|
minimum: 0, |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'offset', |
||||||
|
description: |
||||||
|
'The `offset` parameter used for pagination, the value helps to select collection from a certain index.', |
||||||
|
example: 0, |
||||||
|
}; |
||||||
|
|
||||||
|
export const shuffleParam = { |
||||||
|
schema: { |
||||||
|
type: 'number', |
||||||
|
minimum: 0, |
||||||
|
maximum: 1, |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'shuffle', |
||||||
|
description: |
||||||
|
'The `shuffle` parameter used for pagination, the response will be shuffled if it is set to 1.', |
||||||
|
example: 0, |
||||||
|
}; |
||||||
|
|
||||||
|
export const columnNameQueryParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'column_name', |
||||||
|
description: |
||||||
|
'Column name of the column you want to group by, eg. `column_name=column1`', |
||||||
|
}; |
||||||
|
|
||||||
|
export const linkFieldNameParam = (columns: SwaggerColumn[]) => { |
||||||
|
const linkColumnIds = []; |
||||||
|
const description = [ |
||||||
|
'**Links Field Identifier** corresponding to the relation field `Links` established between tables.\n\nLink Columns:', |
||||||
|
]; |
||||||
|
for (const { column } of columns) { |
||||||
|
if (!isLinksOrLTAR(column) || column.system) continue; |
||||||
|
linkColumnIds.push(column.id); |
||||||
|
|
||||||
|
description.push(`* ${column.id} - ${column.title}`); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
enum: linkColumnIds, |
||||||
|
}, |
||||||
|
name: 'linkFieldId', |
||||||
|
in: 'path', |
||||||
|
required: true, |
||||||
|
description: description.join('\n'), |
||||||
|
}; |
||||||
|
}; |
||||||
|
export const viewIdParams = (views: SwaggerView[]) => { |
||||||
|
const viewIds = []; |
||||||
|
const description = [ |
||||||
|
'Allows you to fetch records that are currently visible within a specific view.\n\nViews:', |
||||||
|
]; |
||||||
|
|
||||||
|
for (const { view } of views) { |
||||||
|
viewIds.push(view.id); |
||||||
|
description.push( |
||||||
|
`* ${view.id} - ${view.is_default ? 'Default view' : view.title}`, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
enum: viewIds, |
||||||
|
}, |
||||||
|
description: description.join('\n'), |
||||||
|
name: 'viewId', |
||||||
|
in: 'query', |
||||||
|
required: false, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export const referencedRowIdParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
name: 'refRowId', |
||||||
|
in: 'path', |
||||||
|
required: true, |
||||||
|
}; |
||||||
|
|
||||||
|
export const exportTypeParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
enum: ['csv', 'excel'], |
||||||
|
}, |
||||||
|
name: 'type', |
||||||
|
in: 'path', |
||||||
|
required: true, |
||||||
|
}; |
||||||
|
|
||||||
|
export const csvExportOffsetParam = { |
||||||
|
schema: { |
||||||
|
type: 'number', |
||||||
|
minimum: 0, |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: 'offset', |
||||||
|
description: |
||||||
|
'Helps to start export from a certain index. You can get the next set of data offset from previous response header named `nc-export-offset`.', |
||||||
|
example: 0, |
||||||
|
}; |
||||||
|
|
||||||
|
export const nestedWhereParam = (colName) => ({ |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: `nested[${colName}][where]`, |
||||||
|
description: `This can be used for filtering rows in nested column \`${colName}\`, which accepts complicated where conditions. For more info visit [here](https://docs.nocodb.com/developer-resources/rest-apis#comparison-operators). Example : \`nested[${colName}][where]=(field1,eq,value)\``, |
||||||
|
}); |
||||||
|
|
||||||
|
export const nestedFieldParam = (colName) => ({ |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: `nested[${colName}][fields]`, |
||||||
|
description: `Array of field names or comma separated filed names to include in the in nested column \`${colName}\` result. In array syntax pass it like \`fields[]=field1&fields[]=field2.\`. Example : \`nested[${colName}][fields]=field1,field2\``, |
||||||
|
}); |
||||||
|
export const nestedSortParam = (colName) => ({ |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: `nested[${colName}][sort]`, |
||||||
|
description: `Comma separated field names to sort rows in nested column \`${colName}\` rows, it will sort in ascending order based on provided columns. To sort in descending order provide \`-\` prefix along with column name, like \`-field\`. Example : \`nested[${colName}][sort]=field1,-field2\``, |
||||||
|
}); |
||||||
|
export const nestedLimitParam = (colName) => ({ |
||||||
|
schema: { |
||||||
|
type: 'number', |
||||||
|
minimum: 1, |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: `nested[${colName}][limit]`, |
||||||
|
description: `The \`limit\` parameter used for pagination of nested \`${colName}\` rows, the response collection size depends on limit value and default value is \`25\`.`, |
||||||
|
example: '25', |
||||||
|
}); |
||||||
|
export const nestedOffsetParam = (colName) => ({ |
||||||
|
schema: { |
||||||
|
type: 'number', |
||||||
|
minimum: 0, |
||||||
|
}, |
||||||
|
in: 'query', |
||||||
|
name: `nested[${colName}][offset]`, |
||||||
|
description: `The \`offset\` parameter used for pagination of nested \`${colName}\` rows, the value helps to select collection from a certain index.`, |
||||||
|
example: 0, |
||||||
|
}); |
||||||
|
|
||||||
|
export const getNestedParams = async ( |
||||||
|
columns: SwaggerColumn[], |
||||||
|
): Promise<any[]> => { |
||||||
|
return await columns.reduce(async (paramsArr, { column }) => { |
||||||
|
if (column.uidt === UITypes.LinkToAnotherRecord && !column.system) { |
||||||
|
const colOpt = await column.getColOptions<LinkToAnotherRecordColumn>(); |
||||||
|
if (colOpt.type !== RelationTypes.BELONGS_TO) { |
||||||
|
return [ |
||||||
|
...(await paramsArr), |
||||||
|
nestedWhereParam(column.title), |
||||||
|
nestedOffsetParam(column.title), |
||||||
|
nestedLimitParam(column.title), |
||||||
|
nestedFieldParam(column.title), |
||||||
|
nestedSortParam(column.title), |
||||||
|
]; |
||||||
|
} else { |
||||||
|
return [...(await paramsArr), nestedFieldParam(column.title)]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return paramsArr; |
||||||
|
}, Promise.resolve([])); |
||||||
|
}; |
@ -0,0 +1,407 @@ |
|||||||
|
import { ModelTypes } from 'nocodb-sdk'; |
||||||
|
import { |
||||||
|
fieldsParam, |
||||||
|
getNestedParams, |
||||||
|
limitParam, |
||||||
|
linkFieldNameParam, |
||||||
|
offsetParam, |
||||||
|
recordIdParam, |
||||||
|
shuffleParam, |
||||||
|
sortParam, |
||||||
|
viewIdParams, |
||||||
|
whereParam, |
||||||
|
} from './params'; |
||||||
|
import type { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||||
|
import type { SwaggerView } from '~/services/api-docs/swaggerV2/getSwaggerJSONV2'; |
||||||
|
import { isRelationExist } from '~/services/api-docs/swagger/templates/paths'; |
||||||
|
|
||||||
|
export const getModelPaths = async (ctx: { |
||||||
|
tableName: string; |
||||||
|
type: ModelTypes; |
||||||
|
columns: SwaggerColumn[]; |
||||||
|
tableId: string; |
||||||
|
views: SwaggerView[]; |
||||||
|
}): Promise<{ [path: string]: any }> => ({ |
||||||
|
[`/api/v2/tables/${ctx.tableId}/records`]: { |
||||||
|
get: { |
||||||
|
summary: `${ctx.tableName} list`, |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-db-table-row-list`, |
||||||
|
description: `List of all rows from ${ctx.tableName} ${ctx.type} and response data fields can be filtered based on query params.`, |
||||||
|
tags: [ctx.tableName], |
||||||
|
parameters: [ |
||||||
|
viewIdParams(ctx.views), |
||||||
|
fieldsParam, |
||||||
|
sortParam, |
||||||
|
whereParam, |
||||||
|
limitParam, |
||||||
|
shuffleParam, |
||||||
|
offsetParam, |
||||||
|
...(await getNestedParams(ctx.columns)), |
||||||
|
], |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: getPaginatedResponseType(`${ctx.tableName}Response`), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
...(ctx.type === ModelTypes.TABLE |
||||||
|
? { |
||||||
|
post: { |
||||||
|
summary: `${ctx.tableName} create`, |
||||||
|
description: |
||||||
|
'Insert a new row in table by providing a key value pair object where key refers to the column alias. All the required fields should be included with payload excluding `autoincrement` and column with default value.', |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-create`, |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}Response`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
'400': { |
||||||
|
$ref: '#/components/responses/BadRequest', |
||||||
|
}, |
||||||
|
}, |
||||||
|
tags: [ctx.tableName], |
||||||
|
requestBody: { |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
oneOf: [ |
||||||
|
{ |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}Request`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'array', |
||||||
|
items: { |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}Request`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
patch: { |
||||||
|
summary: `${ctx.tableName} update`, |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-update`, |
||||||
|
description: |
||||||
|
'Partial update row in table by providing a key value pair object where key refers to the column alias. You need to only include columns which you want to update.', |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: {}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
'400': { |
||||||
|
$ref: '#/components/responses/BadRequest', |
||||||
|
}, |
||||||
|
}, |
||||||
|
tags: [ctx.tableName], |
||||||
|
requestBody: { |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
oneOf: [ |
||||||
|
{ |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}Request`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'array', |
||||||
|
items: { |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}Request`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
delete: { |
||||||
|
summary: `${ctx.tableName} delete`, |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-delete`, |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
}, |
||||||
|
}, |
||||||
|
tags: [ctx.tableName], |
||||||
|
description: |
||||||
|
'Delete a row by using the **primary key** column value.', |
||||||
|
requestBody: { |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
oneOf: [ |
||||||
|
{ |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}IdRequest`, |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'array', |
||||||
|
items: { |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}IdRequest`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
: {}), |
||||||
|
}, |
||||||
|
[`/api/v2/tables/${ctx.tableId}/records/{recordId}`]: { |
||||||
|
get: { |
||||||
|
parameters: [recordIdParam, fieldsParam], |
||||||
|
summary: `${ctx.tableName} read`, |
||||||
|
description: 'Read a row data by using the **primary key** column value.', |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-read`, |
||||||
|
tags: [ctx.tableName], |
||||||
|
responses: { |
||||||
|
'201': { |
||||||
|
description: 'Created', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
$ref: `#/components/schemas/${ctx.tableName}Response`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
[`/api/v2/tables/${ctx.tableId}/records/count`]: { |
||||||
|
parameters: [viewIdParams(ctx.views)], |
||||||
|
get: { |
||||||
|
summary: `${ctx.tableName} count`, |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-count`, |
||||||
|
description: 'Get rows count of a table by applying optional filters.', |
||||||
|
tags: [ctx.tableName], |
||||||
|
parameters: [whereParam], |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
type: 'object', |
||||||
|
properties: { |
||||||
|
count: { |
||||||
|
type: 'number', |
||||||
|
}, |
||||||
|
}, |
||||||
|
required: ['list', 'pageInfo'], |
||||||
|
}, |
||||||
|
examples: { |
||||||
|
'Example 1': { |
||||||
|
value: { |
||||||
|
count: 3, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
'400': { |
||||||
|
$ref: '#/components/responses/BadRequest', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
...(isRelationExist(ctx.columns) |
||||||
|
? { |
||||||
|
[`/api/v2/tables/${ctx.tableId}/links/{linkFieldId}/records/{recordId}`]: |
||||||
|
{ |
||||||
|
parameters: [linkFieldNameParam(ctx.columns), recordIdParam], |
||||||
|
get: { |
||||||
|
summary: 'Link Records list', |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-nested-list`, |
||||||
|
description: |
||||||
|
'This API endpoint allows you to retrieve list of linked records for a specific `Link field` and `Record ID`. The response is an array of objects containing Primary Key and its corresponding display value.', |
||||||
|
tags: [ctx.tableName], |
||||||
|
parameters: [ |
||||||
|
fieldsParam, |
||||||
|
sortParam, |
||||||
|
whereParam, |
||||||
|
limitParam, |
||||||
|
offsetParam, |
||||||
|
], |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
type: 'object', |
||||||
|
properties: { |
||||||
|
list: { |
||||||
|
type: 'array', |
||||||
|
description: 'List of data objects', |
||||||
|
items: { |
||||||
|
type: 'object', |
||||||
|
}, |
||||||
|
}, |
||||||
|
pageInfo: { |
||||||
|
$ref: '#/components/schemas/Paginated', |
||||||
|
description: 'Paginated Info', |
||||||
|
}, |
||||||
|
}, |
||||||
|
required: ['list', 'pageInfo'], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
'400': { |
||||||
|
$ref: '#/components/responses/BadRequest', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
post: { |
||||||
|
summary: 'Link Records', |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-nested-link`, |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: {}, |
||||||
|
examples: { |
||||||
|
'Example 1': { |
||||||
|
value: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
'400': { |
||||||
|
$ref: '#/components/responses/BadRequest', |
||||||
|
}, |
||||||
|
}, |
||||||
|
tags: [ctx.tableName], |
||||||
|
requestBody: { |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
oneOf: [ |
||||||
|
{ |
||||||
|
type: 'object', |
||||||
|
}, |
||||||
|
{ |
||||||
|
type: 'array', |
||||||
|
items: { |
||||||
|
type: 'object', |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
examples: { |
||||||
|
'Example 1': { |
||||||
|
value: [ |
||||||
|
{ |
||||||
|
Id: 4, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Id: 5, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
description: |
||||||
|
'This API endpoint allows you to link records to a specific `Link field` and `Record ID`. The request payload is an array of record-ids from the adjacent table for linking purposes. Note that any existing links, if present, will be unaffected during this operation.', |
||||||
|
parameters: [recordIdParam], |
||||||
|
}, |
||||||
|
delete: { |
||||||
|
summary: 'Unlink Records', |
||||||
|
operationId: `${ctx.tableName.toLowerCase()}-nested-unlink`, |
||||||
|
responses: { |
||||||
|
'200': { |
||||||
|
description: 'OK', |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: {}, |
||||||
|
examples: { |
||||||
|
'Example 1': { |
||||||
|
value: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
'400': { |
||||||
|
$ref: '#/components/responses/BadRequest', |
||||||
|
}, |
||||||
|
}, |
||||||
|
tags: [ctx.tableName], |
||||||
|
requestBody: { |
||||||
|
content: { |
||||||
|
'application/json': { |
||||||
|
schema: { |
||||||
|
oneOf: [ |
||||||
|
{ |
||||||
|
type: 'array', |
||||||
|
items: { |
||||||
|
type: 'object', |
||||||
|
}, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
examples: { |
||||||
|
'Example 1': { |
||||||
|
value: [ |
||||||
|
{ |
||||||
|
Id: 1, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Id: 2, |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
description: |
||||||
|
'This API endpoint allows you to unlink records from a specific `Link field` and `Record ID`. The request payload is an array of record-ids from the adjacent table for unlinking purposes. Note that, \n- duplicated record-ids will be ignored.\n- non-existent record-ids will be ignored.', |
||||||
|
parameters: [recordIdParam], |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
: {}), |
||||||
|
}); |
||||||
|
|
||||||
|
function getPaginatedResponseType(type: string) { |
||||||
|
return { |
||||||
|
type: 'object', |
||||||
|
properties: { |
||||||
|
list: { |
||||||
|
type: 'array', |
||||||
|
items: { |
||||||
|
$ref: `#/components/schemas/${type}`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
PageInfo: { |
||||||
|
$ref: `#/components/schemas/Paginated`, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
import { isSystemColumn } from 'nocodb-sdk'; |
||||||
|
import type { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||||
|
|
||||||
|
export const getModelSchemas = (ctx: { |
||||||
|
tableName: string; |
||||||
|
orgs: string; |
||||||
|
baseName: string; |
||||||
|
columns: Array<SwaggerColumn>; |
||||||
|
}) => ({ |
||||||
|
[`${ctx.tableName}Response`]: { |
||||||
|
title: `${ctx.tableName} Response`, |
||||||
|
type: 'object', |
||||||
|
description: '', |
||||||
|
'x-internal': false, |
||||||
|
properties: { |
||||||
|
...(ctx.columns?.reduce( |
||||||
|
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||||
|
...colsObj, |
||||||
|
...(column.system |
||||||
|
? {} |
||||||
|
: { |
||||||
|
[title]: fieldProps, |
||||||
|
}), |
||||||
|
}), |
||||||
|
{}, |
||||||
|
) || {}), |
||||||
|
}, |
||||||
|
}, |
||||||
|
[`${ctx.tableName}Request`]: { |
||||||
|
title: `${ctx.tableName} Request`, |
||||||
|
type: 'object', |
||||||
|
description: '', |
||||||
|
'x-internal': false, |
||||||
|
properties: { |
||||||
|
...(ctx.columns?.reduce( |
||||||
|
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||||
|
...colsObj, |
||||||
|
...(virtual || isSystemColumn(column) || column.ai || column.meta?.ag |
||||||
|
? {} |
||||||
|
: { |
||||||
|
[title]: fieldProps, |
||||||
|
}), |
||||||
|
}), |
||||||
|
{}, |
||||||
|
) || {}), |
||||||
|
}, |
||||||
|
}, |
||||||
|
[`${ctx.tableName}IdRequest`]: { |
||||||
|
title: `${ctx.tableName} Id Request`, |
||||||
|
type: 'object', |
||||||
|
description: '', |
||||||
|
'x-internal': false, |
||||||
|
properties: { |
||||||
|
...(ctx.columns?.reduce( |
||||||
|
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||||
|
...colsObj, |
||||||
|
...(column.pk |
||||||
|
? { |
||||||
|
[title]: fieldProps, |
||||||
|
} |
||||||
|
: {}), |
||||||
|
}), |
||||||
|
{}, |
||||||
|
) || {}), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
export const getViewSchemas = (ctx: { |
||||||
|
tableName: string; |
||||||
|
viewName: string; |
||||||
|
orgs: string; |
||||||
|
baseName: string; |
||||||
|
columns: Array<SwaggerColumn>; |
||||||
|
}) => ({ |
||||||
|
[`${ctx.tableName}${ctx.viewName}GridResponse`]: { |
||||||
|
title: `${ctx.tableName} : ${ctx.viewName} Response`, |
||||||
|
type: 'object', |
||||||
|
description: '', |
||||||
|
'x-internal': false, |
||||||
|
properties: { |
||||||
|
...(ctx.columns?.reduce( |
||||||
|
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||||
|
...colsObj, |
||||||
|
[title]: fieldProps, |
||||||
|
}), |
||||||
|
{}, |
||||||
|
) || {}), |
||||||
|
}, |
||||||
|
}, |
||||||
|
[`${ctx.tableName}${ctx.viewName}GridRequest`]: { |
||||||
|
title: `${ctx.tableName} : ${ctx.viewName} Request`, |
||||||
|
type: 'object', |
||||||
|
description: '', |
||||||
|
'x-internal': false, |
||||||
|
properties: { |
||||||
|
...(ctx.columns?.reduce( |
||||||
|
(colsObj, { title, virtual, column, ...fieldProps }) => ({ |
||||||
|
...colsObj, |
||||||
|
...(virtual |
||||||
|
? {} |
||||||
|
: { |
||||||
|
[title]: fieldProps, |
||||||
|
}), |
||||||
|
}), |
||||||
|
{}, |
||||||
|
) || {}), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
Loading…
Reference in new issue