@budibase/server 0.9.190-alpha.0 → 0.9.190-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builder/assets/{index.7d405e74.css → index.4fb3f887.css} +1 -1
- package/builder/assets/{index.43f4c913.js → index.92596167.js} +171 -171
- package/builder/index.html +2 -2
- package/coverage/clover.xml +381 -390
- package/coverage/coverage-final.json +23 -23
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +62 -80
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +1 -1
- package/coverage/lcov-report/api/controllers/datasource.js.html +15 -15
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +29 -29
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +9 -9
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query.js.html +34 -28
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +27 -27
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/screen.js.html +2 -2
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +14 -14
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/internal.js.html +8 -20
- package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +5 -5
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/application.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/row.js.html +536 -2
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/static.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +436 -7
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- package/coverage/lcov-report/automations/index.html +1 -1
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +1 -1
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/automations/utils.js.html +5 -5
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +1 -1
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +2 -2
- package/coverage/lcov-report/db/client.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +2 -2
- package/coverage/lcov-report/db/utils.js.html +19 -19
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +25 -25
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/authorized.js.html +9 -9
- package/coverage/lcov-report/middleware/builder.js.html +7 -7
- package/coverage/lcov-report/middleware/currentapp.js.html +28 -28
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +7 -7
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +2 -2
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +1 -1
- package/coverage/lcov-report/threads/index.js.html +1 -1
- package/coverage/lcov-report/threads/query.js.html +1 -1
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +13 -13
- package/coverage/lcov-report/utilities/index.html +1 -1
- package/coverage/lcov-report/utilities/index.js.html +3 -3
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +2 -2
- package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +645 -658
- package/dist/api/controllers/application.js +1 -5
- package/dist/api/controllers/query.js +2 -4
- package/dist/api/controllers/row/ExternalRequest.js +46 -22
- package/dist/api/controllers/table/internal.js +0 -4
- package/dist/api/routes/row.js +178 -0
- package/dist/api/routes/table.js +148 -5
- package/dist/definitions/datasource.js +12 -1
- package/dist/integrations/base/sql.js +30 -15
- package/dist/integrations/index.js +1 -1
- package/dist/integrations/mysql.js +2 -1
- package/dist/integrations/oracle.js +42 -20
- package/dist/integrations/utils.js +1 -1
- package/dist/package.json +10 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -7
- package/scripts/docs/README.md +31 -0
- package/scripts/docs/generate.js +74 -0
- package/scripts/docs/toSwagger.js +320 -0
- package/scripts/integrations/oracle/docker-compose.yml +1 -1
- package/src/api/controllers/application.js +0 -6
- package/src/api/controllers/query.js +6 -4
- package/src/api/controllers/row/ExternalRequest.ts +52 -22
- package/src/api/controllers/table/internal.js +0 -4
- package/src/api/routes/row.js +178 -0
- package/src/api/routes/table.js +148 -5
- package/src/api/routes/tests/application.spec.js +0 -2
- package/src/api/routes/tests/query.spec.js +38 -8
- package/src/api/routes/tests/screen.spec.js +1 -1
- package/src/definitions/datasource.ts +11 -0
- package/src/integrations/base/sql.ts +31 -14
- package/src/integrations/index.ts +2 -2
- package/src/integrations/mysql.ts +3 -1
- package/src/integrations/oracle.ts +43 -24
- package/src/integrations/utils.ts +6 -2
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "0.9.190-alpha.
|
|
4
|
+
"version": "0.9.190-alpha.12",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
|
-
"main": "src/index.
|
|
6
|
+
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/Budibase/budibase.git"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"test:watch": "jest --watch",
|
|
16
16
|
"predocker": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client",
|
|
17
17
|
"build:docker": "yarn run predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION",
|
|
18
|
+
"build:docs": "node ./scripts/docs/generate.js open",
|
|
18
19
|
"run:docker": "node dist/index.js",
|
|
19
20
|
"dev:stack:up": "node scripts/dev/manage.js up",
|
|
20
21
|
"dev:stack:down": "node scripts/dev/manage.js down",
|
|
@@ -68,9 +69,9 @@
|
|
|
68
69
|
"author": "Budibase",
|
|
69
70
|
"license": "GPL-3.0",
|
|
70
71
|
"dependencies": {
|
|
71
|
-
"@budibase/auth": "^0.9.190-alpha.
|
|
72
|
-
"@budibase/client": "^0.9.190-alpha.
|
|
73
|
-
"@budibase/string-templates": "^0.9.190-alpha.
|
|
72
|
+
"@budibase/auth": "^0.9.190-alpha.12",
|
|
73
|
+
"@budibase/client": "^0.9.190-alpha.12",
|
|
74
|
+
"@budibase/string-templates": "^0.9.190-alpha.12",
|
|
74
75
|
"@bull-board/api": "^3.7.0",
|
|
75
76
|
"@bull-board/koa": "^3.7.0",
|
|
76
77
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -106,7 +107,7 @@
|
|
|
106
107
|
"mssql": "6.2.3",
|
|
107
108
|
"mysql2": "^2.3.1",
|
|
108
109
|
"node-fetch": "2.6.0",
|
|
109
|
-
"open": "
|
|
110
|
+
"open": "^8.4.0",
|
|
110
111
|
"pg": "8.5.1",
|
|
111
112
|
"pino-pretty": "4.0.0",
|
|
112
113
|
"posthog-node": "^1.1.4",
|
|
@@ -130,6 +131,7 @@
|
|
|
130
131
|
"@babel/preset-env": "^7.14.4",
|
|
131
132
|
"@budibase/standard-components": "^0.9.139",
|
|
132
133
|
"@jest/test-sequencer": "^24.8.0",
|
|
134
|
+
"@types/apidoc": "^0.50.0",
|
|
133
135
|
"@types/bull": "^3.15.1",
|
|
134
136
|
"@types/jest": "^26.0.23",
|
|
135
137
|
"@types/koa": "^2.13.3",
|
|
@@ -137,12 +139,14 @@
|
|
|
137
139
|
"@types/node": "^15.12.4",
|
|
138
140
|
"@types/oracledb": "^5.2.1",
|
|
139
141
|
"@typescript-eslint/parser": "4.28.0",
|
|
142
|
+
"apidoc": "^0.50.2",
|
|
140
143
|
"babel-jest": "^27.0.2",
|
|
141
144
|
"copyfiles": "^2.4.1",
|
|
142
145
|
"docker-compose": "^0.23.6",
|
|
143
146
|
"eslint": "^6.8.0",
|
|
144
147
|
"jest": "^27.0.5",
|
|
145
148
|
"nodemon": "^2.0.4",
|
|
149
|
+
"path-to-regexp": "^6.2.0",
|
|
146
150
|
"prettier": "^2.3.1",
|
|
147
151
|
"rimraf": "^3.0.2",
|
|
148
152
|
"supertest": "^4.0.2",
|
|
@@ -154,5 +158,5 @@
|
|
|
154
158
|
"optionalDependencies": {
|
|
155
159
|
"oracledb": "^5.3.0"
|
|
156
160
|
},
|
|
157
|
-
"gitHead": "
|
|
161
|
+
"gitHead": "4e607feda0a258f486df6fe138940b7faab1db99"
|
|
158
162
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
### Documentation
|
|
2
|
+
|
|
3
|
+
This directory contains the scripts required to generate the APIDoc based documentation.
|
|
4
|
+
You can find the docs about comment structure at the [APIDocs page](https://apidocjs.com/).
|
|
5
|
+
|
|
6
|
+
In general most API endpoints will look like:
|
|
7
|
+
```js
|
|
8
|
+
/**
|
|
9
|
+
* @api {post} /api/:param/url Give it a name
|
|
10
|
+
* @apiName Give it a name
|
|
11
|
+
* @apiGroup group
|
|
12
|
+
* @apiPermission permission
|
|
13
|
+
* @apiDescription Describe what the endpoint does, any special cases the user
|
|
14
|
+
* should be aware of.
|
|
15
|
+
*
|
|
16
|
+
* @apiParam {string} param describe a URL parameter.
|
|
17
|
+
*
|
|
18
|
+
* @apiParam (Body) input describe a field on the body.
|
|
19
|
+
*
|
|
20
|
+
* @apiSuccess {object} output describe the output.
|
|
21
|
+
*/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
There are a few key points to note when writing API docs:
|
|
25
|
+
1. Don't use `@apiBody` - this isn't currently supported by our swagger converter.
|
|
26
|
+
2. Make sure to always have an `@api` definition at the start, which must always have the
|
|
27
|
+
HTTP verb, the endpoint URL and the name.
|
|
28
|
+
3. There are three ways you can specify parameters used as inputs for your endpoint,
|
|
29
|
+
`@apiParam` for a URL param, `@apiParam (Body)` for a field on the request body and `@apiParam (Query)`
|
|
30
|
+
for query string parameters.
|
|
31
|
+
4. The `@apiGroup` should be the same for all API Doc comments in a route file.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const fs = require("fs")
|
|
2
|
+
const { join } = require("path")
|
|
3
|
+
const { createDoc } = require("apidoc")
|
|
4
|
+
const packageJson = require("../../package.json")
|
|
5
|
+
const toSwagger = require("./toSwagger")
|
|
6
|
+
const open = require("open")
|
|
7
|
+
|
|
8
|
+
const config = {
|
|
9
|
+
name: "Budibase API",
|
|
10
|
+
version: packageJson.version,
|
|
11
|
+
description: "Documenting the Budibase backend API",
|
|
12
|
+
title: "Budibase app service API",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const shouldOpen = process.argv[2]
|
|
16
|
+
const disallowed = []
|
|
17
|
+
|
|
18
|
+
function filter(parsedRouteFiles) {
|
|
19
|
+
const tagToSearch = "url"
|
|
20
|
+
for (let routeFile of parsedRouteFiles) {
|
|
21
|
+
for (let route of routeFile) {
|
|
22
|
+
let routeInfo = route["local"]
|
|
23
|
+
if (disallowed.includes(routeInfo[tagToSearch])) {
|
|
24
|
+
const idx = routeFile.indexOf(route)
|
|
25
|
+
routeFile.splice(idx, 1)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function generate() {
|
|
32
|
+
// start by writing a config file
|
|
33
|
+
const configPath = join(__dirname, "config.json")
|
|
34
|
+
fs.writeFileSync(configPath, JSON.stringify(config))
|
|
35
|
+
const mainPath = join(__dirname, "..", "..")
|
|
36
|
+
const srcPath = join(mainPath, "src", "api", "routes")
|
|
37
|
+
const assetsPath = join(mainPath, "builder", "assets", "docs")
|
|
38
|
+
if (!fs.existsSync(assetsPath)) {
|
|
39
|
+
fs.mkdirSync(assetsPath, { recursive: true })
|
|
40
|
+
}
|
|
41
|
+
const options = {
|
|
42
|
+
src: [srcPath],
|
|
43
|
+
dest: assetsPath,
|
|
44
|
+
filters: {
|
|
45
|
+
main: {
|
|
46
|
+
postFilter: filter,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
config: configPath,
|
|
50
|
+
}
|
|
51
|
+
const doc = createDoc(options)
|
|
52
|
+
if (typeof doc !== "boolean") {
|
|
53
|
+
const swagger = toSwagger(JSON.parse(doc.data), JSON.parse(doc.project))
|
|
54
|
+
fs.writeFileSync(join(assetsPath, "swagger.json"), JSON.stringify(swagger))
|
|
55
|
+
fs.writeFileSync(join(assetsPath, "apidoc.json"), doc.data)
|
|
56
|
+
fs.writeFileSync(join(assetsPath, "project.json"), doc.project)
|
|
57
|
+
console.log(
|
|
58
|
+
`Docs generated successfully, find in ${assetsPath}, swagger.json, apidoc.json and project.json`
|
|
59
|
+
)
|
|
60
|
+
} else {
|
|
61
|
+
throw "Unable to generate docs."
|
|
62
|
+
}
|
|
63
|
+
// delete the temporary config file
|
|
64
|
+
fs.unlinkSync(configPath)
|
|
65
|
+
setTimeout(async () => {
|
|
66
|
+
if (shouldOpen === "open") {
|
|
67
|
+
await open(join(assetsPath, "index.html"), { wait: false })
|
|
68
|
+
}
|
|
69
|
+
}, 2000)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
generate().catch(err => {
|
|
73
|
+
console.error(err)
|
|
74
|
+
})
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
let _ = require("lodash")
|
|
2
|
+
let { pathToRegexp } = require("path-to-regexp")
|
|
3
|
+
|
|
4
|
+
/********************************************************
|
|
5
|
+
* Based on: https://github.com/fsbahman/apidoc-swagger *
|
|
6
|
+
********************************************************/
|
|
7
|
+
|
|
8
|
+
let swagger = {
|
|
9
|
+
swagger: "2.0",
|
|
10
|
+
info: {},
|
|
11
|
+
paths: {},
|
|
12
|
+
definitions: {},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function toSwagger(apidocJson, projectJson) {
|
|
16
|
+
swagger.info = addInfo(projectJson)
|
|
17
|
+
swagger.paths = extractPaths(apidocJson)
|
|
18
|
+
return swagger
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let tagsRegex = /(<([^>]+)>)/gi
|
|
22
|
+
// Removes <p> </p> tags from text
|
|
23
|
+
function removeTags(text) {
|
|
24
|
+
return text ? text.replace(tagsRegex, "") : text
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function addInfo(projectJson) {
|
|
28
|
+
let info = {}
|
|
29
|
+
info["title"] = projectJson.title || projectJson.name
|
|
30
|
+
info["version"] = projectJson.version
|
|
31
|
+
info["description"] = projectJson.description
|
|
32
|
+
return info
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Extracts paths provided in json format
|
|
37
|
+
* post, patch, put request parameters are extracted in body
|
|
38
|
+
* get and delete are extracted to path parameters
|
|
39
|
+
* @param apidocJson
|
|
40
|
+
* @returns {{}}
|
|
41
|
+
*/
|
|
42
|
+
function extractPaths(apidocJson) {
|
|
43
|
+
let apiPaths = groupByUrl(apidocJson)
|
|
44
|
+
let paths = {}
|
|
45
|
+
for (let i = 0; i < apiPaths.length; i++) {
|
|
46
|
+
let verbs = apiPaths[i].verbs
|
|
47
|
+
let url = verbs[0].url
|
|
48
|
+
let pattern = pathToRegexp(url, null)
|
|
49
|
+
let matches = pattern.exec(url)
|
|
50
|
+
|
|
51
|
+
// Surrounds URL parameters with curly brackets -> :email with {email}
|
|
52
|
+
let pathKeys = []
|
|
53
|
+
for (let j = 1; j < matches.length; j++) {
|
|
54
|
+
let key = matches[j].substr(1)
|
|
55
|
+
url = url.replace(matches[j], "{" + key + "}")
|
|
56
|
+
pathKeys.push(key)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (let j = 0; j < verbs.length; j++) {
|
|
60
|
+
let verb = verbs[j]
|
|
61
|
+
let type = verb.type
|
|
62
|
+
|
|
63
|
+
let obj = (paths[url] = paths[url] || {})
|
|
64
|
+
|
|
65
|
+
if (type === "post" || type === "patch" || type === "put") {
|
|
66
|
+
_.extend(
|
|
67
|
+
obj,
|
|
68
|
+
createPostPushPutOutput(verb, swagger.definitions, pathKeys)
|
|
69
|
+
)
|
|
70
|
+
} else {
|
|
71
|
+
_.extend(obj, createGetDeleteOutput(verb, swagger.definitions))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return paths
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function createPostPushPutOutput(verbs, definitions, pathKeys) {
|
|
79
|
+
let pathItemObject = {}
|
|
80
|
+
let verbDefinitionResult = createVerbDefinitions(verbs, definitions)
|
|
81
|
+
|
|
82
|
+
let params = []
|
|
83
|
+
let pathParams = createPathParameters(verbs, pathKeys)
|
|
84
|
+
pathParams = _.filter(pathParams, function (param) {
|
|
85
|
+
let hasKey = pathKeys.indexOf(param.name) !== -1
|
|
86
|
+
return !(param.in === "path" && !hasKey)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
params = params.concat(pathParams)
|
|
90
|
+
let required =
|
|
91
|
+
verbs.parameter &&
|
|
92
|
+
verbs.parameter.fields &&
|
|
93
|
+
verbs.parameter.fields.Parameter &&
|
|
94
|
+
verbs.parameter.fields.Parameter.length > 0
|
|
95
|
+
|
|
96
|
+
params.push({
|
|
97
|
+
in: "body",
|
|
98
|
+
name: "body",
|
|
99
|
+
description: removeTags(verbs.description),
|
|
100
|
+
required: required,
|
|
101
|
+
schema: {
|
|
102
|
+
$ref: "#/definitions/" + verbDefinitionResult.topLevelParametersRef,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
pathItemObject[verbs.type] = {
|
|
107
|
+
tags: [verbs.group],
|
|
108
|
+
summary: removeTags(verbs.description),
|
|
109
|
+
consumes: ["application/json"],
|
|
110
|
+
produces: ["application/json"],
|
|
111
|
+
parameters: params,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (verbDefinitionResult.topLevelSuccessRef) {
|
|
115
|
+
pathItemObject[verbs.type].responses = {
|
|
116
|
+
200: {
|
|
117
|
+
description: "successful operation",
|
|
118
|
+
schema: {
|
|
119
|
+
type: verbDefinitionResult.topLevelSuccessRefType,
|
|
120
|
+
items: {
|
|
121
|
+
$ref: "#/definitions/" + verbDefinitionResult.topLevelSuccessRef,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return pathItemObject
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function createVerbDefinitions(verbs, definitions) {
|
|
132
|
+
let result = {
|
|
133
|
+
topLevelParametersRef: null,
|
|
134
|
+
topLevelSuccessRef: null,
|
|
135
|
+
topLevelSuccessRefType: null,
|
|
136
|
+
}
|
|
137
|
+
let defaultObjectName = verbs.name
|
|
138
|
+
|
|
139
|
+
let fieldArrayResult = {}
|
|
140
|
+
if (verbs && verbs.parameter && verbs.parameter.fields) {
|
|
141
|
+
fieldArrayResult = createFieldArrayDefinitions(
|
|
142
|
+
verbs.parameter.fields.Parameter,
|
|
143
|
+
definitions,
|
|
144
|
+
verbs.name,
|
|
145
|
+
defaultObjectName
|
|
146
|
+
)
|
|
147
|
+
result.topLevelParametersRef = fieldArrayResult.topLevelRef
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (verbs && verbs.success && verbs.success.fields) {
|
|
151
|
+
fieldArrayResult = createFieldArrayDefinitions(
|
|
152
|
+
verbs.success.fields["Success 200"],
|
|
153
|
+
definitions,
|
|
154
|
+
verbs.name,
|
|
155
|
+
defaultObjectName
|
|
156
|
+
)
|
|
157
|
+
result.topLevelSuccessRef = fieldArrayResult.topLevelRef
|
|
158
|
+
result.topLevelSuccessRefType = fieldArrayResult.topLevelRefType
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return result
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function createFieldArrayDefinitions(
|
|
165
|
+
fieldArray,
|
|
166
|
+
definitions,
|
|
167
|
+
topLevelRef,
|
|
168
|
+
defaultObjectName
|
|
169
|
+
) {
|
|
170
|
+
let result = {
|
|
171
|
+
topLevelRef: topLevelRef,
|
|
172
|
+
topLevelRefType: null,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!fieldArray) {
|
|
176
|
+
return result
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (let i = 0; i < fieldArray.length; i++) {
|
|
180
|
+
let parameter = fieldArray[i]
|
|
181
|
+
|
|
182
|
+
let nestedName = createNestedName(parameter.field)
|
|
183
|
+
let objectName = nestedName.objectName
|
|
184
|
+
if (!objectName) {
|
|
185
|
+
objectName = defaultObjectName
|
|
186
|
+
}
|
|
187
|
+
let type = parameter.type
|
|
188
|
+
if (i === 0) {
|
|
189
|
+
result.topLevelRefType = type
|
|
190
|
+
if (parameter.type === "Object") {
|
|
191
|
+
objectName = nestedName.propertyName
|
|
192
|
+
nestedName.propertyName = null
|
|
193
|
+
} else if (parameter.type === "Array") {
|
|
194
|
+
objectName = nestedName.propertyName
|
|
195
|
+
nestedName.propertyName = null
|
|
196
|
+
result.topLevelRefType = "array"
|
|
197
|
+
}
|
|
198
|
+
result.topLevelRef = objectName
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
definitions[objectName] = definitions[objectName] || {
|
|
202
|
+
properties: {},
|
|
203
|
+
required: [],
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (nestedName.propertyName) {
|
|
207
|
+
let prop = {
|
|
208
|
+
type: (parameter.type || "").toLowerCase(),
|
|
209
|
+
description: removeTags(parameter.description),
|
|
210
|
+
}
|
|
211
|
+
if (parameter.type === "Object") {
|
|
212
|
+
prop.$ref = "#/definitions/" + parameter.field
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let typeIndex = type.indexOf("[]")
|
|
216
|
+
if (typeIndex !== -1 && typeIndex === type.length - 2) {
|
|
217
|
+
prop.type = "array"
|
|
218
|
+
prop.items = {
|
|
219
|
+
type: type.slice(0, type.length - 2),
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
definitions[objectName]["properties"][nestedName.propertyName] = prop
|
|
224
|
+
if (!parameter.optional) {
|
|
225
|
+
let arr = definitions[objectName]["required"]
|
|
226
|
+
if (arr.indexOf(nestedName.propertyName) === -1) {
|
|
227
|
+
arr.push(nestedName.propertyName)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return result
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function createNestedName(field) {
|
|
237
|
+
let propertyName = field
|
|
238
|
+
let objectName
|
|
239
|
+
let propertyNames = field.split(".")
|
|
240
|
+
if (propertyNames && propertyNames.length > 1) {
|
|
241
|
+
propertyName = propertyNames[propertyNames.length - 1]
|
|
242
|
+
propertyNames.pop()
|
|
243
|
+
objectName = propertyNames.join(".")
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
propertyName: propertyName,
|
|
248
|
+
objectName: objectName,
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Generate get, delete method output
|
|
254
|
+
* @param verbs
|
|
255
|
+
* @param definitions
|
|
256
|
+
* @returns {{}}
|
|
257
|
+
*/
|
|
258
|
+
function createGetDeleteOutput(verbs, definitions) {
|
|
259
|
+
let pathItemObject = {}
|
|
260
|
+
verbs.type = verbs.type === "del" ? "delete" : verbs.type
|
|
261
|
+
|
|
262
|
+
let verbDefinitionResult = createVerbDefinitions(verbs, definitions)
|
|
263
|
+
pathItemObject[verbs.type] = {
|
|
264
|
+
tags: [verbs.group],
|
|
265
|
+
summary: removeTags(verbs.description),
|
|
266
|
+
consumes: ["application/json"],
|
|
267
|
+
produces: ["application/json"],
|
|
268
|
+
parameters: createPathParameters(verbs),
|
|
269
|
+
}
|
|
270
|
+
if (verbDefinitionResult.topLevelSuccessRef) {
|
|
271
|
+
pathItemObject[verbs.type].responses = {
|
|
272
|
+
200: {
|
|
273
|
+
description: "successful operation",
|
|
274
|
+
schema: {
|
|
275
|
+
type: verbDefinitionResult.topLevelSuccessRefType,
|
|
276
|
+
items: {
|
|
277
|
+
$ref: "#/definitions/" + verbDefinitionResult.topLevelSuccessRef,
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return pathItemObject
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Iterate through all method parameters and create array of parameter objects which are stored as path parameters
|
|
288
|
+
* @param verbs
|
|
289
|
+
* @returns {Array}
|
|
290
|
+
*/
|
|
291
|
+
function createPathParameters(verbs) {
|
|
292
|
+
let pathItemObject = []
|
|
293
|
+
if (verbs.parameter && verbs.parameter.fields.Parameter) {
|
|
294
|
+
for (let i = 0; i < verbs.parameter.fields.Parameter.length; i++) {
|
|
295
|
+
let param = verbs.parameter.fields.Parameter[i]
|
|
296
|
+
let field = param.field
|
|
297
|
+
let type = param.type
|
|
298
|
+
pathItemObject.push({
|
|
299
|
+
name: field,
|
|
300
|
+
in: type === "file" ? "formData" : "path",
|
|
301
|
+
required: !param.optional,
|
|
302
|
+
type: param.type.toLowerCase(),
|
|
303
|
+
description: removeTags(param.description),
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return pathItemObject
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function groupByUrl(apidocJson) {
|
|
311
|
+
return _.chain(apidocJson)
|
|
312
|
+
.groupBy("url")
|
|
313
|
+
.toPairs()
|
|
314
|
+
.map(function (element) {
|
|
315
|
+
return _.zipObject(["url", "verbs"], element)
|
|
316
|
+
})
|
|
317
|
+
.value()
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
module.exports = toSwagger
|
|
@@ -15,14 +15,12 @@ const {
|
|
|
15
15
|
generateAppID,
|
|
16
16
|
getLayoutParams,
|
|
17
17
|
getScreenParams,
|
|
18
|
-
generateScreenID,
|
|
19
18
|
generateDevAppID,
|
|
20
19
|
DocumentTypes,
|
|
21
20
|
AppStatus,
|
|
22
21
|
} = require("../../db/utils")
|
|
23
22
|
const { BUILTIN_ROLE_IDS, AccessController } = require("@budibase/auth/roles")
|
|
24
23
|
const { BASE_LAYOUTS } = require("../../constants/layouts")
|
|
25
|
-
const { createHomeScreen } = require("../../constants/screens")
|
|
26
24
|
const { cloneDeep } = require("lodash/fp")
|
|
27
25
|
const { processObject } = require("@budibase/string-templates")
|
|
28
26
|
const {
|
|
@@ -409,9 +407,5 @@ const createEmptyAppPackage = async (ctx, app) => {
|
|
|
409
407
|
screensAndLayouts.push(await processObject(cloned, app))
|
|
410
408
|
}
|
|
411
409
|
|
|
412
|
-
const homeScreen = createHomeScreen(app)
|
|
413
|
-
homeScreen._id = generateScreenID()
|
|
414
|
-
screensAndLayouts.push(homeScreen)
|
|
415
|
-
|
|
416
410
|
await db.bulkDocs(screensAndLayouts)
|
|
417
411
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const { processString } = require("@budibase/string-templates")
|
|
2
2
|
const CouchDB = require("../../db")
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
generateQueryID,
|
|
5
|
+
getQueryParams,
|
|
6
|
+
isProdAppID,
|
|
7
|
+
} = require("../../db/utils")
|
|
4
8
|
const { BaseQueryVerbs } = require("../../constants")
|
|
5
|
-
const env = require("../../environment")
|
|
6
9
|
const { Thread, ThreadType } = require("../../threads")
|
|
7
10
|
|
|
8
11
|
const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
|
|
@@ -90,10 +93,9 @@ exports.find = async function (ctx) {
|
|
|
90
93
|
const db = new CouchDB(ctx.appId)
|
|
91
94
|
const query = enrichQueries(await db.get(ctx.params.queryId))
|
|
92
95
|
// remove properties that could be dangerous in real app
|
|
93
|
-
if (
|
|
96
|
+
if (isProdAppID(ctx.appId)) {
|
|
94
97
|
delete query.fields
|
|
95
98
|
delete query.parameters
|
|
96
|
-
delete query.schema
|
|
97
99
|
}
|
|
98
100
|
ctx.body = query
|
|
99
101
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
FilterTypes,
|
|
2
3
|
IncludeRelationships,
|
|
3
4
|
Operation,
|
|
4
5
|
PaginationJson,
|
|
@@ -118,8 +119,13 @@ module External {
|
|
|
118
119
|
}
|
|
119
120
|
// check the row and filters to make sure they aren't a key of some sort
|
|
120
121
|
if (config.filters) {
|
|
121
|
-
for (let filter of Object.
|
|
122
|
-
|
|
122
|
+
for (let [key, filter] of Object.entries(config.filters)) {
|
|
123
|
+
// oneOf is an array, don't iterate it
|
|
124
|
+
if (
|
|
125
|
+
typeof filter !== "object" ||
|
|
126
|
+
Object.keys(filter).length === 0 ||
|
|
127
|
+
key === FilterTypes.ONE_OF
|
|
128
|
+
) {
|
|
123
129
|
continue
|
|
124
130
|
}
|
|
125
131
|
iterateObject(filter)
|
|
@@ -197,9 +203,9 @@ module External {
|
|
|
197
203
|
return row
|
|
198
204
|
}
|
|
199
205
|
|
|
200
|
-
function
|
|
206
|
+
function isOneSide(field: FieldSchema) {
|
|
201
207
|
return (
|
|
202
|
-
field.relationshipType && field.relationshipType.split("-")[0] === "
|
|
208
|
+
field.relationshipType && field.relationshipType.split("-")[0] === "one"
|
|
203
209
|
)
|
|
204
210
|
}
|
|
205
211
|
|
|
@@ -274,25 +280,37 @@ module External {
|
|
|
274
280
|
const linkTable = this.tables[linkTableName]
|
|
275
281
|
// @ts-ignore
|
|
276
282
|
const linkTablePrimary = linkTable.primary[0]
|
|
277
|
-
|
|
283
|
+
// one to many
|
|
284
|
+
if (isOneSide(field)) {
|
|
278
285
|
newRow[field.foreignKey || linkTablePrimary] = breakRowIdField(
|
|
279
286
|
row[key][0]
|
|
280
287
|
)[0]
|
|
281
|
-
}
|
|
288
|
+
}
|
|
289
|
+
// many to many
|
|
290
|
+
else if (field.through) {
|
|
282
291
|
// we're not inserting a doc, will be a bunch of update calls
|
|
283
|
-
const
|
|
284
|
-
const thisKey: string =
|
|
285
|
-
? "id"
|
|
286
|
-
: field.throughTo || linkTablePrimary
|
|
287
|
-
// @ts-ignore
|
|
288
|
-
const otherKey: string = isUpdate
|
|
289
|
-
? field.fieldName
|
|
290
|
-
: field.throughFrom || tablePrimary
|
|
292
|
+
const otherKey: string = field.throughFrom || linkTablePrimary
|
|
293
|
+
const thisKey: string = field.throughTo || tablePrimary
|
|
291
294
|
row[key].map((relationship: any) => {
|
|
292
|
-
// we don't really support composite keys for relationships, this is why [0] is used
|
|
293
295
|
manyRelationships.push({
|
|
294
296
|
tableId: field.through || field.tableId,
|
|
295
|
-
isUpdate,
|
|
297
|
+
isUpdate: false,
|
|
298
|
+
key: otherKey,
|
|
299
|
+
[otherKey]: breakRowIdField(relationship)[0],
|
|
300
|
+
// leave the ID for enrichment later
|
|
301
|
+
[thisKey]: `{{ literal ${tablePrimary} }}`,
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
// many to one
|
|
306
|
+
else {
|
|
307
|
+
const thisKey: string = "id"
|
|
308
|
+
// @ts-ignore
|
|
309
|
+
const otherKey: string = field.fieldName
|
|
310
|
+
row[key].map((relationship: any) => {
|
|
311
|
+
manyRelationships.push({
|
|
312
|
+
tableId: field.tableId,
|
|
313
|
+
isUpdate: true,
|
|
296
314
|
key: otherKey,
|
|
297
315
|
[thisKey]: breakRowIdField(relationship)[0],
|
|
298
316
|
// leave the ID for enrichment later
|
|
@@ -425,8 +443,8 @@ module External {
|
|
|
425
443
|
)
|
|
426
444
|
definition.through = throughTableName
|
|
427
445
|
// don't support composite keys for relationships
|
|
428
|
-
definition.from = field.
|
|
429
|
-
definition.to = field.
|
|
446
|
+
definition.from = field.throughTo || table.primary[0]
|
|
447
|
+
definition.to = field.throughFrom || linkTable.primary[0]
|
|
430
448
|
definition.fromPrimary = table.primary[0]
|
|
431
449
|
definition.toPrimary = linkTable.primary[0]
|
|
432
450
|
}
|
|
@@ -448,24 +466,36 @@ module External {
|
|
|
448
466
|
// make a new request to get the row with all its relationships
|
|
449
467
|
// we need this to work out if any relationships need removed
|
|
450
468
|
for (let field of Object.values(table.schema)) {
|
|
451
|
-
if (
|
|
469
|
+
if (
|
|
470
|
+
field.type !== FieldTypes.LINK ||
|
|
471
|
+
!field.fieldName ||
|
|
472
|
+
isOneSide(field)
|
|
473
|
+
) {
|
|
452
474
|
continue
|
|
453
475
|
}
|
|
454
476
|
const isMany = field.relationshipType === RelationshipTypes.MANY_TO_MANY
|
|
455
477
|
const tableId = isMany ? field.through : field.tableId
|
|
456
|
-
const
|
|
478
|
+
const { tableName: relatedTableName } = breakExternalTableId(tableId)
|
|
479
|
+
// @ts-ignore
|
|
480
|
+
const linkPrimaryKey = this.tables[relatedTableName].primary[0]
|
|
481
|
+
const manyKey = field.throughTo || primaryKey
|
|
482
|
+
const lookupField = isMany ? primaryKey : field.foreignKey
|
|
457
483
|
const fieldName = isMany ? manyKey : field.fieldName
|
|
484
|
+
if (!lookupField || !row[lookupField]) {
|
|
485
|
+
continue
|
|
486
|
+
}
|
|
458
487
|
const response = await getDatasourceAndQuery(this.appId, {
|
|
459
488
|
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
|
|
460
489
|
filters: {
|
|
461
490
|
equal: {
|
|
462
|
-
[fieldName]: row[
|
|
491
|
+
[fieldName]: row[lookupField],
|
|
463
492
|
},
|
|
464
493
|
},
|
|
465
494
|
})
|
|
466
495
|
// this is the response from knex if no rows found
|
|
467
496
|
const rows = !response[0].read ? response : []
|
|
468
|
-
|
|
497
|
+
const storeTo = isMany ? field.throughFrom || linkPrimaryKey : manyKey
|
|
498
|
+
related[storeTo] = { rows, isMany, tableId }
|
|
469
499
|
}
|
|
470
500
|
return related
|
|
471
501
|
}
|