Some typescript conversions, moving a few imports around.
This commit is contained in:
parent
082b0851db
commit
de82eca502
|
@ -390,7 +390,7 @@ export const uploadDirectory = async (
|
|||
return files
|
||||
}
|
||||
|
||||
exports.downloadTarballDirect = async (
|
||||
export const downloadTarballDirect = async (
|
||||
url: string,
|
||||
path: string,
|
||||
headers = {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
/**
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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<void>} 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<any> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
|
@ -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) {
|
|
@ -19,6 +19,8 @@ export interface FieldSchema {
|
|||
formulaType?: string
|
||||
main?: boolean
|
||||
ignoreTimezones?: boolean
|
||||
timeOnly?: boolean
|
||||
lastID?: number
|
||||
meta?: {
|
||||
toTable: string
|
||||
toKey: string
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface User extends Document {
|
|||
userGroups?: string[]
|
||||
forceResetPassword?: boolean
|
||||
dayPassRecordedAt?: string
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export interface UserRoles {
|
||||
|
|
Loading…
Reference in New Issue