mirror of https://github.com/nocodb/nocodb
Pranav C
2 years ago
21 changed files with 707 additions and 460 deletions
@ -1,48 +0,0 @@ |
|||||||
import Noco from '../../../Noco'; |
|
||||||
import Model from '../../../models/Model'; |
|
||||||
import Project from '../../../models/Project'; |
|
||||||
import { getModelPaths, getViewPaths } from './templates/paths'; |
|
||||||
import { SwaggerColumn } from './getSwaggerColumnMetas'; |
|
||||||
import { SwaggerView } from './getSwaggerJSON'; |
|
||||||
|
|
||||||
export default async function getPaths( |
|
||||||
{ |
|
||||||
project, |
|
||||||
model, |
|
||||||
columns, |
|
||||||
views, |
|
||||||
}: { |
|
||||||
project: Project; |
|
||||||
model: Model; |
|
||||||
columns: SwaggerColumn[]; |
|
||||||
views: SwaggerView[]; |
|
||||||
}, |
|
||||||
_ncMeta = Noco.ncMeta |
|
||||||
) { |
|
||||||
const swaggerPaths = await getModelPaths({ |
|
||||||
tableName: model.title, |
|
||||||
type: model.type, |
|
||||||
orgs: 'v1', |
|
||||||
columns, |
|
||||||
projectName: project.title, |
|
||||||
}); |
|
||||||
|
|
||||||
for (const { view, columns: viewColumns } of views) { |
|
||||||
const swaggerColumns = columns.filter( |
|
||||||
(c) => viewColumns.find((vc) => vc.fk_column_id === c.column.id)?.show |
|
||||||
); |
|
||||||
Object.assign( |
|
||||||
swaggerPaths, |
|
||||||
await getViewPaths({ |
|
||||||
tableName: model.title, |
|
||||||
viewName: view.title, |
|
||||||
type: model.type, |
|
||||||
orgs: 'v1', |
|
||||||
columns: swaggerColumns, |
|
||||||
projectName: project.title, |
|
||||||
}) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return swaggerPaths; |
|
||||||
} |
|
@ -1,46 +0,0 @@ |
|||||||
import Noco from '../../../Noco'; |
|
||||||
import Model from '../../../models/Model'; |
|
||||||
import Project from '../../../models/Project'; |
|
||||||
import { getModelSchemas, getViewSchemas } from './templates/schemas'; |
|
||||||
import { SwaggerColumn } from './getSwaggerColumnMetas'; |
|
||||||
import { SwaggerView } from './getSwaggerJSON'; |
|
||||||
|
|
||||||
export default async function getSchemas( |
|
||||||
{ |
|
||||||
project, |
|
||||||
model, |
|
||||||
columns, |
|
||||||
views, |
|
||||||
}: { |
|
||||||
project: Project; |
|
||||||
model: Model; |
|
||||||
columns: SwaggerColumn[]; |
|
||||||
views: SwaggerView[]; |
|
||||||
}, |
|
||||||
_ncMeta = Noco.ncMeta |
|
||||||
) { |
|
||||||
const swaggerSchemas = getModelSchemas({ |
|
||||||
tableName: model.title, |
|
||||||
orgs: 'v1', |
|
||||||
projectName: project.title, |
|
||||||
columns, |
|
||||||
}); |
|
||||||
|
|
||||||
for (const { view, columns: viewColumns } of views) { |
|
||||||
const swaggerColumns = columns.filter( |
|
||||||
(c) => viewColumns.find((vc) => vc.fk_column_id === c.column.id)?.show |
|
||||||
); |
|
||||||
Object.assign( |
|
||||||
swaggerSchemas, |
|
||||||
getViewSchemas({ |
|
||||||
tableName: model.title, |
|
||||||
viewName: view.title, |
|
||||||
orgs: 'v1', |
|
||||||
columns: swaggerColumns, |
|
||||||
projectName: project.title, |
|
||||||
}) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return swaggerSchemas; |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
import { UITypes } from 'nocodb-sdk'; |
|
||||||
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn'; |
|
||||||
import SwaggerTypes from '../../../db/sql-mgr/code/routers/xc-ts/SwaggerTypes'; |
|
||||||
import Column from '../../../models/Column'; |
|
||||||
import Noco from '../../../Noco'; |
|
||||||
import Project from '../../../models/Project'; |
|
||||||
|
|
||||||
export default async ( |
|
||||||
columns: Column[], |
|
||||||
project: Project, |
|
||||||
ncMeta = Noco.ncMeta |
|
||||||
): Promise<SwaggerColumn[]> => { |
|
||||||
const dbType = await project.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: |
|
||||||
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; |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
import FormViewColumn from '../../../models/FormViewColumn'; |
|
||||||
import GalleryViewColumn from '../../../models/GalleryViewColumn'; |
|
||||||
import Noco from '../../../Noco'; |
|
||||||
import Model from '../../../models/Model'; |
|
||||||
import swaggerBase from './swagger-base.json'; |
|
||||||
import getPaths from './getPaths'; |
|
||||||
import getSchemas from './getSchemas'; |
|
||||||
import Project from '../../../models/Project'; |
|
||||||
import getSwaggerColumnMetas from './getSwaggerColumnMetas'; |
|
||||||
import { ViewTypes } from 'nocodb-sdk'; |
|
||||||
import GridViewColumn from '../../../models/GridViewColumn'; |
|
||||||
import View from '../../../models/View'; |
|
||||||
|
|
||||||
export default async function getSwaggerJSON( |
|
||||||
project: Project, |
|
||||||
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), |
|
||||||
project, |
|
||||||
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({ project, model, columns, views }, ncMeta); |
|
||||||
|
|
||||||
const schemas = await getSchemas( |
|
||||||
{ project, 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>; |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
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', |
|
||||||
}, |
|
||||||
}; |
|
@ -1,217 +0,0 @@ |
|||||||
import { SwaggerColumn } from '../getSwaggerColumnMetas'; |
|
||||||
import { RelationTypes, UITypes } from 'nocodb-sdk'; |
|
||||||
import LinkToAnotherRecordColumn from '../../../../models/LinkToAnotherRecordColumn'; |
|
||||||
|
|
||||||
export const rowIdParam = { |
|
||||||
schema: { |
|
||||||
type: 'string', |
|
||||||
}, |
|
||||||
name: 'rowId', |
|
||||||
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 relationTypeParam = { |
|
||||||
schema: { |
|
||||||
type: 'string', |
|
||||||
enum: ['mm', 'hm'], |
|
||||||
}, |
|
||||||
name: 'relationType', |
|
||||||
in: 'path', |
|
||||||
required: true, |
|
||||||
}; |
|
||||||
|
|
||||||
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 columnNameParam = (columns: SwaggerColumn[]) => { |
|
||||||
const columnNames = []; |
|
||||||
for (const { column } of columns) { |
|
||||||
if (column.uidt !== UITypes.LinkToAnotherRecord || column.system) continue; |
|
||||||
columnNames.push(column.title); |
|
||||||
} |
|
||||||
|
|
||||||
return { |
|
||||||
schema: { |
|
||||||
type: 'string', |
|
||||||
enum: columnNames, |
|
||||||
}, |
|
||||||
name: 'columnName', |
|
||||||
in: 'path', |
|
||||||
required: true, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
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) { |
|
||||||
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,36 @@ |
|||||||
|
import { Router } from 'express' |
||||||
|
import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw' |
||||||
|
import getSwaggerHtml from './swaggerHtml' |
||||||
|
import getRedocHtml from './redocHtml' |
||||||
|
import { swaggerService } from '../../services' |
||||||
|
|
||||||
|
async function swaggerJson(req, res) { |
||||||
|
const swagger = await swaggerService.swaggerJson({ |
||||||
|
projectId: req.params.projectId, |
||||||
|
siteUrl: req.ncSiteUrl, |
||||||
|
}) |
||||||
|
|
||||||
|
res.json(swagger) |
||||||
|
} |
||||||
|
|
||||||
|
function swaggerHtml(_, res) { |
||||||
|
res.send(getSwaggerHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })) |
||||||
|
} |
||||||
|
|
||||||
|
function redocHtml(_, res) { |
||||||
|
res.send(getRedocHtml({ ncSiteUrl: process.env.NC_PUBLIC_URL || '' })) |
||||||
|
} |
||||||
|
|
||||||
|
const router = Router({ mergeParams: true }) |
||||||
|
|
||||||
|
// todo: auth
|
||||||
|
router.get( |
||||||
|
'/api/v1/db/meta/projects/:projectId/swagger.json', |
||||||
|
ncMetaAclMw(swaggerJson, 'swaggerJson'), |
||||||
|
) |
||||||
|
|
||||||
|
router.get('/api/v1/db/meta/projects/:projectId/swagger', swaggerHtml) |
||||||
|
|
||||||
|
router.get('/api/v1/db/meta/projects/:projectId/redoc', redocHtml) |
||||||
|
|
||||||
|
export default router |
@ -0,0 +1,93 @@ |
|||||||
|
export default ({ |
||||||
|
ncSiteUrl, |
||||||
|
}: { |
||||||
|
ncSiteUrl: string; |
||||||
|
}): string => `<!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="${ncSiteUrl}/css/fonts.montserrat.css" rel="stylesheet"> |
||||||
|
<!-- |
||||||
|
Redoc doesn't change outer page styles |
||||||
|
--> |
||||||
|
<style> |
||||||
|
body { |
||||||
|
margin: 0; |
||||||
|
padding: 0; |
||||||
|
} |
||||||
|
</style> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="redoc"></div> |
||||||
|
<script src="${ncSiteUrl}/js/redoc.standalone.min.js"></script> |
||||||
|
<script> |
||||||
|
let initialLocalStorage = {} |
||||||
|
|
||||||
|
try { |
||||||
|
initialLocalStorage = JSON.parse(localStorage.getItem('nocodb-gui-v2') || '{}'); |
||||||
|
} catch (e) { |
||||||
|
console.error('Failed to parse local storage', e); |
||||||
|
} |
||||||
|
|
||||||
|
const xhttp = new XMLHttpRequest(); |
||||||
|
|
||||||
|
xhttp.open("GET", "./swagger.json"); |
||||||
|
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); |
||||||
|
xhttp.setRequestHeader("xc-auth", initialLocalStorage && initialLocalStorage.token); |
||||||
|
|
||||||
|
xhttp.onload = function () { |
||||||
|
const swaggerJson = this.responseText; |
||||||
|
const swagger = JSON.parse(swaggerJson); |
||||||
|
Redoc.init(swagger, { |
||||||
|
scrollYOffset: 50 |
||||||
|
}, document.getElementById('redoc')) |
||||||
|
}; |
||||||
|
|
||||||
|
xhttp.send(); |
||||||
|
</script> |
||||||
|
<script> |
||||||
|
console.log('%c🚀 We are Hiring!!! 🚀%c\\n%cJoin the forces http://careers.nocodb.com', 'color:#1348ba;font-size:3rem;padding:20px;', 'display:none', 'font-size:1.5rem;padding:20px') |
||||||
|
const linkEl = document.createElement('a') |
||||||
|
linkEl.setAttribute('href', "http://careers.nocodb.com") |
||||||
|
linkEl.setAttribute('target', '_blank') |
||||||
|
linkEl.setAttribute('class', 'we-are-hiring') |
||||||
|
linkEl.innerHTML = '🚀 We are Hiring!!! 🚀' |
||||||
|
const styleEl = document.createElement('style'); |
||||||
|
styleEl.innerHTML = \` |
||||||
|
.we-are-hiring { |
||||||
|
position: fixed; |
||||||
|
bottom: 50px; |
||||||
|
right: -250px; |
||||||
|
opacity: 0; |
||||||
|
background: orange; |
||||||
|
border-radius: 4px; |
||||||
|
padding: 19px; |
||||||
|
z-index: 200; |
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase; |
||||||
|
color: black; |
||||||
|
transition: 1s opacity, 1s right; |
||||||
|
display: block; |
||||||
|
font-weight: bold; |
||||||
|
}
|
||||||
|
|
||||||
|
.we-are-hiring.active { |
||||||
|
opacity: 1; |
||||||
|
right:25px; |
||||||
|
} |
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) { |
||||||
|
.we-are-hiring { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
} |
||||||
|
\` |
||||||
|
document.body.appendChild(linkEl, document.body.firstChild) |
||||||
|
document.body.appendChild(styleEl, document.body.firstChild) |
||||||
|
setTimeout(() => linkEl.classList.add('active'), 2000) |
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html>`;
|
@ -0,0 +1,87 @@ |
|||||||
|
export default ({ |
||||||
|
ncSiteUrl, |
||||||
|
}: { |
||||||
|
ncSiteUrl: string; |
||||||
|
}): string => `<!DOCTYPE html>
|
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>NocoDB : API Docs</title> |
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> |
||||||
|
<link rel="shortcut icon" href="${ncSiteUrl}/favicon.ico" /> |
||||||
|
<link rel="stylesheet" href="${ncSiteUrl}/css/swagger-ui-bundle.4.5.2.min.css"/> |
||||||
|
<script src="${ncSiteUrl}/js/swagger-ui-bundle.4.5.2.min.js"></script> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<div id="app"></div> |
||||||
|
<script> |
||||||
|
|
||||||
|
let initialLocalStorage = {} |
||||||
|
|
||||||
|
try { |
||||||
|
initialLocalStorage = JSON.parse(localStorage.getItem('nocodb-gui-v2') || '{}'); |
||||||
|
} catch (e) { |
||||||
|
console.error('Failed to parse local storage', e); |
||||||
|
} |
||||||
|
|
||||||
|
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
|
||||||
|
xmlhttp.open("GET", "./swagger.json"); |
||||||
|
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); |
||||||
|
xmlhttp.setRequestHeader("xc-auth", initialLocalStorage && initialLocalStorage.token); |
||||||
|
xmlhttp.onload = function () { |
||||||
|
|
||||||
|
const ui = SwaggerUIBundle({ |
||||||
|
// url: ,
|
||||||
|
spec: JSON.parse(xmlhttp.responseText), |
||||||
|
dom_id: '#app', |
||||||
|
presets: [ |
||||||
|
SwaggerUIBundle.presets.apis, |
||||||
|
SwaggerUIBundle.SwaggerUIStandalonePreset |
||||||
|
], |
||||||
|
}) |
||||||
|
} |
||||||
|
xmlhttp.send(); |
||||||
|
|
||||||
|
|
||||||
|
console.log('%c🚀 We are Hiring!!! 🚀%c\\n%cJoin the forces http://careers.nocodb.com', 'color:#1348ba;font-size:3rem;padding:20px;', 'display:none', 'font-size:1.5rem;padding:20px'); |
||||||
|
const linkEl = document.createElement('a') |
||||||
|
linkEl.setAttribute('href', "http://careers.nocodb.com") |
||||||
|
linkEl.setAttribute('target', '_blank') |
||||||
|
linkEl.setAttribute('class', 'we-are-hiring') |
||||||
|
linkEl.innerHTML = '🚀 We are Hiring!!! 🚀' |
||||||
|
const styleEl = document.createElement('style'); |
||||||
|
styleEl.innerHTML = \` |
||||||
|
.we-are-hiring { |
||||||
|
position: fixed; |
||||||
|
bottom: 50px; |
||||||
|
right: -250px; |
||||||
|
opacity: 0; |
||||||
|
background: orange; |
||||||
|
border-radius: 4px; |
||||||
|
padding: 19px; |
||||||
|
z-index: 200; |
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase; |
||||||
|
color: black; |
||||||
|
transition: 1s opacity, 1s right; |
||||||
|
display: block; |
||||||
|
font-weight: bold; |
||||||
|
}
|
||||||
|
|
||||||
|
.we-are-hiring.active { |
||||||
|
opacity: 1; |
||||||
|
right:25px; |
||||||
|
} |
||||||
|
|
||||||
|
@media only screen and (max-width: 600px) { |
||||||
|
.we-are-hiring { |
||||||
|
display: none; |
||||||
|
} |
||||||
|
} |
||||||
|
\` |
||||||
|
document.body.appendChild(linkEl, document.body.firstChild) |
||||||
|
document.body.appendChild(styleEl, document.body.firstChild) |
||||||
|
setTimeout(() => linkEl.classList.add('active'), 2000) |
||||||
|
</script> |
||||||
|
</body> |
||||||
|
</html> |
||||||
|
`;
|
@ -0,0 +1,48 @@ |
|||||||
|
import Noco from '../../Noco'; |
||||||
|
import Model from '../../models/Model'; |
||||||
|
import Project from '../../models/Project'; |
||||||
|
import { getModelPaths, getViewPaths } from './templates/paths'; |
||||||
|
import { SwaggerColumn } from './getSwaggerColumnMetas'; |
||||||
|
import { SwaggerView } from './getSwaggerJSON'; |
||||||
|
|
||||||
|
export default async function getPaths( |
||||||
|
{ |
||||||
|
project, |
||||||
|
model, |
||||||
|
columns, |
||||||
|
views, |
||||||
|
}: { |
||||||
|
project: Project; |
||||||
|
model: Model; |
||||||
|
columns: SwaggerColumn[]; |
||||||
|
views: SwaggerView[]; |
||||||
|
}, |
||||||
|
_ncMeta = Noco.ncMeta |
||||||
|
) { |
||||||
|
const swaggerPaths = await getModelPaths({ |
||||||
|
tableName: model.title, |
||||||
|
type: model.type, |
||||||
|
orgs: 'v1', |
||||||
|
columns, |
||||||
|
projectName: project.title, |
||||||
|
}); |
||||||
|
|
||||||
|
for (const { view, columns: viewColumns } of views) { |
||||||
|
const swaggerColumns = columns.filter( |
||||||
|
(c) => viewColumns.find((vc) => vc.fk_column_id === c.column.id)?.show |
||||||
|
); |
||||||
|
Object.assign( |
||||||
|
swaggerPaths, |
||||||
|
await getViewPaths({ |
||||||
|
tableName: model.title, |
||||||
|
viewName: view.title, |
||||||
|
type: model.type, |
||||||
|
orgs: 'v1', |
||||||
|
columns: swaggerColumns, |
||||||
|
projectName: project.title, |
||||||
|
}) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return swaggerPaths; |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
import Noco from '../../Noco'; |
||||||
|
import Model from '../../models/Model'; |
||||||
|
import Project from '../../models/Project'; |
||||||
|
import { getModelSchemas, getViewSchemas } from './templates/schemas'; |
||||||
|
import { SwaggerColumn } from './getSwaggerColumnMetas'; |
||||||
|
import { SwaggerView } from './getSwaggerJSON'; |
||||||
|
|
||||||
|
export default async function getSchemas( |
||||||
|
{ |
||||||
|
project, |
||||||
|
model, |
||||||
|
columns, |
||||||
|
views, |
||||||
|
}: { |
||||||
|
project: Project; |
||||||
|
model: Model; |
||||||
|
columns: SwaggerColumn[]; |
||||||
|
views: SwaggerView[]; |
||||||
|
}, |
||||||
|
_ncMeta = Noco.ncMeta |
||||||
|
) { |
||||||
|
const swaggerSchemas = getModelSchemas({ |
||||||
|
tableName: model.title, |
||||||
|
orgs: 'v1', |
||||||
|
projectName: project.title, |
||||||
|
columns, |
||||||
|
}); |
||||||
|
|
||||||
|
for (const { view, columns: viewColumns } of views) { |
||||||
|
const swaggerColumns = columns.filter( |
||||||
|
(c) => viewColumns.find((vc) => vc.fk_column_id === c.column.id)?.show |
||||||
|
); |
||||||
|
Object.assign( |
||||||
|
swaggerSchemas, |
||||||
|
getViewSchemas({ |
||||||
|
tableName: model.title, |
||||||
|
viewName: view.title, |
||||||
|
orgs: 'v1', |
||||||
|
columns: swaggerColumns, |
||||||
|
projectName: project.title, |
||||||
|
}) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return swaggerSchemas; |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
import { UITypes } from 'nocodb-sdk'; |
||||||
|
import LinkToAnotherRecordColumn from '../../models/LinkToAnotherRecordColumn'; |
||||||
|
import SwaggerTypes from '../../db/sql-mgr/code/routers/xc-ts/SwaggerTypes'; |
||||||
|
import Column from '../../models/Column'; |
||||||
|
import Noco from '../../Noco'; |
||||||
|
import Project from '../../models/Project'; |
||||||
|
|
||||||
|
export default async ( |
||||||
|
columns: Column[], |
||||||
|
project: Project, |
||||||
|
ncMeta = Noco.ncMeta |
||||||
|
): Promise<SwaggerColumn[]> => { |
||||||
|
const dbType = await project.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: |
||||||
|
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 { Model, Project, View } from '../../models' |
||||||
|
import FormViewColumn from '../../models/FormViewColumn'; |
||||||
|
import GalleryViewColumn from '../../models/GalleryViewColumn'; |
||||||
|
import Noco from '../../Noco'; |
||||||
|
import swaggerBase from './swagger-base.json'; |
||||||
|
import getPaths from './getPaths'; |
||||||
|
import getSchemas from './getSchemas'; |
||||||
|
import getSwaggerColumnMetas from './getSwaggerColumnMetas'; |
||||||
|
import { ViewTypes } from 'nocodb-sdk'; |
||||||
|
import GridViewColumn from '../../models/GridViewColumn'; |
||||||
|
|
||||||
|
export default async function getSwaggerJSON( |
||||||
|
project: Project, |
||||||
|
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), |
||||||
|
project, |
||||||
|
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({ project, model, columns, views }, ncMeta); |
||||||
|
|
||||||
|
const schemas = await getSchemas( |
||||||
|
{ project, 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,34 @@ |
|||||||
|
import { NcError } from '../../meta/helpers/catchError' |
||||||
|
import Model from '../../models/Model' |
||||||
|
import Project from '../../models/Project' |
||||||
|
import getSwaggerJSON from './getSwaggerJSON' |
||||||
|
|
||||||
|
export async function swaggerJson(param:{projectId:string; siteUrl:string}){ |
||||||
|
const project = await Project.get(param.projectId); |
||||||
|
|
||||||
|
if (!project) NcError.notFound(); |
||||||
|
|
||||||
|
const models = await Model.list({ |
||||||
|
project_id: param.projectId, |
||||||
|
base_id: null, |
||||||
|
}); |
||||||
|
|
||||||
|
const swagger = await getSwaggerJSON(project, models); |
||||||
|
|
||||||
|
swagger.servers = [ |
||||||
|
{ |
||||||
|
url: param.siteUrl, |
||||||
|
}, |
||||||
|
{ |
||||||
|
url: '{customUrl}', |
||||||
|
variables: { |
||||||
|
customUrl: { |
||||||
|
default: param.siteUrl, |
||||||
|
description: 'Provide custom nocodb app base url', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
] as any; |
||||||
|
|
||||||
|
return swagger; |
||||||
|
} |
@ -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,217 @@ |
|||||||
|
import { SwaggerColumn } from '../getSwaggerColumnMetas'; |
||||||
|
import { RelationTypes, UITypes } from 'nocodb-sdk'; |
||||||
|
import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn'; |
||||||
|
|
||||||
|
export const rowIdParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
}, |
||||||
|
name: 'rowId', |
||||||
|
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 relationTypeParam = { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
enum: ['mm', 'hm'], |
||||||
|
}, |
||||||
|
name: 'relationType', |
||||||
|
in: 'path', |
||||||
|
required: true, |
||||||
|
}; |
||||||
|
|
||||||
|
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 columnNameParam = (columns: SwaggerColumn[]) => { |
||||||
|
const columnNames = []; |
||||||
|
for (const { column } of columns) { |
||||||
|
if (column.uidt !== UITypes.LinkToAnotherRecord || column.system) continue; |
||||||
|
columnNames.push(column.title); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
schema: { |
||||||
|
type: 'string', |
||||||
|
enum: columnNames, |
||||||
|
}, |
||||||
|
name: 'columnName', |
||||||
|
in: 'path', |
||||||
|
required: true, |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
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) { |
||||||
|
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([])); |
||||||
|
}; |
Loading…
Reference in new issue