From ebf5dfeeeaa3430fa861be996610aa8a3bedc9bc Mon Sep 17 00:00:00 2001 From: michael shanks Date: Fri, 26 Jul 2019 15:13:15 +0100 Subject: [PATCH] get components server endpoint ... tested --- packages/server/.vscode/launch.json | 17 +++- .../testApp/components/myTextBox.json | 4 + .../components/subfolder/otherTextBox.json | 4 + .../components.json | 0 .../textbox/index.js | 0 .../appPackages/testApp/dist/package.tar.gz | Bin 1710 -> 2139 bytes .../moreCustomComponents/components.json | 11 ++ .../moreCustomComponents/textbox/index.js | 0 .../server/appPackages/testApp/pages.json | 2 +- packages/server/jest.config.js.later | 12 --- packages/server/middleware/routers.js | 49 ++++----- .../tests/{all.spec.js => apps.spec.js} | 0 packages/server/tests/builder.spec.js | 66 ++++++++++++ packages/server/tests/testApp.js | 3 +- packages/server/utilities/builder.js | 94 ++++++++++++++---- 15 files changed, 202 insertions(+), 60 deletions(-) create mode 100644 packages/server/appPackages/testApp/components/myTextBox.json create mode 100644 packages/server/appPackages/testApp/components/subfolder/otherTextBox.json rename packages/server/appPackages/testApp/{myComponents => customComponents}/components.json (100%) rename packages/server/appPackages/testApp/{myComponents => customComponents}/textbox/index.js (100%) create mode 100644 packages/server/appPackages/testApp/moreCustomComponents/components.json create mode 100644 packages/server/appPackages/testApp/moreCustomComponents/textbox/index.js delete mode 100644 packages/server/jest.config.js.later rename packages/server/tests/{all.spec.js => apps.spec.js} (100%) create mode 100644 packages/server/tests/builder.spec.js diff --git a/packages/server/.vscode/launch.json b/packages/server/.vscode/launch.json index 023c59ffd3..23fcc70dc3 100644 --- a/packages/server/.vscode/launch.json +++ b/packages/server/.vscode/launch.json @@ -13,9 +13,22 @@ { "type": "node", "request": "launch", - "name": "Jest All", + "name": "Jest App Server", "program": "${workspaceFolder}/node_modules/.bin/jest", - "args": ["--runInBand"], + "args": ["apps", "--runInBand"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "windows": { + "program": "${workspaceFolder}/node_modules/jest-cli/bin/jest", + } + }, + { + "type": "node", + "request": "launch", + "name": "Jest Builder", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["builder", "--runInBand"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, diff --git a/packages/server/appPackages/testApp/components/myTextBox.json b/packages/server/appPackages/testApp/components/myTextBox.json new file mode 100644 index 0000000000..e76040fb63 --- /dev/null +++ b/packages/server/appPackages/testApp/components/myTextBox.json @@ -0,0 +1,4 @@ +{ + "_component": "./customComponents/textbox", + "label": "hello" +} \ No newline at end of file diff --git a/packages/server/appPackages/testApp/components/subfolder/otherTextBox.json b/packages/server/appPackages/testApp/components/subfolder/otherTextBox.json new file mode 100644 index 0000000000..038c1f094b --- /dev/null +++ b/packages/server/appPackages/testApp/components/subfolder/otherTextBox.json @@ -0,0 +1,4 @@ +{ + "_component": "./moreCustomComponents/textbox", + "label": "hello" +} \ No newline at end of file diff --git a/packages/server/appPackages/testApp/myComponents/components.json b/packages/server/appPackages/testApp/customComponents/components.json similarity index 100% rename from packages/server/appPackages/testApp/myComponents/components.json rename to packages/server/appPackages/testApp/customComponents/components.json diff --git a/packages/server/appPackages/testApp/myComponents/textbox/index.js b/packages/server/appPackages/testApp/customComponents/textbox/index.js similarity index 100% rename from packages/server/appPackages/testApp/myComponents/textbox/index.js rename to packages/server/appPackages/testApp/customComponents/textbox/index.js diff --git a/packages/server/appPackages/testApp/dist/package.tar.gz b/packages/server/appPackages/testApp/dist/package.tar.gz index 43818edf6a67364141acdd7a3a175630308c8e24..1af8254fa48f3874986687afff0e8b9dc20cd3f4 100644 GIT binary patch literal 2139 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 E0QkaY>;M1& diff --git a/packages/server/appPackages/testApp/moreCustomComponents/components.json b/packages/server/appPackages/testApp/moreCustomComponents/components.json new file mode 100644 index 0000000000..3b11bb76a3 --- /dev/null +++ b/packages/server/appPackages/testApp/moreCustomComponents/components.json @@ -0,0 +1,11 @@ +{ + "textbox" : { + "path": "./textbox", + "name": "Textbox", + "description": "A text input, with a label", + "props": { + "label": "string" + }, + "tags": ["textboxt", "input", "text"] + } +} \ No newline at end of file diff --git a/packages/server/appPackages/testApp/moreCustomComponents/textbox/index.js b/packages/server/appPackages/testApp/moreCustomComponents/textbox/index.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/server/appPackages/testApp/pages.json b/packages/server/appPackages/testApp/pages.json index 9d38c2c9de..2ac68a995d 100644 --- a/packages/server/appPackages/testApp/pages.json +++ b/packages/server/appPackages/testApp/pages.json @@ -15,5 +15,5 @@ }, "appBody" : "./unauthenticated.app.json" }, - "componentLibraries": ["./myComponents"] + "componentLibraries": ["./customComponents", "./moreCustomComponents"] } \ No newline at end of file diff --git a/packages/server/jest.config.js.later b/packages/server/jest.config.js.later deleted file mode 100644 index e683c93c32..0000000000 --- a/packages/server/jest.config.js.later +++ /dev/null @@ -1,12 +0,0 @@ -const Sequencer = require('@jest/test-sequencer').default; - -const testOrder = [""] - -class CustomSequencer extends Sequencer { - sort(tests) { - // Test structure information - // https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21 - const copyTests = Array.from(tests); - return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1)); - } -} diff --git a/packages/server/middleware/routers.js b/packages/server/middleware/routers.js index 42ed712caa..2c496e0754 100644 --- a/packages/server/middleware/routers.js +++ b/packages/server/middleware/routers.js @@ -4,13 +4,10 @@ const StatusCodes = require("../utilities/statusCodes"); const fs = require("fs"); const { resolve } = require("path"); const send = require('koa-send'); -const { getPackageForBuilder, getComponents, +const { getPackageForBuilder, getRootComponents: getComponents, savePackage, getApps } = require("../utilities/builder"); const builderPath = resolve(__dirname, "../builder"); -const appUiPath = appname => { - -} module.exports = (config, app) => { @@ -33,21 +30,27 @@ module.exports = (config, app) => { ctx.throw(StatusCodes.NOT_FOUND, "App Name not declared"); } - const instance = await ctx.master.getInstanceApiForSession( - pathParts[1], - ctx.sessionId); - + const appname = pathParts[1]; - ctx.instance = instance.instance; - ctx.publicPath = instance.publicPath; - ctx.isAuthenticated = !!instance.instance; - - await next(); + if(appname === "_builder") { + await next(); + } else { + const instance = await ctx.master.getInstanceApiForSession( + appname, + ctx.sessionId); + + + ctx.instance = instance.instance; + ctx.publicPath = instance.publicPath; + ctx.isAuthenticated = !!instance.instance; + + await next(); + } }) .get("/_builder", async (ctx) => { if(!config.dev) { - ctx.request.status = StatusCodes.FORBIDDEN; - ctx.request.body = "run in dev mode to access builder"; + ctx.response.status = StatusCodes.FORBIDDEN; + ctx.body = "run in dev mode to access builder"; return; } @@ -56,8 +59,8 @@ module.exports = (config, app) => { }) .get("/_builder/*", async (ctx, next) => { if(!config.dev) { - ctx.request.status = StatusCodes.FORBIDDEN; - ctx.request.body = "run in dev mode to access builder"; + ctx.response.status = StatusCodes.FORBIDDEN; + ctx.body = "run in dev mode to access builder"; return; } @@ -117,8 +120,8 @@ module.exports = (config, app) => { }) .get("/_builder/api/apps", async (ctx) => { if(!config.dev) { - ctx.request.status = StatusCodes.FORBIDDEN; - ctx.request.body = "run in dev mode to access builder"; + ctx.response.status = StatusCodes.FORBIDDEN; + ctx.response.body = "run in dev mode to access builder"; return; } @@ -128,8 +131,8 @@ module.exports = (config, app) => { }) .get("/_builder/api/:appname/appPackage", async (ctx) => { if(!config.dev) { - ctx.request.status = StatusCodes.FORBIDDEN; - ctx.request.body = "run in dev mode to access builder"; + ctx.response.status = StatusCodes.FORBIDDEN; + ctx.body = "run in dev mode to access builder"; return; } @@ -141,7 +144,7 @@ module.exports = (config, app) => { }) .post("/_builder/api/:appname/appPackage", async (ctx) => { if(!config.dev) { - ctx.request.status = StatusCodes.FORBIDDEN; + ctx.response.status = StatusCodes.FORBIDDEN; ctx.body = "run in dev mode to access builder"; return; } @@ -154,7 +157,7 @@ module.exports = (config, app) => { }) .get("/_builder/api/:appname/components", async (ctx) => { if(!config.dev) { - ctx.request.status = StatusCodes.FORBIDDEN; + ctx.response.status = StatusCodes.FORBIDDEN; ctx.body = "run in dev mode to access builder"; return; } diff --git a/packages/server/tests/all.spec.js b/packages/server/tests/apps.spec.js similarity index 100% rename from packages/server/tests/all.spec.js rename to packages/server/tests/apps.spec.js diff --git a/packages/server/tests/builder.spec.js b/packages/server/tests/builder.spec.js new file mode 100644 index 0000000000..91c67f9a1b --- /dev/null +++ b/packages/server/tests/builder.spec.js @@ -0,0 +1,66 @@ + +const testAppDef = require("../appPackages/testApp/appDefinition.json"); +const testAccessLevels = require("../appPackages/testApp/access_levels.json"); +const testPages = require("../appPackages/testApp/pages.json"); +const testComponents = require("../appPackages/testApp/customComponents/components.json"); +const testMoreComponents = require("../appPackages/testApp/moreCustomComponents/components.json"); +const statusCodes = require("../utilities/statusCodes"); +const derivedComponent1 = require("../appPackages/testApp/components/myTextBox.json"); +const derivedComponent2 = require("../appPackages/testApp/components/subfolder/otherTextBox.json"); + +const app = require("./testApp")(); + +beforeAll(async () => await app.start()); +afterAll(async () => await app.destroy()); + + +it("/apppackage should get appDefinition", async () => { + + const {body} = await app.get("/_builder/api/testApp/appPackage") + .expect(statusCodes.OK); + + expect(body.appDefinition).toEqual(testAppDef); +}); + +it("/apppackage should get access levels", async () => { + + const {body} = await app.get("/_builder/api/testApp/appPackage") + .expect(statusCodes.OK); + + expect(body.accessLevels).toEqual(testAccessLevels); +}); + +it("/apppackage should get pages", async () => { + + const {body} = await app.get("/_builder/api/testApp/appPackage") + .expect(statusCodes.OK); + expect(body.pages).toEqual(testPages); +}); + +it("/apppackage should get rootComponents", async () => { + + const {body} = await app.get("/_builder/api/testApp/appPackage") + .expect(statusCodes.OK); + + expect(body.rootComponents["./customComponents/textbox"]).toBeDefined(); + expect(body.rootComponents["./moreCustomComponents/textbox"]).toBeDefined(); + + expect(body.rootComponents["./customComponents/textbox"]) + .toEqual(testComponents.textbox); + + expect(body.rootComponents["./moreCustomComponents/textbox"]) + .toEqual(testMoreComponents.textbox); +}); + +it("/apppackage should get derivedComponents", async () => { + + const {body} = await app.get("/_builder/api/testApp/appPackage") + .expect(statusCodes.OK); + + const expectedComponents = { + "myTextBox" : {...derivedComponent1, _name:"myTextBox"}, + "subfolder/otherTextBox": {...derivedComponent2, _name:"subfolder/otherTextBox"} + }; + + expect(body.derivedComponents).toEqual(expectedComponents); +}); diff --git a/packages/server/tests/testApp.js b/packages/server/tests/testApp.js index 2009a9aa69..e7cd9adac5 100644 --- a/packages/server/tests/testApp.js +++ b/packages/server/tests/testApp.js @@ -46,7 +46,8 @@ const config = { port: 4002, latestPackagesFolder: "./appPackages", extraMasterPlugins, - customizeMaster + customizeMaster, + dev:true } diff --git a/packages/server/utilities/builder.js b/packages/server/utilities/builder.js index f98ca2df87..724d2b1009 100644 --- a/packages/server/utilities/builder.js +++ b/packages/server/utilities/builder.js @@ -6,20 +6,29 @@ const { writeFile, readFile, readdir, - exists + exists, + stat } = require("./fsawait"); -const { resolve } = require("path"); +const { + resolve, + join +} = require("path"); const { $ } = require("budibase-core").common; const { keys, reduce, - map, - flatten, - some + some, + keyBy } = require("lodash/fp"); +const {merge} = require("lodash"); module.exports.getPackageForBuilder = async (config, appname) => { const appPath = appPackageFolder(config, appname); + + const pages = JSON.parse(await readFile( + `${appPath}/pages.json`, + "utf8")); + return ({ appDefinition: JSON.parse(await readFile( `${appPath}/appDefinition.json`, @@ -29,9 +38,12 @@ module.exports.getPackageForBuilder = async (config, appname) => { `${appPath}/access_levels.json`, "utf8")), - pages: JSON.parse(await readFile( - `${appPath}/pages.json`, - "utf8")) + pages, + + rootComponents: await getRootComponents(appPath, pages), + + derivedComponents: keyBy("_name")( + await fetchDerivedComponents(appPath)) }) } @@ -50,7 +62,7 @@ module.exports.savePackage = async (config, appname, pkg) => { await writeFile( `${appPath}/pages.json`, - JSON.stringify(pkg.accessLevels), + JSON.stringify(pkg.pages), "utf8"); } @@ -58,9 +70,9 @@ module.exports.getApps = async (config) => await readdir(appsFolder(config)); -module.exports.getComponents = async (config, appname, lib) => { +const getRootComponents = async (appPath, pages ,lib) => { - const componentsInLibrary = (libname) => { + const componentsInLibrary = async (libname) => { const isRelative = some(c => c === libname.substring(0,1)) ("./~\\".split("")); @@ -77,10 +89,10 @@ module.exports.getComponents = async (config, appname, lib) => { let components; try { components = JSON.parse( - readFile(componentsPath, "utf8")); + await readFile(componentsPath, "utf8")); } catch(e) { - const e = new Error(`could not parse JSON - ${componentsPath} `); - throw e; + const err = `could not parse JSON - ${componentsPath} : ${e.message}`; + throw new Error(err); } return $(components, [ @@ -94,9 +106,7 @@ module.exports.getComponents = async (config, appname, lib) => { let libs; if(!lib) { - const appPath = appPackageFolder(config, appname); - - const pages = JSON.parse(await readFile( + pages = pages || JSON.parse(await readFile( `${appPath}/pages.json`, "utf8")); @@ -107,9 +117,51 @@ module.exports.getComponents = async (config, appname, lib) => { libs = [lib]; } - return $(libs, [ - map(componentsInLibrary), - flatten - ]); + const components = {}; + for(let l of libs) { + merge(components, await componentsInLibrary(l)) + } + + return components; } +const fetchDerivedComponents = async (appPath, relativePath = "") => { + + const currentDir = join(appPath, "components", relativePath); + + const contents = await readdir(currentDir); + + const components = []; + + for(let item of contents) { + const itemRelativePath = join(relativePath, item); + const itemFullPath = join(currentDir, item); + const stats = await stat(itemFullPath); + + if(stats.isFile()) { + + if(!item.endsWith(".json")) continue; + + const component = JSON.parse( + await readFile(itemFullPath, "utf8")); + + component._name = itemRelativePath + .substring(0, itemRelativePath.length - 5) + .replace(/\\/g, "/"); + + components.push(component); + } else { + const childComponents = await fetchDerivedComponents( + appPath, join(relativePath, item) + ); + + for(let c of childComponents) { + components.push(c); + } + } + } + + return components; +} + +module.exports.getRootComponents = getRootComponents; \ No newline at end of file