diff --git a/packages/backend-core/src/objectStore/index.ts b/packages/backend-core/src/objectStore/index.ts index 84eebeff81..a1193c0303 100644 --- a/packages/backend-core/src/objectStore/index.ts +++ b/packages/backend-core/src/objectStore/index.ts @@ -390,7 +390,7 @@ export const uploadDirectory = async ( return files } -exports.downloadTarballDirect = async ( +export const downloadTarballDirect = async ( url: string, path: string, headers = {} diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index bdd9bfd4a6..68c01557bc 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -17,13 +17,9 @@ const { clientLibraryPath } = require("../../../utilities") const { upload, deleteFiles } = require("../../../utilities/fileSystem") const { attachmentsRelativeURL } = require("../../../utilities") const { DocumentType } = require("../../../db/utils") -const { getAppDB, getAppId } = require("@budibase/backend-core/context") -const { setCookie, clearCookie } = require("@budibase/backend-core/utils") +const { context, objectStore, utils } = require("@budibase/backend-core") const AWS = require("aws-sdk") const fs = require("fs") -const { - downloadTarballDirect, -} = require("../../../utilities/fileSystem/utilities") async function prepareUpload({ s3Key, bucket, metadata, file }: any) { const response = await upload({ @@ -48,7 +44,7 @@ export const toggleBetaUiFeature = async function (ctx: any) { const cookieName = `beta:${ctx.params.feature}` if (ctx.cookies.get(cookieName)) { - clearCookie(ctx, cookieName) + utils.clearCookie(ctx, cookieName) ctx.body = { message: `${ctx.params.feature} disabled`, } @@ -61,11 +57,11 @@ export const toggleBetaUiFeature = async function (ctx: any) { if (!fs.existsSync(builderPath)) { fs.mkdirSync(builderPath) } - await downloadTarballDirect( + await objectStore.downloadTarballDirect( "https://cdn.budi.live/beta:design_ui/new_ui.tar.gz", builderPath ) - setCookie(ctx, {}, cookieName) + utils.setCookie(ctx, {}, cookieName) ctx.body = { message: `${ctx.params.feature} enabled`, @@ -103,9 +99,9 @@ export const deleteObjects = async function (ctx: any) { } export const serveApp = async function (ctx: any) { - const db = getAppDB({ skip_setup: true }) + const db = context.getAppDB({ skip_setup: true }) const appInfo = await db.get(DocumentType.APP_METADATA) - let appId = getAppId() + let appId = context.getAppId() if (!env.isJest()) { const App = require("./templates/BudibaseApp.svelte").default @@ -134,11 +130,11 @@ export const serveApp = async function (ctx: any) { } export const serveBuilderPreview = async function (ctx: any) { - const db = getAppDB({ skip_setup: true }) + const db = context.getAppDB({ skip_setup: true }) const appInfo = await db.get(DocumentType.APP_METADATA) if (!env.isJest()) { - let appId = getAppId() + let appId = context.getAppId() const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`) ctx.body = await processString(previewHbs, { clientLibPath: clientLibraryPath(appId, appInfo.version, ctx), @@ -156,7 +152,7 @@ export const serveClientLibrary = async function (ctx: any) { } export const getSignedUploadURL = async function (ctx: any) { - const database = getAppDB() + const database = context.getAppDB() // Ensure datasource is valid let datasource diff --git a/packages/server/src/api/routes/application.ts b/packages/server/src/api/routes/application.ts index 8627dd024c..38d857ac8d 100644 --- a/packages/server/src/api/routes/application.ts +++ b/packages/server/src/api/routes/application.ts @@ -4,7 +4,7 @@ import authorized from "../../middleware/authorized" import { BUILDER } from "@budibase/backend-core/permissions" import { applicationValidator } from "./utils/validators" -const router = new Router() +const router: Router = new Router() router .post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync) diff --git a/packages/server/src/api/routes/auth.ts b/packages/server/src/api/routes/auth.ts index 8a9d11fb27..c4e65a4c25 100644 --- a/packages/server/src/api/routes/auth.ts +++ b/packages/server/src/api/routes/auth.ts @@ -1,7 +1,7 @@ import Router from "@koa/router" import * as controller from "../controllers/auth" -const router = new Router() +const router: Router = new Router() router.get("/api/self", controller.fetchSelf) diff --git a/packages/server/src/api/routes/backup.ts b/packages/server/src/api/routes/backup.ts index 2473fa9f67..cc7eb25c8b 100644 --- a/packages/server/src/api/routes/backup.ts +++ b/packages/server/src/api/routes/backup.ts @@ -3,7 +3,7 @@ import * as controller from "../controllers/backup" import authorized from "../../middleware/authorized" import { BUILDER } from "@budibase/backend-core/permissions" -const router = new Router() +const router: Router = new Router() router.get("/api/backups/export", authorized(BUILDER), controller.exportAppDump) diff --git a/packages/server/src/api/routes/deploy.js b/packages/server/src/api/routes/deploy.js deleted file mode 100644 index 1f6b07c6f3..0000000000 --- a/packages/server/src/api/routes/deploy.js +++ /dev/null @@ -1,17 +0,0 @@ -const Router = require("@koa/router") -const controller = require("../controllers/deploy") -const authorized = require("../../middleware/authorized") -const { BUILDER } = require("@budibase/backend-core/permissions") - -const router = new Router() - -router - .get("/api/deployments", authorized(BUILDER), controller.fetchDeployments) - .get( - "/api/deploy/:deploymentId", - authorized(BUILDER), - controller.deploymentProgress - ) - .post("/api/deploy", authorized(BUILDER), controller.deployApp) - -module.exports = router diff --git a/packages/server/src/api/routes/deploy.ts b/packages/server/src/api/routes/deploy.ts new file mode 100644 index 0000000000..d091581ec1 --- /dev/null +++ b/packages/server/src/api/routes/deploy.ts @@ -0,0 +1,21 @@ +import Router from "@koa/router" +import * as controller from "../controllers/deploy" +import authorized from "../../middleware/authorized" +import { permissions } from "@budibase/backend-core" + +const router: Router = new Router() + +router + .get( + "/api/deployments", + authorized(permissions.BUILDER), + controller.fetchDeployments + ) + .get( + "/api/deploy/:deploymentId", + authorized(permissions.BUILDER), + controller.deploymentProgress + ) + .post("/api/deploy", authorized(permissions.BUILDER), controller.deployApp) + +export default router diff --git a/packages/server/src/api/routes/plugin.ts b/packages/server/src/api/routes/plugin.ts index d619745a4a..d5bd7607f7 100644 --- a/packages/server/src/api/routes/plugin.ts +++ b/packages/server/src/api/routes/plugin.ts @@ -3,7 +3,7 @@ import * as controller from "../controllers/plugin" import authorized from "../../middleware/authorized" import { BUILDER } from "@budibase/backend-core/permissions" -const router = new Router() +const router: Router = new Router() router .post("/api/plugin/upload", authorized(BUILDER), controller.upload) diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts index 72189a2482..e75b7d6e20 100644 --- a/packages/server/src/api/routes/row.ts +++ b/packages/server/src/api/routes/row.ts @@ -8,7 +8,7 @@ const { } = require("@budibase/backend-core/permissions") const { internalSearchValidator } = require("./utils/validators") -const router = new Router() +const router: Router = new Router() router /** diff --git a/packages/server/src/api/routes/static.ts b/packages/server/src/api/routes/static.ts index ccfec6fd8c..7dbd998583 100644 --- a/packages/server/src/api/routes/static.ts +++ b/packages/server/src/api/routes/static.ts @@ -10,7 +10,7 @@ import { import * as env from "../../environment" import { paramResource } from "../../middleware/resourceId" -const router = new Router() +const router: Router = new Router() /* istanbul ignore next */ router.param("file", async (file: any, ctx: any, next: any) => { diff --git a/packages/server/src/api/routes/utils/validators.js b/packages/server/src/api/routes/utils/validators.ts similarity index 79% rename from packages/server/src/api/routes/utils/validators.js rename to packages/server/src/api/routes/utils/validators.ts index b44cce5771..b6def14d70 100644 --- a/packages/server/src/api/routes/utils/validators.js +++ b/packages/server/src/api/routes/utils/validators.ts @@ -1,20 +1,16 @@ -const { joiValidator } = require("@budibase/backend-core/auth") -const { DataSourceOperation } = require("../../../constants") -const { - BuiltinPermissionID, - PermissionLevel, -} = require("@budibase/backend-core/permissions") -const { WebhookActionType } = require("@budibase/types") -const Joi = require("joi") +import { auth, permissions } from "@budibase/backend-core" +import { DataSourceOperation } from "../../../constants" +import { WebhookActionType } from "@budibase/types" +import Joi from "joi" const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("") const OPTIONAL_NUMBER = Joi.number().optional().allow(null) const OPTIONAL_BOOLEAN = Joi.boolean().optional().allow(null) const APP_NAME_REGEX = /^[\w\s]+$/ -exports.tableValidator = () => { +export function tableValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ _id: OPTIONAL_STRING, _rev: OPTIONAL_STRING, type: OPTIONAL_STRING.valid("table", "internal", "external"), @@ -26,16 +22,16 @@ exports.tableValidator = () => { }).unknown(true)) } -exports.nameValidator = () => { +export function nameValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ name: OPTIONAL_STRING, })) } -exports.datasourceValidator = () => { +export function datasourceValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ _id: Joi.string(), _rev: Joi.string(), type: OPTIONAL_STRING.allow("datasource_plus"), @@ -64,9 +60,9 @@ function filterObject() { }).unknown(true) } -exports.internalSearchValidator = () => { +export function internalSearchValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ tableId: OPTIONAL_STRING, query: filterObject(), limit: OPTIONAL_NUMBER, @@ -78,8 +74,8 @@ exports.internalSearchValidator = () => { })) } -exports.externalSearchValidator = () => { - return joiValidator.body( +export function externalSearchValidator() { + return auth.joiValidator.body( Joi.object({ query: filterObject(), paginate: Joi.boolean().optional(), @@ -96,9 +92,9 @@ exports.externalSearchValidator = () => { ) } -exports.datasourceQueryValidator = () => { +export function datasourceQueryValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ endpoint: Joi.object({ datasourceId: Joi.string().required(), operation: Joi.string().required().valid(...Object.values(DataSourceOperation)), @@ -117,9 +113,9 @@ exports.datasourceQueryValidator = () => { })) } -exports.webhookValidator = () => { +export function webhookValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ live: Joi.bool(), _id: OPTIONAL_STRING, _rev: OPTIONAL_STRING, @@ -132,15 +128,15 @@ exports.webhookValidator = () => { }).unknown(true)) } -exports.roleValidator = () => { - const permLevelArray = Object.values(PermissionLevel) +export function roleValidator() { + const permLevelArray = Object.values(permissions.PermissionLevel) // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ _id: OPTIONAL_STRING, _rev: OPTIONAL_STRING, name: Joi.string().required(), // this is the base permission ID (for now a built in) - permissionId: Joi.string().valid(...Object.values(BuiltinPermissionID)).required(), + permissionId: Joi.string().valid(...Object.values(permissions.BuiltinPermissionID)).required(), permissions: Joi.object() .pattern(/.*/, [Joi.string().valid(...permLevelArray)]) .optional(), @@ -148,19 +144,19 @@ exports.roleValidator = () => { }).unknown(true)) } -exports.permissionValidator = () => { - const permLevelArray = Object.values(PermissionLevel) +export function permissionValidator() { + const permLevelArray = Object.values(permissions.PermissionLevel) // prettier-ignore - return joiValidator.params(Joi.object({ + return auth.joiValidator.params(Joi.object({ level: Joi.string().valid(...permLevelArray).required(), resourceId: Joi.string(), roleId: Joi.string(), }).unknown(true)) } -exports.screenValidator = () => { +export function screenValidator() { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ name: Joi.string().required(), showNavigation: OPTIONAL_BOOLEAN, width: OPTIONAL_STRING, @@ -181,7 +177,7 @@ exports.screenValidator = () => { }).unknown(true)) } -function generateStepSchema(allowStepTypes) { +function generateStepSchema(allowStepTypes: string[]) { // prettier-ignore return Joi.object({ stepId: Joi.string().required(), @@ -196,9 +192,9 @@ function generateStepSchema(allowStepTypes) { }).unknown(true) } -exports.automationValidator = (existing = false) => { +export function automationValidator(existing = false) { // prettier-ignore - return joiValidator.body(Joi.object({ + return auth.joiValidator.body(Joi.object({ _id: existing ? Joi.string().required() : OPTIONAL_STRING, _rev: existing ? Joi.string().required() : OPTIONAL_STRING, name: Joi.string().required(), @@ -210,9 +206,9 @@ exports.automationValidator = (existing = false) => { }).unknown(true)) } -exports.applicationValidator = (opts = { isCreate: true }) => { +export function applicationValidator(opts = { isCreate: true }) { // prettier-ignore - const base = { + const base: any = { _id: OPTIONAL_STRING, _rev: OPTIONAL_STRING, url: OPTIONAL_STRING, @@ -230,7 +226,7 @@ exports.applicationValidator = (opts = { isCreate: true }) => { base.name = appNameValidator.optional() } - return joiValidator.body( + return auth.joiValidator.body( Joi.object({ _id: OPTIONAL_STRING, _rev: OPTIONAL_STRING, diff --git a/packages/server/src/api/routes/webhook.ts b/packages/server/src/api/routes/webhook.ts index 103ab98142..3aa9525ed5 100644 --- a/packages/server/src/api/routes/webhook.ts +++ b/packages/server/src/api/routes/webhook.ts @@ -5,7 +5,7 @@ import { permissions } from "@budibase/backend-core" import { webhookValidator } from "./utils/validators" const BUILDER = permissions.BUILDER -const router = new Router() +const router: Router = new Router() router .get("/api/webhooks", authorized(BUILDER), controller.fetch) diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index 9c870208a7..d47e5c42da 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -1,23 +1,13 @@ -import { - getUserRoleHierarchy, - getRequiredResourceRole, - BUILTIN_ROLE_IDS, -} from "@budibase/backend-core/roles" -const { - PermissionType, - PermissionLevel, - doesHaveBasePermission, -} = require("@budibase/backend-core/permissions") -const builderMiddleware = require("./builder") -const { isWebhookEndpoint } = require("./utils") -const { buildCsrfMiddleware } = require("@budibase/backend-core/auth") -const { getAppId } = require("@budibase/backend-core/context") +import { roles, permissions, auth, context } from "@budibase/backend-core" +import { Role } from "@budibase/types" +import builderMiddleware from "./builder" +import { isWebhookEndpoint } from "./utils" function hasResource(ctx: any) { return ctx.resourceId != null } -const csrf = buildCsrfMiddleware() +const csrf = auth.buildCsrfMiddleware() /** * Apply authorization to the requested resource: @@ -33,7 +23,7 @@ const checkAuthorized = async ( ) => { // check if this is a builder api and the user is not a builder const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global - const isBuilderApi = permType === PermissionType.BUILDER + const isBuilderApi = permType === permissions.PermissionType.BUILDER if (isBuilderApi && !isBuilder) { return ctx.throw(403, "Not Authorized") } @@ -51,10 +41,10 @@ const checkAuthorizedResource = async ( permLevel: any ) => { // get the user's roles - const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC - const userRoles = (await getUserRoleHierarchy(roleId, { + const roleId = ctx.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC + const userRoles = (await roles.getUserRoleHierarchy(roleId, { idOnly: false, - })) as { _id: string }[] + })) as Role[] const permError = "User does not have permission" // check if the user has the required role if (resourceRoles.length > 0) { @@ -66,7 +56,9 @@ const checkAuthorizedResource = async ( ctx.throw(403, permError) } // fallback to the base permissions when no resource roles are found - } else if (!doesHaveBasePermission(permType, permLevel, userRoles)) { + } else if ( + !permissions.doesHaveBasePermission(permType, permLevel, userRoles) + ) { ctx.throw(403, permError) } } @@ -91,21 +83,22 @@ export = (permType: any, permLevel: any = null, opts = { schema: false }) => let resourceRoles: any = [] let otherLevelRoles: any = [] const otherLevel = - permLevel === PermissionLevel.READ - ? PermissionLevel.WRITE - : PermissionLevel.READ - const appId = getAppId() + permLevel === permissions.PermissionLevel.READ + ? permissions.PermissionLevel.WRITE + : permissions.PermissionLevel.READ + const appId = context.getAppId() if (appId && hasResource(ctx)) { - resourceRoles = await getRequiredResourceRole(permLevel, ctx) + resourceRoles = await roles.getRequiredResourceRole(permLevel, ctx) if (opts && opts.schema) { - otherLevelRoles = await getRequiredResourceRole(otherLevel, ctx) + otherLevelRoles = await roles.getRequiredResourceRole(otherLevel, ctx) } } // if the resource is public, proceed if ( - resourceRoles.includes(BUILTIN_ROLE_IDS.PUBLIC) || - (otherLevelRoles && otherLevelRoles.includes(BUILTIN_ROLE_IDS.PUBLIC)) + resourceRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC) || + (otherLevelRoles && + otherLevelRoles.includes(roles.BUILTIN_ROLE_IDS.PUBLIC)) ) { return next() } diff --git a/packages/server/src/middleware/tests/authorized.spec.js b/packages/server/src/middleware/tests/authorized.spec.js index c64f758749..18a100bd93 100644 --- a/packages/server/src/middleware/tests/authorized.spec.js +++ b/packages/server/src/middleware/tests/authorized.spec.js @@ -9,8 +9,7 @@ jest.mock("../../environment", () => ({ ) const authorizedMiddleware = require("../authorized") const env = require("../../environment") -const { PermissionType, PermissionLevel } = require("@budibase/backend-core/permissions") -const { doInAppContext } = require("@budibase/backend-core/context") +const { permissions } = require("@budibase/backend-core") const APP_ID = "" @@ -113,7 +112,7 @@ describe("Authorization middleware", () => { it("throws if the user does not have builder permissions", async () => { config.setEnvironment(false) - config.setMiddlewareRequiredPermission(PermissionType.BUILDER) + config.setMiddlewareRequiredPermission(permissions.PermissionType.BUILDER) config.setUser({ role: { _id: "" @@ -125,13 +124,13 @@ describe("Authorization middleware", () => { }) it("passes on to next() middleware if the user has resource permission", async () => { - config.setResourceId(PermissionType.QUERY) + config.setResourceId(permissions.PermissionType.QUERY) config.setUser({ role: { _id: "" } }) - config.setMiddlewareRequiredPermission(PermissionType.QUERY) + config.setMiddlewareRequiredPermission(permissions.PermissionType.QUERY) await config.executeMiddleware() expect(config.next).toHaveBeenCalled() diff --git a/packages/server/src/sdk/app/backups/exports.ts b/packages/server/src/sdk/app/backups/exports.ts index 8de51ed1e6..9acad1344c 100644 --- a/packages/server/src/sdk/app/backups/exports.ts +++ b/packages/server/src/sdk/app/backups/exports.ts @@ -1,6 +1,5 @@ -import { db as dbCore } from "@budibase/backend-core" +import { db as dbCore, objectStore } from "@budibase/backend-core" import { budibaseTempDir } from "../../../utilities/budibaseDir" -import { retrieveDirectory } from "../../../utilities/fileSystem/utilities" import { streamFile, createTempFolder } from "../../../utilities/fileSystem" import { ObjectStoreBuckets } from "../../../constants" import { @@ -88,7 +87,10 @@ export async function exportApp(appId: string, config?: ExportOpts) { // export bucket contents let tmpPath if (!env.isTest()) { - tmpPath = await retrieveDirectory(ObjectStoreBuckets.APPS, appPath) + tmpPath = await objectStore.retrieveDirectory( + ObjectStoreBuckets.APPS, + appPath + ) } else { tmpPath = createTempFolder(uuid()) } diff --git a/packages/server/src/sdk/app/backups/imports.ts b/packages/server/src/sdk/app/backups/imports.ts index 1a745599f7..4c2a721c2c 100644 --- a/packages/server/src/sdk/app/backups/imports.ts +++ b/packages/server/src/sdk/app/backups/imports.ts @@ -1,12 +1,8 @@ -import { db as dbCore } from "@budibase/backend-core" +import { db as dbCore, objectStore } from "@budibase/backend-core" import { Database } from "@budibase/types" import { getAutomationParams, TABLE_ROW_PREFIX } from "../../../db/utils" import { budibaseTempDir } from "../../../utilities/budibaseDir" import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants" -import { - upload, - uploadDirectory, -} from "../../../utilities/fileSystem/utilities" import { downloadTemplate } from "../../../utilities/fileSystem" import { FieldTypes, ObjectStoreBuckets } from "../../../constants" import { join } from "path" @@ -174,11 +170,11 @@ export async function importApp( filename = join(prodAppId, filename) if (fs.lstatSync(path).isDirectory()) { promises.push( - uploadDirectory(ObjectStoreBuckets.APPS, path, filename) + objectStore.uploadDirectory(ObjectStoreBuckets.APPS, path, filename) ) } else { promises.push( - upload({ + objectStore.upload({ bucket: ObjectStoreBuckets.APPS, path, filename, diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js index 044ad4bbf7..76c79b838f 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.js +++ b/packages/server/src/tests/utilities/TestConfiguration.js @@ -1,5 +1,4 @@ require("../../db").init() -const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const env = require("../../environment") const { basicTable, @@ -13,18 +12,21 @@ const { basicWebhook, TENANT_ID, } = require("./structures") +const { + constants, + tenancy, + sessions, + cache, + context, + db: dbCore, + encryption, + auth, + roles, +} = require("@budibase/backend-core") const controllers = require("./controllers") const supertest = require("supertest") const { cleanup } = require("../../utilities/fileSystem") -const { Cookie, Header } = require("@budibase/backend-core/constants") -const { jwt } = require("@budibase/backend-core/auth") -const { doInTenant, doWithGlobalDB } = require("@budibase/backend-core/tenancy") -const { createASession } = require("@budibase/backend-core/sessions") -const { user: userCache } = require("@budibase/backend-core/cache") const newid = require("../../db/newid") -const context = require("@budibase/backend-core/context") -const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db") -const { encrypt } = require("@budibase/backend-core/encryption") const { DocumentType, generateUserMetadataID } = require("../../db/utils") const { startup } = require("../../startup") @@ -83,7 +85,7 @@ class TestConfiguration { if (!appId) { appId = this.appId } - return doInTenant(TENANT_ID, () => { + return tenancy.doInTenant(TENANT_ID, () => { // check if already in a context if (context.getAppId() == null && appId !== null) { return context.doInAppContext(appId, async () => { @@ -155,7 +157,7 @@ class TestConfiguration { email = EMAIL, roles, } = {}) { - return doWithGlobalDB(TENANT_ID, async db => { + return tenancy.doWithGlobalDB(TENANT_ID, async db => { let existing try { existing = await db.get(id) @@ -170,7 +172,7 @@ class TestConfiguration { firstName, lastName, } - await createASession(id, { + await sessions.createASession(id, { sessionId: "sessionid", tenantId: TENANT_ID, csrfToken: CSRF_TOKEN, @@ -212,7 +214,7 @@ class TestConfiguration { admin, roles, }) - await userCache.invalidateUser(globalId) + await cache.user.invalidateUser(globalId) return { ...resp, globalId, @@ -227,19 +229,19 @@ class TestConfiguration { throw "Server has not been opened, cannot login." } // make sure the user exists in the global DB - if (roleId !== BUILTIN_ROLE_IDS.PUBLIC) { + if (roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) { await this.globalUser({ id: userId, builder, roles: { [this.prodAppId]: roleId }, }) } - await createASession(userId, { + await sessions.createASession(userId, { sessionId: "sessionid", tenantId: TENANT_ID, }) // have to fake this - const auth = { + const authObj = { userId, sessionId: "sessionid", tenantId: TENANT_ID, @@ -248,45 +250,45 @@ class TestConfiguration { roleId: roleId, appId, } - const authToken = jwt.sign(auth, env.JWT_SECRET) - const appToken = jwt.sign(app, env.JWT_SECRET) + const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) + const appToken = auth.jwt.sign(app, env.JWT_SECRET) // returning necessary request headers - await userCache.invalidateUser(userId) + await cache.user.invalidateUser(userId) return { Accept: "application/json", Cookie: [ - `${Cookie.Auth}=${authToken}`, - `${Cookie.CurrentApp}=${appToken}`, + `${constants.Cookie.Auth}=${authToken}`, + `${constants.Cookie.CurrentApp}=${appToken}`, ], - [Header.APP_ID]: appId, + [constants.Header.APP_ID]: appId, } }) } defaultHeaders(extras = {}) { - const auth = { + const authObj = { userId: GLOBAL_USER_ID, sessionId: "sessionid", tenantId: TENANT_ID, } const app = { - roleId: BUILTIN_ROLE_IDS.ADMIN, + roleId: roles.BUILTIN_ROLE_IDS.ADMIN, appId: this.appId, } - const authToken = jwt.sign(auth, env.JWT_SECRET) - const appToken = jwt.sign(app, env.JWT_SECRET) + const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) + const appToken = auth.jwt.sign(app, env.JWT_SECRET) const headers = { Accept: "application/json", Cookie: [ - `${Cookie.Auth}=${authToken}`, - `${Cookie.CurrentApp}=${appToken}`, + `${constants.Cookie.Auth}=${authToken}`, + `${constants.Cookie.CurrentApp}=${appToken}`, ], - [Header.CSRF_TOKEN]: CSRF_TOKEN, + [constants.Header.CSRF_TOKEN]: CSRF_TOKEN, ...extras, } if (this.appId) { - headers[Header.APP_ID] = this.appId + headers[constants.Header.APP_ID] = this.appId } return headers } @@ -298,14 +300,14 @@ class TestConfiguration { Accept: "application/json", } if (appId) { - headers[Header.APP_ID] = appId + headers[constants.Header.APP_ID] = appId } return headers } async roleHeaders({ email = EMAIL, - roleId = BUILTIN_ROLE_IDS.ADMIN, + roleId = roles.BUILTIN_ROLE_IDS.ADMIN, builder = false, prodApp = true, } = {}) { @@ -315,15 +317,17 @@ class TestConfiguration { // API async generateApiKey(userId = GLOBAL_USER_ID) { - return doWithGlobalDB(TENANT_ID, async db => { - const id = generateDevInfoID(userId) + return tenancy.doWithGlobalDB(TENANT_ID, async db => { + const id = dbCore.generateDevInfoID(userId) let devInfo try { devInfo = await db.get(id) } catch (err) { devInfo = { _id: id, userId } } - devInfo.apiKey = encrypt(`${TENANT_ID}${SEPARATOR}${newid()}`) + devInfo.apiKey = encryption.encrypt( + `${TENANT_ID}${dbCore.SEPARATOR}${newid()}` + ) await db.put(devInfo) return devInfo.apiKey }) diff --git a/packages/server/src/tests/utilities/structures.js b/packages/server/src/tests/utilities/structures.js index 39361c5d32..610084b04e 100644 --- a/packages/server/src/tests/utilities/structures.js +++ b/packages/server/src/tests/utilities/structures.js @@ -1,5 +1,4 @@ -const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") -const { BuiltinPermissionID } = require("@budibase/backend-core/permissions") +const { roles, permissions } = require("@budibase/backend-core") const { createHomeScreen } = require("../../constants/screens") const { EMPTY_LAYOUT } = require("../../constants/layouts") const { cloneDeep } = require("lodash/fp") @@ -134,8 +133,8 @@ exports.basicLinkedRow = (tableId, linkedRowId, linkField = "link") => { exports.basicRole = () => { return { name: "NewRole", - inherits: BUILTIN_ROLE_IDS.BASIC, - permissionId: BuiltinPermissionID.READ_ONLY, + inherits: roles.BUILTIN_ROLE_IDS.BASIC, + permissionId: permissions.BuiltinPermissionID.READ_ONLY, } } diff --git a/packages/server/src/utilities/fileSystem/clientLibrary.js b/packages/server/src/utilities/fileSystem/clientLibrary.js index 4d3ad551cd..37faa4256f 100644 --- a/packages/server/src/utilities/fileSystem/clientLibrary.js +++ b/packages/server/src/utilities/fileSystem/clientLibrary.js @@ -1,7 +1,7 @@ const { join } = require("path") const { ObjectStoreBuckets } = require("../../constants") const fs = require("fs") -const { upload, retrieveToTmp, streamUpload } = require("./utilities") +const { objectStore } = require("@budibase/backend-core") const { resolve } = require("../centralPath") const env = require("../../environment") const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..") @@ -38,13 +38,13 @@ exports.backupClientLibrary = async appId => { let tmpManifestPath try { // Try to load the manifest from the new file location - tmpManifestPath = await retrieveToTmp( + tmpManifestPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "manifest.json") ) } catch (error) { // Fallback to loading it from the old location for old apps - tmpManifestPath = await retrieveToTmp( + tmpManifestPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join( appId, @@ -58,19 +58,19 @@ exports.backupClientLibrary = async appId => { } // Copy existing client lib to tmp - const tmpClientPath = await retrieveToTmp( + const tmpClientPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "budibase-client.js") ) // Upload manifest and client library as backups - const manifestUpload = upload({ + const manifestUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "manifest.json.bak"), path: tmpManifestPath, type: "application/json", }) - const clientUpload = upload({ + const clientUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "budibase-client.js.bak"), path: tmpClientPath, @@ -99,7 +99,7 @@ exports.updateClientLibrary = async appId => { } // Upload latest manifest and client library - const manifestUpload = streamUpload( + const manifestUpload = objectStore.streamUpload( ObjectStoreBuckets.APPS, join(appId, "manifest.json"), fs.createReadStream(manifest), @@ -107,7 +107,7 @@ exports.updateClientLibrary = async appId => { ContentType: "application/json", } ) - const clientUpload = streamUpload( + const clientUpload = objectStore.streamUpload( ObjectStoreBuckets.APPS, join(appId, "budibase-client.js"), fs.createReadStream(client), @@ -126,25 +126,25 @@ exports.updateClientLibrary = async appId => { */ exports.revertClientLibrary = async appId => { // Copy backups manifest to tmp directory - const tmpManifestPath = await retrieveToTmp( + const tmpManifestPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "manifest.json.bak") ) // Copy backup client lib to tmp - const tmpClientPath = await retrieveToTmp( + const tmpClientPath = await objectStore.retrieveToTmp( ObjectStoreBuckets.APPS, join(appId, "budibase-client.js.bak") ) // Upload backups as new versions - const manifestUpload = upload({ + const manifestUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "manifest.json"), path: tmpManifestPath, type: "application/json", }) - const clientUpload = upload({ + const clientUpload = objectStore.upload({ bucket: ObjectStoreBuckets.APPS, filename: join(appId, "budibase-client.js"), path: tmpClientPath, diff --git a/packages/server/src/utilities/fileSystem/index.js b/packages/server/src/utilities/fileSystem/index.js index 1eb8a481e5..5b46565897 100644 --- a/packages/server/src/utilities/fileSystem/index.js +++ b/packages/server/src/utilities/fileSystem/index.js @@ -2,7 +2,13 @@ const { budibaseTempDir } = require("../budibaseDir") const fs = require("fs") const { join } = require("path") const uuid = require("uuid/v4") +const { context, objectStore } = require("@budibase/backend-core") const { ObjectStoreBuckets } = require("../../constants") +const { updateClientLibrary } = require("./clientLibrary") +const { checkSlashesInUrl } = require("../") +const env = require("../../environment") +const tar = require("tar") +const fetch = require("node-fetch") const { upload, retrieve, @@ -11,13 +17,7 @@ const { downloadTarball, downloadTarballDirect, deleteFiles, -} = require("./utilities") -const { updateClientLibrary } = require("./clientLibrary") -const { checkSlashesInUrl } = require("../") -const env = require("../../environment") -const { getAppId } = require("@budibase/backend-core/context") -const tar = require("tar") -const fetch = require("node-fetch") +} = objectStore const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..") const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules") @@ -165,7 +165,7 @@ exports.downloadTemplate = async (type, name) => { * Retrieves component libraries from object store (or tmp symlink if in local) */ exports.getComponentLibraryManifest = async library => { - const appId = getAppId() + const appId = context.getAppId() const filename = "manifest.json" /* istanbul ignore next */ // when testing in cypress and so on we need to get the package diff --git a/packages/server/src/utilities/fileSystem/utilities.js b/packages/server/src/utilities/fileSystem/utilities.js deleted file mode 100644 index 01ba58f5bc..0000000000 --- a/packages/server/src/utilities/fileSystem/utilities.js +++ /dev/null @@ -1,36 +0,0 @@ -const { - ObjectStore, - makeSureBucketExists, - upload, - deleteFiles, - streamUpload, - retrieve, - retrieveToTmp, - retrieveDirectory, - deleteFolder, - uploadDirectory, - downloadTarball, - downloadTarballDirect, -} = require("@budibase/backend-core/objectStore") - -/*********************************** - * NOTE * - * This file purely exists so that * - * the object store functionality * - * can easily be mocked out of * - * the server without mocking the * - * entire core library. * - ***********************************/ - -exports.ObjectStore = ObjectStore -exports.makeSureBucketExists = makeSureBucketExists -exports.upload = upload -exports.streamUpload = streamUpload -exports.retrieve = retrieve -exports.retrieveToTmp = retrieveToTmp -exports.retrieveDirectory = retrieveDirectory -exports.deleteFolder = deleteFolder -exports.uploadDirectory = uploadDirectory -exports.downloadTarball = downloadTarball -exports.downloadTarballDirect = downloadTarballDirect -exports.deleteFiles = deleteFiles diff --git a/packages/server/src/utilities/rowProcessor/index.js b/packages/server/src/utilities/rowProcessor/index.ts similarity index 66% rename from packages/server/src/utilities/rowProcessor/index.js rename to packages/server/src/utilities/rowProcessor/index.ts index 91daa1b5a0..b075e11c8c 100644 --- a/packages/server/src/utilities/rowProcessor/index.js +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -1,129 +1,40 @@ -const linkRows = require("../../db/linkedRows") +import linkRows from "../../db/linkedRows" +import { FieldTypes, AutoFieldSubTypes } from "../../constants" +import { attachmentsRelativeURL } from "../index" +import { processFormulas, fixAutoColumnSubType } from "./utils" +import { ObjectStoreBuckets } from "../../constants" +import { context, db as dbCore, objectStore } from "@budibase/backend-core" +import { InternalTables } from "../../db/utils" +import { TYPE_TRANSFORM_MAP } from "./map" +import { Row, User, Table } from "@budibase/types" const { cloneDeep } = require("lodash/fp") -const { FieldTypes, AutoFieldSubTypes } = require("../../constants") -const { attachmentsRelativeURL } = require("../index") -const { processFormulas, fixAutoColumnSubType } = require("./utils") -const { deleteFiles } = require("../../utilities/fileSystem/utilities") -const { ObjectStoreBuckets } = require("../../constants") -const { - isProdAppID, - getProdAppID, - dbExists, -} = require("@budibase/backend-core/db") -const { getAppId } = require("@budibase/backend-core/context") -const { InternalTables } = require("../../db/utils") + +type AutoColumnProcessingOpts = { + reprocessing?: boolean + noAutoRelationships?: boolean +} const BASE_AUTO_ID = 1 -/** - * A map of how we convert various properties in rows to each other based on the row type. - */ -const TYPE_TRANSFORM_MAP = { - [FieldTypes.LINK]: { - "": [], - [null]: [], - [undefined]: undefined, - parse: link => { - if (Array.isArray(link) && typeof link[0] === "object") { - return link.map(el => (el && el._id ? el._id : el)) - } - if (typeof link === "string") { - return [link] - } - return link - }, - }, - [FieldTypes.OPTIONS]: { - "": null, - [null]: null, - [undefined]: undefined, - }, - [FieldTypes.ARRAY]: { - "": [], - [null]: [], - [undefined]: undefined, - }, - [FieldTypes.STRING]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.BARCODEQR]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.FORMULA]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.LONGFORM]: { - "": "", - [null]: "", - [undefined]: undefined, - }, - [FieldTypes.NUMBER]: { - "": null, - [null]: null, - [undefined]: undefined, - parse: n => parseFloat(n), - }, - [FieldTypes.DATETIME]: { - "": null, - [undefined]: undefined, - [null]: null, - parse: date => { - if (date instanceof Date) { - return date.toISOString() - } - return date - }, - }, - [FieldTypes.ATTACHMENT]: { - "": [], - [null]: [], - [undefined]: undefined, - }, - [FieldTypes.BOOLEAN]: { - "": null, - [null]: null, - [undefined]: undefined, - true: true, - false: false, - }, - [FieldTypes.AUTO]: { - parse: () => undefined, - }, - [FieldTypes.JSON]: { - parse: input => { - try { - if (input === "") { - return undefined - } - return JSON.parse(input) - } catch (err) { - return input - } - }, - }, -} - /** * Given the old state of the row and the new one after an update, this will * find the keys that have been removed in the updated row. */ -function getRemovedAttachmentKeys(oldRow, row, attachmentKey) { +function getRemovedAttachmentKeys( + oldRow: Row, + row: Row, + attachmentKey: string +) { if (!oldRow[attachmentKey]) { return [] } - const oldKeys = oldRow[attachmentKey].map(attachment => attachment.key) + const oldKeys = oldRow[attachmentKey].map((attachment: any) => attachment.key) // no attachments in new row, all removed if (!row[attachmentKey]) { return oldKeys } - const newKeys = row[attachmentKey].map(attachment => attachment.key) - return oldKeys.filter(key => newKeys.indexOf(key) === -1) + const newKeys = row[attachmentKey].map((attachment: any) => attachment.key) + return oldKeys.filter((key: any) => newKeys.indexOf(key) === -1) } /** @@ -136,11 +47,11 @@ function getRemovedAttachmentKeys(oldRow, row, attachmentKey) { * @returns {{row: Object, table: Object}} The updated row and table, the table may need to be updated * for automatic ID purposes. */ -function processAutoColumn( - user, - table, - row, - opts = { reprocessing: false, noAutoRelationships: false } +export function processAutoColumn( + user: User, + table: Table, + row: Row, + opts: AutoColumnProcessingOpts ) { let noUser = !user || !user.userId let isUserTable = table._id === InternalTables.USER_METADATA @@ -186,9 +97,6 @@ function processAutoColumn( } return { table, row } } -exports.processAutoColumn = processAutoColumn -exports.fixAutoColumnSubType = fixAutoColumnSubType -exports.processFormulas = processFormulas /** * This will coerce a value to the correct types based on the type transform map @@ -196,15 +104,17 @@ exports.processFormulas = processFormulas * @param {object} type The type fo coerce to * @returns {object} The coerced value */ -exports.coerce = (row, type) => { +export function coerce(row: any, type: any) { // no coercion specified for type, skip it if (!TYPE_TRANSFORM_MAP[type]) { return row } // eslint-disable-next-line no-prototype-builtins if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) { + // @ts-ignore return TYPE_TRANSFORM_MAP[type][row] } else if (TYPE_TRANSFORM_MAP[type].parse) { + // @ts-ignore return TYPE_TRANSFORM_MAP[type].parse(row) } @@ -220,12 +130,12 @@ exports.coerce = (row, type) => { * @param {object} opts some input processing options (like disabling auto-column relationships). * @returns {object} the row which has been prepared to be written to the DB. */ -exports.inputProcessing = ( - user = {}, - table, - row, - opts = { noAutoRelationships: false } -) => { +export function inputProcessing( + user: User, + table: Table, + row: Row, + opts: AutoColumnProcessingOpts +) { let clonedRow = cloneDeep(row) // need to copy the table so it can be differenced on way out const copiedTable = cloneDeep(table) @@ -245,7 +155,7 @@ exports.inputProcessing = ( } // otherwise coerce what is there to correct types else { - clonedRow[key] = exports.coerce(value, field.type) + clonedRow[key] = coerce(value, field.type) } } @@ -267,7 +177,11 @@ exports.inputProcessing = ( * @param {object} opts used to set some options for the output, such as disabling relationship squashing. * @returns {object[]|object} the enriched rows will be returned. */ -exports.outputProcessing = async (table, rows, opts = { squash: true }) => { +export async function outputProcessing( + table: Table, + rows: Row[], + opts = { squash: true } +) { let wasArray = true if (!(rows instanceof Array)) { rows = [rows] @@ -286,7 +200,7 @@ exports.outputProcessing = async (table, rows, opts = { squash: true }) => { if (row[property] == null || !Array.isArray(row[property])) { continue } - row[property].forEach(attachment => { + row[property].forEach((attachment: any) => { attachment.url = attachmentsRelativeURL(attachment.key) }) } @@ -308,20 +222,28 @@ exports.outputProcessing = async (table, rows, opts = { squash: true }) => { * deleted attachment columns. * @return {Promise} When all attachments have been removed this will return. */ -exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => { - const appId = getAppId() - if (!isProdAppID(appId)) { - const prodAppId = getProdAppID(appId) +export async function cleanupAttachments( + table: Table, + { + row, + rows, + oldRow, + oldTable, + }: { row?: Row; rows?: Row[]; oldRow?: Row; oldTable: Table } +): Promise { + const appId = context.getAppId() + if (!dbCore.isProdAppID(appId)) { + const prodAppId = dbCore.getProdAppID(appId!) // if prod exists, then don't allow deleting - const exists = await dbExists(prodAppId) + const exists = await dbCore.dbExists(prodAppId) if (exists) { return } } - let files = [] - function addFiles(row, key) { + let files: string[] = [] + function addFiles(row: Row, key: string) { if (row[key]) { - files = files.concat(row[key].map(attachment => attachment.key)) + files = files.concat(row[key].map((attachment: any) => attachment.key)) } } const schemaToUse = oldTable ? oldTable.schema : table.schema @@ -330,7 +252,7 @@ exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => { continue } // old table had this column, new table doesn't - delete it - if (oldTable && !table.schema[key]) { + if (rows && oldTable && !table.schema[key]) { rows.forEach(row => addFiles(row, key)) } else if (oldRow && row) { // if updating, need to manage the differences @@ -342,6 +264,6 @@ exports.cleanupAttachments = async (table, { row, rows, oldRow, oldTable }) => { } } if (files.length > 0) { - return deleteFiles(ObjectStoreBuckets.APPS, files) + return objectStore.deleteFiles(ObjectStoreBuckets.APPS, files) } } diff --git a/packages/server/src/utilities/rowProcessor/map.js b/packages/server/src/utilities/rowProcessor/map.js new file mode 100644 index 0000000000..4e05868bfc --- /dev/null +++ b/packages/server/src/utilities/rowProcessor/map.js @@ -0,0 +1,95 @@ +const { FieldTypes } = require("../../constants") + +/** + * A map of how we convert various properties in rows to each other based on the row type. + */ +exports.TYPE_TRANSFORM_MAP = { + [FieldTypes.LINK]: { + "": [], + [null]: [], + [undefined]: undefined, + parse: link => { + if (Array.isArray(link) && typeof link[0] === "object") { + return link.map(el => (el && el._id ? el._id : el)) + } + if (typeof link === "string") { + return [link] + } + return link + }, + }, + [FieldTypes.OPTIONS]: { + "": null, + [null]: null, + [undefined]: undefined, + }, + [FieldTypes.ARRAY]: { + "": [], + [null]: [], + [undefined]: undefined, + }, + [FieldTypes.STRING]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.BARCODEQR]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.FORMULA]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.LONGFORM]: { + "": "", + [null]: "", + [undefined]: undefined, + }, + [FieldTypes.NUMBER]: { + "": null, + [null]: null, + [undefined]: undefined, + parse: n => parseFloat(n), + }, + [FieldTypes.DATETIME]: { + "": null, + [undefined]: undefined, + [null]: null, + parse: date => { + if (date instanceof Date) { + return date.toISOString() + } + return date + }, + }, + [FieldTypes.ATTACHMENT]: { + "": [], + [null]: [], + [undefined]: undefined, + }, + [FieldTypes.BOOLEAN]: { + "": null, + [null]: null, + [undefined]: undefined, + true: true, + false: false, + }, + [FieldTypes.AUTO]: { + parse: () => undefined, + }, + [FieldTypes.JSON]: { + parse: input => { + try { + if (input === "") { + return undefined + } + return JSON.parse(input) + } catch (err) { + return input + } + }, + }, +} diff --git a/packages/server/src/utilities/rowProcessor/utils.js b/packages/server/src/utilities/rowProcessor/utils.ts similarity index 86% rename from packages/server/src/utilities/rowProcessor/utils.js rename to packages/server/src/utilities/rowProcessor/utils.ts index d659cb6822..7853775f19 100644 --- a/packages/server/src/utilities/rowProcessor/utils.js +++ b/packages/server/src/utilities/rowProcessor/utils.ts @@ -1,16 +1,17 @@ -const { +import { FieldTypes, FormulaTypes, AutoFieldDefaultNames, AutoFieldSubTypes, -} = require("../../constants") -const { processStringSync } = require("@budibase/string-templates") +} from "../../constants" +import { processStringSync } from "@budibase/string-templates" +import { FieldSchema, Table, Row } from "@budibase/types" /** * If the subtype has been lost for any reason this works out what * subtype the auto column should be. */ -exports.fixAutoColumnSubType = column => { +export function fixAutoColumnSubType(column: FieldSchema) { if (!column.autocolumn || !column.name || column.subtype) { return column } @@ -32,11 +33,11 @@ exports.fixAutoColumnSubType = column => { /** * Looks through the rows provided and finds formulas - which it then processes. */ -exports.processFormulas = ( - table, - rows, - { dynamic, contextRows } = { dynamic: true } -) => { +export function processFormulas( + table: Table, + rows: Row[], + { dynamic, contextRows }: any = { dynamic: true } +) { const single = !Array.isArray(rows) if (single) { rows = [rows] @@ -70,7 +71,7 @@ exports.processFormulas = ( * Processes any date columns and ensures that those without the ignoreTimezones * flag set are parsed as UTC rather than local time. */ -exports.processDates = (table, rows) => { +export function processDates(table: Table, rows: Row[]) { let datesWithTZ = [] for (let [column, schema] of Object.entries(table.schema)) { if (schema.type !== FieldTypes.DATETIME) { diff --git a/packages/types/src/documents/app/table.ts b/packages/types/src/documents/app/table.ts index 1d559c5bef..8b6dfc1519 100644 --- a/packages/types/src/documents/app/table.ts +++ b/packages/types/src/documents/app/table.ts @@ -19,6 +19,8 @@ export interface FieldSchema { formulaType?: string main?: boolean ignoreTimezones?: boolean + timeOnly?: boolean + lastID?: number meta?: { toTable: string toKey: string diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 6c93bac1ac..9a1fb472f0 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -17,6 +17,7 @@ export interface User extends Document { userGroups?: string[] forceResetPassword?: boolean dayPassRecordedAt?: string + userId?: string } export interface UserRoles {