From 07c719215469be236103187bc7d9c05aca409d0e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Sep 2023 13:29:16 +0100 Subject: [PATCH] Adding documentation for export endpoint. --- packages/server/specs/openapi.json | 58 ++++++++++++++++++- packages/server/specs/openapi.yaml | 40 ++++++++++++- .../server/specs/resources/application.ts | 11 ++++ .../api/controllers/public/applications.ts | 19 +++++- .../src/api/routes/public/applications.ts | 33 ++++++++++- packages/server/src/definitions/openapi.ts | 34 +++++++++++ 6 files changed, 191 insertions(+), 4 deletions(-) diff --git a/packages/server/specs/openapi.json b/packages/server/specs/openapi.json index 3652182f9d..18f9dd4245 100644 --- a/packages/server/specs/openapi.json +++ b/packages/server/specs/openapi.json @@ -613,6 +613,23 @@ "data" ] }, + "appExport": { + "type": "object", + "properties": { + "encryptPassword": { + "description": "An optional password used to encrypt the export.", + "type": "string" + }, + "excludeRows": { + "description": "Set whether the internal table rows should be excluded from the export.", + "type": "boolean" + } + }, + "required": [ + "encryptPassword", + "excludeRows" + ] + }, "row": { "description": "The row to be created/updated, based on the table schema.", "type": "object", @@ -2166,7 +2183,8 @@ "/applications/{appId}/import": { "post": { "operationId": "appImport", - "summary": "Import an app to an existing app", + "summary": "Import an app to an existing app 🔒", + "description": "This endpoint is only available on a business or enterprise license.", "tags": [ "applications" ], @@ -2205,6 +2223,44 @@ } } }, + "/applications/{appId}/export": { + "post": { + "operationId": "appExport", + "summary": "Export an app 🔒", + "description": "This endpoint is only available on a business or enterprise license.", + "tags": [ + "applications" + ], + "parameters": [ + { + "$ref": "#/components/parameters/appIdUrl" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/appExport" + } + } + } + }, + "responses": { + "200": { + "description": "A gzip tarball containing the app export, encrypted if password provided.", + "content": { + "application/gzip": { + "schema": { + "type": "string", + "format": "binary", + "example": "Tarball containing database and object store contents..." + } + } + } + } + } + } + }, "/applications/search": { "post": { "operationId": "appSearch", diff --git a/packages/server/specs/openapi.yaml b/packages/server/specs/openapi.yaml index c533ee5a9a..4916141569 100644 --- a/packages/server/specs/openapi.yaml +++ b/packages/server/specs/openapi.yaml @@ -587,6 +587,19 @@ components: - appUrl required: - data + appExport: + type: object + properties: + encryptPassword: + description: An optional password used to encrypt the export. + type: string + excludeRows: + description: Set whether the internal table rows should be excluded from the + export. + type: boolean + required: + - encryptPassword + - excludeRows row: description: The row to be created/updated, based on the table schema. type: object @@ -1766,7 +1779,8 @@ paths: "/applications/{appId}/import": post: operationId: appImport - summary: Import an app to an existing app + summary: Import an app to an existing app 🔒 + description: This endpoint is only available on a business or enterprise license. tags: - applications parameters: @@ -1789,6 +1803,30 @@ paths: responses: "204": description: Application has been updated. + "/applications/{appId}/export": + post: + operationId: appExport + summary: Export an app 🔒 + description: This endpoint is only available on a business or enterprise license. + tags: + - applications + parameters: + - $ref: "#/components/parameters/appIdUrl" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/appExport" + responses: + "200": + description: A gzip tarball containing the app export, encrypted if password + provided. + content: + application/gzip: + schema: + type: string + format: binary + example: Tarball containing database and object store contents... /applications/search: post: operationId: appSearch diff --git a/packages/server/specs/resources/application.ts b/packages/server/specs/resources/application.ts index cd7a68c049..081dd9e72a 100644 --- a/packages/server/specs/resources/application.ts +++ b/packages/server/specs/resources/application.ts @@ -134,4 +134,15 @@ export default new Resource() deploymentOutput: object({ data: deploymentOutputSchema, }), + appExport: object({ + encryptPassword: { + description: "An optional password used to encrypt the export.", + type: "string", + }, + excludeRows: { + description: + "Set whether the internal table rows should be excluded from the export.", + type: "boolean", + }, + }), }) diff --git a/packages/server/src/api/controllers/public/applications.ts b/packages/server/src/api/controllers/public/applications.ts index d9a94ed073..f2168ee98a 100644 --- a/packages/server/src/api/controllers/public/applications.ts +++ b/packages/server/src/api/controllers/public/applications.ts @@ -2,6 +2,7 @@ import { db as dbCore, context } from "@budibase/backend-core" import { search as stringSearch, addRev } from "./utils" import * as controller from "../application" import * as deployController from "../deploy" +import * as backupController from "../backup" import { Application } from "../../../definitions/common" import { UserCtx } from "@budibase/types" import { Next } from "koa" @@ -94,8 +95,10 @@ export async function publish(ctx: UserCtx, next: Next) { } export async function importToApp(ctx: UserCtx, next: Next) { + if (!ctx.request.files?.appExport) { + ctx.throw(400, "Must provide app export file for import.") + } await context.doInAppContext(ctx.params.appId, async () => { - // TODO: paid control await controller.importToApp(ctx) ctx.body = undefined ctx.status = 204 @@ -103,6 +106,19 @@ export async function importToApp(ctx: UserCtx, next: Next) { }) } +export async function exportApp(ctx: UserCtx, next: Next) { + const { encryptPassword, excludeRows } = ctx.request.body + await context.doInAppContext(ctx.params.appId, async () => { + // make sure no other inputs + ctx.request.body = { + encryptPassword, + excludeRows, + } + await backupController.exportAppDump(ctx) + await next() + }) +} + export default { create, update, @@ -112,4 +128,5 @@ export default { unpublish, publish, importToApp, + exportApp, } diff --git a/packages/server/src/api/routes/public/applications.ts b/packages/server/src/api/routes/public/applications.ts index 6fff83bd07..5410eb7dcf 100644 --- a/packages/server/src/api/routes/public/applications.ts +++ b/packages/server/src/api/routes/public/applications.ts @@ -142,7 +142,8 @@ write.push( * /applications/{appId}/import: * post: * operationId: appImport - * summary: Import an app to an existing app + * summary: Import an app to an existing app 🔒 + * description: This endpoint is only available on a business or enterprise license. * tags: * - applications * parameters: @@ -170,6 +171,36 @@ write.push( new Endpoint("post", "/applications/:appId/import", controller.importToApp) ) +/** + * @openapi + * /applications/{appId}/export: + * post: + * operationId: appExport + * summary: Export an app 🔒 + * description: This endpoint is only available on a business or enterprise license. + * tags: + * - applications + * parameters: + * - $ref: '#/components/parameters/appIdUrl' + * requestBody: + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/appExport' + * responses: + * 200: + * description: A gzip tarball containing the app export, encrypted if password provided. + * content: + * application/gzip: + * schema: + * type: string + * format: binary + * example: Tarball containing database and object store contents... + */ +read.push( + new Endpoint("post", "/applications/:appId/export", controller.exportApp) +) + /** * @openapi * /applications/{appId}: diff --git a/packages/server/src/definitions/openapi.ts b/packages/server/src/definitions/openapi.ts index 9057e39005..52434494e5 100644 --- a/packages/server/src/definitions/openapi.ts +++ b/packages/server/src/definitions/openapi.ts @@ -19,8 +19,13 @@ export interface paths { post: operations["appPublish"]; }; "/applications/{appId}/import": { + /** This endpoint is only available on a business or enterprise license. */ post: operations["appImport"]; }; + "/applications/{appId}/export": { + /** This endpoint is only available on a business or enterprise license. */ + post: operations["appExport"]; + }; "/applications/search": { /** Based on application properties (currently only name) search for applications. */ post: operations["appSearch"]; @@ -161,6 +166,12 @@ export interface components { appUrl: string; }; }; + appExport: { + /** @description An optional password used to encrypt the export. */ + encryptPassword: string; + /** @description Set whether the internal table rows should be excluded from the export. */ + excludeRows: boolean; + }; /** @description The row to be created/updated, based on the table schema. */ row: { [key: string]: unknown }; searchOutput: { @@ -892,6 +903,7 @@ export interface operations { }; }; }; + /** This endpoint is only available on a business or enterprise license. */ appImport: { parameters: { path: { @@ -917,6 +929,28 @@ export interface operations { }; }; }; + /** This endpoint is only available on a business or enterprise license. */ + appExport: { + parameters: { + path: { + /** The ID of the app which this request is targeting. */ + appId: components["parameters"]["appIdUrl"]; + }; + }; + responses: { + /** A gzip tarball containing the app export, encrypted if password provided. */ + 200: { + content: { + "application/gzip": string; + }; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["appExport"]; + }; + }; + }; /** Based on application properties (currently only name) search for applications. */ appSearch: { responses: {