Main body of work, refactoring most usages.
This commit is contained in:
parent
d8ab715267
commit
d2fe119d90
|
@ -0,0 +1,15 @@
|
||||||
|
const {
|
||||||
|
getAppDB,
|
||||||
|
getDevAppDB,
|
||||||
|
getProdAppDB,
|
||||||
|
getAppId,
|
||||||
|
updateAppId,
|
||||||
|
} = require("./src/tenancy/context")
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getAppDB,
|
||||||
|
getDevAppDB,
|
||||||
|
getProdAppDB,
|
||||||
|
getAppId,
|
||||||
|
updateAppId,
|
||||||
|
}
|
|
@ -250,11 +250,10 @@ exports.getAllDbs = async () => {
|
||||||
/**
|
/**
|
||||||
* Lots of different points in the system need to find the full list of apps, this will
|
* Lots of different points in the system need to find the full list of apps, this will
|
||||||
* enumerate the entire CouchDB cluster and get the list of databases (every app).
|
* enumerate the entire CouchDB cluster and get the list of databases (every app).
|
||||||
* NOTE: this operation is fine in self hosting, but cannot be used when hosting many
|
|
||||||
* different users/companies apps as there is no security around it - all apps are returned.
|
|
||||||
* @return {Promise<object[]>} returns the app information document stored in each app database.
|
* @return {Promise<object[]>} returns the app information document stored in each app database.
|
||||||
*/
|
*/
|
||||||
exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => {
|
exports.getAllApps = async ({ dev, all, idsOnly } = {}) => {
|
||||||
|
const CouchDB = getCouch()
|
||||||
let tenantId = getTenantId()
|
let tenantId = getTenantId()
|
||||||
if (!env.MULTI_TENANCY && !tenantId) {
|
if (!env.MULTI_TENANCY && !tenantId) {
|
||||||
tenantId = DEFAULT_TENANT_ID
|
tenantId = DEFAULT_TENANT_ID
|
||||||
|
@ -310,8 +309,8 @@ exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => {
|
||||||
/**
|
/**
|
||||||
* Utility function for getAllApps but filters to production apps only.
|
* Utility function for getAllApps but filters to production apps only.
|
||||||
*/
|
*/
|
||||||
exports.getDeployedAppIDs = async CouchDB => {
|
exports.getDeployedAppIDs = async () => {
|
||||||
return (await exports.getAllApps(CouchDB, { idsOnly: true })).filter(
|
return (await exports.getAllApps({ idsOnly: true })).filter(
|
||||||
id => !exports.isDevAppID(id)
|
id => !exports.isDevAppID(id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -319,13 +318,14 @@ exports.getDeployedAppIDs = async CouchDB => {
|
||||||
/**
|
/**
|
||||||
* Utility function for the inverse of above.
|
* Utility function for the inverse of above.
|
||||||
*/
|
*/
|
||||||
exports.getDevAppIDs = async CouchDB => {
|
exports.getDevAppIDs = async () => {
|
||||||
return (await exports.getAllApps(CouchDB, { idsOnly: true })).filter(id =>
|
return (await exports.getAllApps({ idsOnly: true })).filter(id =>
|
||||||
exports.isDevAppID(id)
|
exports.isDevAppID(id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.dbExists = async (CouchDB, dbName) => {
|
exports.dbExists = async dbName => {
|
||||||
|
const CouchDB = getCouch()
|
||||||
let exists = false
|
let exists = false
|
||||||
try {
|
try {
|
||||||
const db = CouchDB(dbName, { skip_setup: true })
|
const db = CouchDB(dbName, { skip_setup: true })
|
||||||
|
|
|
@ -3,6 +3,7 @@ const {
|
||||||
updateTenantId,
|
updateTenantId,
|
||||||
isTenantIdSet,
|
isTenantIdSet,
|
||||||
DEFAULT_TENANT_ID,
|
DEFAULT_TENANT_ID,
|
||||||
|
updateAppId,
|
||||||
} = require("../tenancy")
|
} = require("../tenancy")
|
||||||
const ContextFactory = require("../tenancy/FunctionContext")
|
const ContextFactory = require("../tenancy/FunctionContext")
|
||||||
const { getTenantIDFromAppID } = require("../db/utils")
|
const { getTenantIDFromAppID } = require("../db/utils")
|
||||||
|
@ -21,5 +22,6 @@ module.exports = () => {
|
||||||
const appId = ctx.appId ? ctx.appId : ctx.user ? ctx.user.appId : null
|
const appId = ctx.appId ? ctx.appId : ctx.user ? ctx.user.appId : null
|
||||||
const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
const tenantId = getTenantIDFromAppID(appId) || DEFAULT_TENANT_ID
|
||||||
updateTenantId(tenantId)
|
updateTenantId(tenantId)
|
||||||
|
updateAppId(appId)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const { getDB } = require("../db")
|
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const { BUILTIN_PERMISSION_IDS } = require("./permissions")
|
const { BUILTIN_PERMISSION_IDS } = require("./permissions")
|
||||||
const {
|
const {
|
||||||
|
@ -7,6 +6,7 @@ const {
|
||||||
DocumentTypes,
|
DocumentTypes,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
} = require("../db/utils")
|
} = require("../db/utils")
|
||||||
|
const { getAppDB } = require("../tenancy/context")
|
||||||
|
|
||||||
const BUILTIN_IDS = {
|
const BUILTIN_IDS = {
|
||||||
ADMIN: "ADMIN",
|
ADMIN: "ADMIN",
|
||||||
|
@ -111,11 +111,10 @@ exports.lowerBuiltinRoleID = (roleId1, roleId2) => {
|
||||||
/**
|
/**
|
||||||
* Gets the role object, this is mainly useful for two purposes, to check if the level exists and
|
* Gets the role object, this is mainly useful for two purposes, to check if the level exists and
|
||||||
* to check if the role inherits any others.
|
* to check if the role inherits any others.
|
||||||
* @param {string} appId The app in which to look for the role.
|
|
||||||
* @param {string|null} roleId The level ID to lookup.
|
* @param {string|null} roleId The level ID to lookup.
|
||||||
* @returns {Promise<Role|object|null>} The role object, which may contain an "inherits" property.
|
* @returns {Promise<Role|object|null>} The role object, which may contain an "inherits" property.
|
||||||
*/
|
*/
|
||||||
exports.getRole = async (appId, roleId) => {
|
exports.getRole = async roleId => {
|
||||||
if (!roleId) {
|
if (!roleId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -128,7 +127,7 @@ exports.getRole = async (appId, roleId) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const db = getDB(appId)
|
const db = getAppDB()
|
||||||
const dbRole = await db.get(exports.getDBRoleID(roleId))
|
const dbRole = await db.get(exports.getDBRoleID(roleId))
|
||||||
role = Object.assign(role, dbRole)
|
role = Object.assign(role, dbRole)
|
||||||
// finalise the ID
|
// finalise the ID
|
||||||
|
@ -145,11 +144,11 @@ exports.getRole = async (appId, roleId) => {
|
||||||
/**
|
/**
|
||||||
* Simple function to get all the roles based on the top level user role ID.
|
* Simple function to get all the roles based on the top level user role ID.
|
||||||
*/
|
*/
|
||||||
async function getAllUserRoles(appId, userRoleId) {
|
async function getAllUserRoles(userRoleId) {
|
||||||
if (!userRoleId) {
|
if (!userRoleId) {
|
||||||
return [BUILTIN_IDS.BASIC]
|
return [BUILTIN_IDS.BASIC]
|
||||||
}
|
}
|
||||||
let currentRole = await exports.getRole(appId, userRoleId)
|
let currentRole = await exports.getRole(userRoleId)
|
||||||
let roles = currentRole ? [currentRole] : []
|
let roles = currentRole ? [currentRole] : []
|
||||||
let roleIds = [userRoleId]
|
let roleIds = [userRoleId]
|
||||||
// get all the inherited roles
|
// get all the inherited roles
|
||||||
|
@ -159,7 +158,7 @@ async function getAllUserRoles(appId, userRoleId) {
|
||||||
roleIds.indexOf(currentRole.inherits) === -1
|
roleIds.indexOf(currentRole.inherits) === -1
|
||||||
) {
|
) {
|
||||||
roleIds.push(currentRole.inherits)
|
roleIds.push(currentRole.inherits)
|
||||||
currentRole = await exports.getRole(appId, currentRole.inherits)
|
currentRole = await exports.getRole(currentRole.inherits)
|
||||||
roles.push(currentRole)
|
roles.push(currentRole)
|
||||||
}
|
}
|
||||||
return roles
|
return roles
|
||||||
|
@ -168,29 +167,23 @@ async function getAllUserRoles(appId, userRoleId) {
|
||||||
/**
|
/**
|
||||||
* Returns an ordered array of the user's inherited role IDs, this can be used
|
* Returns an ordered array of the user's inherited role IDs, this can be used
|
||||||
* to determine if a user can access something that requires a specific role.
|
* to determine if a user can access something that requires a specific role.
|
||||||
* @param {string} appId The ID of the application from which roles should be obtained.
|
|
||||||
* @param {string} userRoleId The user's role ID, this can be found in their access token.
|
* @param {string} userRoleId The user's role ID, this can be found in their access token.
|
||||||
* @param {object} opts Various options, such as whether to only retrieve the IDs (default true).
|
* @param {object} opts Various options, such as whether to only retrieve the IDs (default true).
|
||||||
* @returns {Promise<string[]>} returns an ordered array of the roles, with the first being their
|
* @returns {Promise<string[]>} returns an ordered array of the roles, with the first being their
|
||||||
* highest level of access and the last being the lowest level.
|
* highest level of access and the last being the lowest level.
|
||||||
*/
|
*/
|
||||||
exports.getUserRoleHierarchy = async (
|
exports.getUserRoleHierarchy = async (userRoleId, opts = { idOnly: true }) => {
|
||||||
appId,
|
|
||||||
userRoleId,
|
|
||||||
opts = { idOnly: true }
|
|
||||||
) => {
|
|
||||||
// special case, if they don't have a role then they are a public user
|
// special case, if they don't have a role then they are a public user
|
||||||
const roles = await getAllUserRoles(appId, userRoleId)
|
const roles = await getAllUserRoles(userRoleId)
|
||||||
return opts.idOnly ? roles.map(role => role._id) : roles
|
return opts.idOnly ? roles.map(role => role._id) : roles
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an app ID this will retrieve all of the roles that are currently within that app.
|
* Given an app ID this will retrieve all of the roles that are currently within that app.
|
||||||
* @param {string} appId The ID of the app to retrieve the roles from.
|
|
||||||
* @return {Promise<object[]>} An array of the role objects that were found.
|
* @return {Promise<object[]>} An array of the role objects that were found.
|
||||||
*/
|
*/
|
||||||
exports.getAllRoles = async appId => {
|
exports.getAllRoles = async () => {
|
||||||
const db = getDB(appId)
|
const db = getAppDB()
|
||||||
const body = await db.allDocs(
|
const body = await db.allDocs(
|
||||||
getRoleParams(null, {
|
getRoleParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -218,19 +211,17 @@ exports.getAllRoles = async appId => {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This retrieves the required role/
|
* This retrieves the required role
|
||||||
* @param appId
|
|
||||||
* @param permLevel
|
* @param permLevel
|
||||||
* @param resourceId
|
* @param resourceId
|
||||||
* @param subResourceId
|
* @param subResourceId
|
||||||
* @return {Promise<{permissions}|Object>}
|
* @return {Promise<{permissions}|Object>}
|
||||||
*/
|
*/
|
||||||
exports.getRequiredResourceRole = async (
|
exports.getRequiredResourceRole = async (
|
||||||
appId,
|
|
||||||
permLevel,
|
permLevel,
|
||||||
{ resourceId, subResourceId }
|
{ resourceId, subResourceId }
|
||||||
) => {
|
) => {
|
||||||
const roles = await exports.getAllRoles(appId)
|
const roles = await exports.getAllRoles()
|
||||||
let main = [],
|
let main = [],
|
||||||
sub = []
|
sub = []
|
||||||
for (let role of roles) {
|
for (let role of roles) {
|
||||||
|
@ -251,8 +242,7 @@ exports.getRequiredResourceRole = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccessController {
|
class AccessController {
|
||||||
constructor(appId) {
|
constructor() {
|
||||||
this.appId = appId
|
|
||||||
this.userHierarchies = {}
|
this.userHierarchies = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +260,7 @@ class AccessController {
|
||||||
}
|
}
|
||||||
let roleIds = this.userHierarchies[userRoleId]
|
let roleIds = this.userHierarchies[userRoleId]
|
||||||
if (!roleIds) {
|
if (!roleIds) {
|
||||||
roleIds = await exports.getUserRoleHierarchy(this.appId, userRoleId)
|
roleIds = await exports.getUserRoleHierarchy(userRoleId)
|
||||||
this.userHierarchies[userRoleId] = roleIds
|
this.userHierarchies[userRoleId] = roleIds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ const { newid } = require("../hashing")
|
||||||
const REQUEST_ID_KEY = "requestId"
|
const REQUEST_ID_KEY = "requestId"
|
||||||
|
|
||||||
class FunctionContext {
|
class FunctionContext {
|
||||||
static getMiddleware(updateCtxFn = null) {
|
static getMiddleware(updateCtxFn = null, contextName = "session") {
|
||||||
const namespace = this.createNamespace()
|
const namespace = this.createNamespace(contextName)
|
||||||
|
|
||||||
return async function (ctx, next) {
|
return async function (ctx, next) {
|
||||||
await new Promise(
|
await new Promise(
|
||||||
|
@ -24,14 +24,14 @@ class FunctionContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static run(callback) {
|
static run(callback, contextName = "session") {
|
||||||
const namespace = this.createNamespace()
|
const namespace = this.createNamespace(contextName)
|
||||||
|
|
||||||
return namespace.runAndReturn(callback)
|
return namespace.runAndReturn(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
static setOnContext(key, value) {
|
static setOnContext(key, value, contextName = "session") {
|
||||||
const namespace = this.createNamespace()
|
const namespace = this.createNamespace(contextName)
|
||||||
namespace.set(key, value)
|
namespace.set(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,16 +55,16 @@ class FunctionContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static destroyNamespace() {
|
static destroyNamespace(name = "session") {
|
||||||
if (this._namespace) {
|
if (this._namespace) {
|
||||||
cls.destroyNamespace("session")
|
cls.destroyNamespace(name)
|
||||||
this._namespace = null
|
this._namespace = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static createNamespace() {
|
static createNamespace(name = "session") {
|
||||||
if (!this._namespace) {
|
if (!this._namespace) {
|
||||||
this._namespace = cls.createNamespace("session")
|
this._namespace = cls.createNamespace(name)
|
||||||
}
|
}
|
||||||
return this._namespace
|
return this._namespace
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const { Headers } = require("../../constants")
|
const { Headers } = require("../../constants")
|
||||||
const cls = require("./FunctionContext")
|
const cls = require("./FunctionContext")
|
||||||
|
const { getCouch } = require("../db")
|
||||||
|
const { getDeployedAppID, getDevelopmentAppID } = require("../db/utils")
|
||||||
|
const { isEqual } = require("lodash")
|
||||||
|
|
||||||
|
// some test cases call functions directly, need to
|
||||||
|
// store an app ID to pretend there is a context
|
||||||
|
let TEST_APP_ID = null
|
||||||
|
|
||||||
|
const ContextKeys = {
|
||||||
|
TENANT_ID: "tenantId",
|
||||||
|
APP_ID: "appId",
|
||||||
|
// whatever the request app DB was
|
||||||
|
CURRENT_DB: "currentDb",
|
||||||
|
// get the prod app DB from the request
|
||||||
|
PROD_DB: "prodDb",
|
||||||
|
// get the dev app DB from the request
|
||||||
|
DEV_DB: "devDb",
|
||||||
|
DB_OPTS: "dbOpts",
|
||||||
|
}
|
||||||
|
|
||||||
exports.DEFAULT_TENANT_ID = "default"
|
exports.DEFAULT_TENANT_ID = "default"
|
||||||
|
|
||||||
|
@ -12,13 +31,11 @@ exports.isMultiTenant = () => {
|
||||||
return env.MULTI_TENANCY
|
return env.MULTI_TENANCY
|
||||||
}
|
}
|
||||||
|
|
||||||
const TENANT_ID = "tenantId"
|
|
||||||
|
|
||||||
// used for automations, API endpoints should always be in context already
|
// used for automations, API endpoints should always be in context already
|
||||||
exports.doInTenant = (tenantId, task) => {
|
exports.doInTenant = (tenantId, task) => {
|
||||||
return cls.run(() => {
|
return cls.run(() => {
|
||||||
// set the tenant id
|
// set the tenant id
|
||||||
cls.setOnContext(TENANT_ID, tenantId)
|
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||||
|
|
||||||
// invoke the task
|
// invoke the task
|
||||||
return task()
|
return task()
|
||||||
|
@ -26,7 +43,19 @@ exports.doInTenant = (tenantId, task) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateTenantId = tenantId => {
|
exports.updateTenantId = tenantId => {
|
||||||
cls.setOnContext(TENANT_ID, tenantId)
|
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.updateAppId = appId => {
|
||||||
|
try {
|
||||||
|
cls.setOnContext(ContextKeys.APP_ID, appId)
|
||||||
|
} catch (err) {
|
||||||
|
if (env.isTest()) {
|
||||||
|
TEST_APP_ID = appId
|
||||||
|
} else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.setTenantId = (
|
exports.setTenantId = (
|
||||||
|
@ -36,7 +65,7 @@ exports.setTenantId = (
|
||||||
let tenantId
|
let tenantId
|
||||||
// exit early if not multi-tenant
|
// exit early if not multi-tenant
|
||||||
if (!exports.isMultiTenant()) {
|
if (!exports.isMultiTenant()) {
|
||||||
cls.setOnContext(TENANT_ID, this.DEFAULT_TENANT_ID)
|
cls.setOnContext(ContextKeys.TENANT_ID, this.DEFAULT_TENANT_ID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,12 +92,12 @@ exports.setTenantId = (
|
||||||
}
|
}
|
||||||
// check tenant ID just incase no tenant was allowed
|
// check tenant ID just incase no tenant was allowed
|
||||||
if (tenantId) {
|
if (tenantId) {
|
||||||
cls.setOnContext(TENANT_ID, tenantId)
|
cls.setOnContext(ContextKeys.TENANT_ID, tenantId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.isTenantIdSet = () => {
|
exports.isTenantIdSet = () => {
|
||||||
const tenantId = cls.getFromContext(TENANT_ID)
|
const tenantId = cls.getFromContext(ContextKeys.TENANT_ID)
|
||||||
return !!tenantId
|
return !!tenantId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,9 +105,77 @@ exports.getTenantId = () => {
|
||||||
if (!exports.isMultiTenant()) {
|
if (!exports.isMultiTenant()) {
|
||||||
return exports.DEFAULT_TENANT_ID
|
return exports.DEFAULT_TENANT_ID
|
||||||
}
|
}
|
||||||
const tenantId = cls.getFromContext(TENANT_ID)
|
const tenantId = cls.getFromContext(ContextKeys.TENANT_ID)
|
||||||
if (!tenantId) {
|
if (!tenantId) {
|
||||||
throw Error("Tenant id not found")
|
throw Error("Tenant id not found")
|
||||||
}
|
}
|
||||||
return tenantId
|
return tenantId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getAppId = () => {
|
||||||
|
const foundId = cls.getFromContext(ContextKeys.APP_ID)
|
||||||
|
if (!foundId && env.isTest() && TEST_APP_ID) {
|
||||||
|
return TEST_APP_ID
|
||||||
|
} else {
|
||||||
|
return foundId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDB(key, opts) {
|
||||||
|
const dbOptsKey = `${key}${ContextKeys.DB_OPTS}`
|
||||||
|
let storedOpts = cls.getFromContext(dbOptsKey)
|
||||||
|
let db = cls.getFromContext(key)
|
||||||
|
if (db && isEqual(opts, storedOpts)) {
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
const appId = exports.getAppId()
|
||||||
|
const CouchDB = getCouch()
|
||||||
|
let toUseAppId
|
||||||
|
switch (key) {
|
||||||
|
case ContextKeys.CURRENT_DB:
|
||||||
|
toUseAppId = appId
|
||||||
|
break
|
||||||
|
case ContextKeys.PROD_DB:
|
||||||
|
toUseAppId = getDeployedAppID(appId)
|
||||||
|
break
|
||||||
|
case ContextKeys.DEV_DB:
|
||||||
|
toUseAppId = getDevelopmentAppID(appId)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
db = new CouchDB(toUseAppId, opts)
|
||||||
|
try {
|
||||||
|
cls.setOnContext(key, db)
|
||||||
|
if (opts) {
|
||||||
|
cls.setOnContext(dbOptsKey, opts)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (!env.isTest()) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the app database based on whatever the request
|
||||||
|
* contained, dev or prod.
|
||||||
|
*/
|
||||||
|
exports.getAppDB = opts => {
|
||||||
|
return getDB(ContextKeys.CURRENT_DB, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This specifically gets the prod app ID, if the request
|
||||||
|
* contained a development app ID, this will open the prod one.
|
||||||
|
*/
|
||||||
|
exports.getProdAppDB = opts => {
|
||||||
|
return getDB(ContextKeys.PROD_DB, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This specifically gets the dev app ID, if the request
|
||||||
|
* contained a prod app ID, this will open the dev one.
|
||||||
|
*/
|
||||||
|
exports.getDevAppDB = opts => {
|
||||||
|
return getDB(ContextKeys.DEV_DB, opts)
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const packageJson = require("../../../package.json")
|
const packageJson = require("../../../package.json")
|
||||||
const {
|
const {
|
||||||
|
@ -45,11 +44,13 @@ const { getTenantId, isMultiTenant } = require("@budibase/backend-core/tenancy")
|
||||||
const { syncGlobalUsers } = require("./user")
|
const { syncGlobalUsers } = require("./user")
|
||||||
const { app: appCache } = require("@budibase/backend-core/cache")
|
const { app: appCache } = require("@budibase/backend-core/cache")
|
||||||
const { cleanupAutomations } = require("../../automations/utils")
|
const { cleanupAutomations } = require("../../automations/utils")
|
||||||
|
const context = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
const URL_REGEX_SLASH = /\/|\\/g
|
const URL_REGEX_SLASH = /\/|\\/g
|
||||||
|
|
||||||
// utility function, need to do away with this
|
// utility function, need to do away with this
|
||||||
async function getLayouts(db) {
|
async function getLayouts() {
|
||||||
|
const db = context.getAppDB()
|
||||||
return (
|
return (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
getLayoutParams(null, {
|
getLayoutParams(null, {
|
||||||
|
@ -59,7 +60,8 @@ async function getLayouts(db) {
|
||||||
).rows.map(row => row.doc)
|
).rows.map(row => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getScreens(db) {
|
async function getScreens() {
|
||||||
|
const db = context.getAppDB()
|
||||||
return (
|
return (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
getScreenParams(null, {
|
getScreenParams(null, {
|
||||||
|
@ -116,8 +118,9 @@ async function createInstance(template) {
|
||||||
const tenantId = isMultiTenant() ? getTenantId() : null
|
const tenantId = isMultiTenant() ? getTenantId() : null
|
||||||
const baseAppId = generateAppID(tenantId)
|
const baseAppId = generateAppID(tenantId)
|
||||||
const appId = generateDevAppID(baseAppId)
|
const appId = generateDevAppID(baseAppId)
|
||||||
|
context.updateAppId(appId)
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = context.getAppDB()
|
||||||
await db.put({
|
await db.put({
|
||||||
_id: "_design/database",
|
_id: "_design/database",
|
||||||
// view collation information, read before writing any complex views:
|
// view collation information, read before writing any complex views:
|
||||||
|
@ -127,9 +130,9 @@ async function createInstance(template) {
|
||||||
|
|
||||||
// NOTE: indexes need to be created before any tables/templates
|
// NOTE: indexes need to be created before any tables/templates
|
||||||
// add view for linked rows
|
// add view for linked rows
|
||||||
await createLinkView(appId)
|
await createLinkView()
|
||||||
await createRoutingView(appId)
|
await createRoutingView()
|
||||||
await createAllSearchIndex(appId)
|
await createAllSearchIndex()
|
||||||
|
|
||||||
// replicate the template data to the instance DB
|
// replicate the template data to the instance DB
|
||||||
// this is currently very hard to test, downloading and importing template files
|
// this is currently very hard to test, downloading and importing template files
|
||||||
|
@ -155,7 +158,7 @@ async function createInstance(template) {
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||||
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
||||||
const apps = await getAllApps(CouchDB, { dev, all })
|
const apps = await getAllApps({ dev, all })
|
||||||
|
|
||||||
// get the locks for all the dev apps
|
// get the locks for all the dev apps
|
||||||
if (dev || all) {
|
if (dev || all) {
|
||||||
|
@ -178,12 +181,11 @@ exports.fetch = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchAppDefinition = async ctx => {
|
exports.fetchAppDefinition = async ctx => {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const layouts = await getLayouts()
|
||||||
const layouts = await getLayouts(db)
|
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new AccessController(ctx.params.appId)
|
const accessController = new AccessController()
|
||||||
const screens = await accessController.checkScreensAccess(
|
const screens = await accessController.checkScreensAccess(
|
||||||
await getScreens(db),
|
await getScreens(),
|
||||||
userRoleId
|
userRoleId
|
||||||
)
|
)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
|
@ -194,15 +196,15 @@ exports.fetchAppDefinition = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchAppPackage = async ctx => {
|
exports.fetchAppPackage = async ctx => {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||||
const layouts = await getLayouts(db)
|
const layouts = await getLayouts()
|
||||||
let screens = await getScreens(db)
|
let screens = await getScreens()
|
||||||
|
|
||||||
// Only filter screens if the user is not a builder
|
// Only filter screens if the user is not a builder
|
||||||
if (!(ctx.user.builder && ctx.user.builder.global)) {
|
if (!(ctx.user.builder && ctx.user.builder.global)) {
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new AccessController(ctx.params.appId)
|
const accessController = new AccessController()
|
||||||
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
screens = await accessController.checkScreensAccess(screens, userRoleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +217,7 @@ exports.fetchAppPackage = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.create = async ctx => {
|
exports.create = async ctx => {
|
||||||
const apps = await getAllApps(CouchDB, { dev: true })
|
const apps = await getAllApps({ dev: true })
|
||||||
const name = ctx.request.body.name
|
const name = ctx.request.body.name
|
||||||
checkAppName(ctx, apps, name)
|
checkAppName(ctx, apps, name)
|
||||||
const url = await getAppUrl(ctx)
|
const url = await getAppUrl(ctx)
|
||||||
|
@ -233,7 +235,7 @@ exports.create = async ctx => {
|
||||||
const instance = await createInstance(instanceConfig)
|
const instance = await createInstance(instanceConfig)
|
||||||
const appId = instance._id
|
const appId = instance._id
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = context.getAppDB()
|
||||||
let _rev
|
let _rev
|
||||||
try {
|
try {
|
||||||
// if template there will be an existing doc
|
// if template there will be an existing doc
|
||||||
|
@ -277,7 +279,7 @@ exports.create = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.update = async ctx => {
|
exports.update = async ctx => {
|
||||||
const apps = await getAllApps(CouchDB, { dev: true })
|
const apps = await getAllApps({ dev: true })
|
||||||
// validation
|
// validation
|
||||||
const name = ctx.request.body.name
|
const name = ctx.request.body.name
|
||||||
checkAppName(ctx, apps, name, ctx.params.appId)
|
checkAppName(ctx, apps, name, ctx.params.appId)
|
||||||
|
@ -292,7 +294,7 @@ exports.update = async ctx => {
|
||||||
|
|
||||||
exports.updateClient = async ctx => {
|
exports.updateClient = async ctx => {
|
||||||
// Get current app version
|
// Get current app version
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||||
const currentVersion = application.version
|
const currentVersion = application.version
|
||||||
|
|
||||||
|
@ -314,7 +316,7 @@ exports.updateClient = async ctx => {
|
||||||
|
|
||||||
exports.revertClient = async ctx => {
|
exports.revertClient = async ctx => {
|
||||||
// Check app can be reverted
|
// Check app can be reverted
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||||
if (!application.revertableVersion) {
|
if (!application.revertableVersion) {
|
||||||
ctx.throw(400, "There is no version to revert to")
|
ctx.throw(400, "There is no version to revert to")
|
||||||
|
@ -336,7 +338,7 @@ exports.revertClient = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.delete = async ctx => {
|
exports.delete = async ctx => {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = context.getAppDB()
|
||||||
|
|
||||||
const result = await db.destroy()
|
const result = await db.destroy()
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
|
@ -364,7 +366,8 @@ exports.sync = async (ctx, next) => {
|
||||||
const prodAppId = getDeployedAppID(appId)
|
const prodAppId = getDeployedAppID(appId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const prodDb = new CouchDB(prodAppId, { skip_setup: true })
|
// specific case, want to make sure setup is skipped
|
||||||
|
const prodDb = context.getProdAppDB({ skip_setup: true })
|
||||||
const info = await prodDb.info()
|
const info = await prodDb.info()
|
||||||
if (info.error) throw info.error
|
if (info.error) throw info.error
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -392,7 +395,7 @@ exports.sync = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync the users
|
// sync the users
|
||||||
await syncGlobalUsers(appId)
|
await syncGlobalUsers()
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
ctx.throw(400, error)
|
ctx.throw(400, error)
|
||||||
|
@ -404,7 +407,7 @@ exports.sync = async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateAppPackage = async (appPackage, appId) => {
|
const updateAppPackage = async (appPackage, appId) => {
|
||||||
const db = new CouchDB(appId)
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentTypes.APP_METADATA)
|
const application = await db.get(DocumentTypes.APP_METADATA)
|
||||||
|
|
||||||
const newAppPackage = { ...application, ...appPackage }
|
const newAppPackage = { ...application, ...appPackage }
|
||||||
|
@ -423,7 +426,7 @@ const updateAppPackage = async (appPackage, appId) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const createEmptyAppPackage = async (ctx, app) => {
|
const createEmptyAppPackage = async (ctx, app) => {
|
||||||
const db = new CouchDB(app.appId)
|
const db = context.getAppDB()
|
||||||
|
|
||||||
let screensAndLayouts = []
|
let screensAndLayouts = []
|
||||||
for (let layout of BASE_LAYOUTS) {
|
for (let layout of BASE_LAYOUTS) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const { getAllApps } = require("@budibase/backend-core/db")
|
const { getAllApps } = require("@budibase/backend-core/db")
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const {
|
const {
|
||||||
exportDB,
|
exportDB,
|
||||||
sendTempFile,
|
sendTempFile,
|
||||||
|
@ -30,7 +29,7 @@ exports.exportApps = async ctx => {
|
||||||
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
|
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
|
||||||
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
|
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
|
||||||
}
|
}
|
||||||
const apps = await getAllApps(CouchDB, { all: true })
|
const apps = await getAllApps({ all: true })
|
||||||
const globalDBString = await exportDB(getGlobalDBName(), {
|
const globalDBString = await exportDB(getGlobalDBName(), {
|
||||||
filter: doc => !doc._id.startsWith(DocumentTypes.USER),
|
filter: doc => !doc._id.startsWith(DocumentTypes.USER),
|
||||||
})
|
})
|
||||||
|
@ -63,7 +62,7 @@ async function hasBeenImported() {
|
||||||
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
if (!env.SELF_HOSTED || env.MULTI_TENANCY) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const apps = await getAllApps(CouchDB, { all: true })
|
const apps = await getAllApps({ all: true })
|
||||||
return apps.length !== 0
|
return apps.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const { DocumentTypes } = require("../../db/utils")
|
const { DocumentTypes } = require("../../db/utils")
|
||||||
const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
|
const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.fetchAppComponentDefinitions = async function (ctx) {
|
exports.fetchAppComponentDefinitions = async function (ctx) {
|
||||||
const appId = ctx.params.appId || ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const app = await db.get(DocumentTypes.APP_METADATA)
|
const app = await db.get(DocumentTypes.APP_METADATA)
|
||||||
|
|
||||||
let componentManifests = await Promise.all(
|
let componentManifests = await Promise.all(
|
||||||
app.componentLibraries.map(async library => {
|
app.componentLibraries.map(async library => {
|
||||||
let manifest = await getComponentLibraryManifest(appId, library)
|
let manifest = await getComponentLibraryManifest(library)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
manifest,
|
manifest,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const {
|
const {
|
||||||
generateDatasourceID,
|
generateDatasourceID,
|
||||||
getDatasourceParams,
|
getDatasourceParams,
|
||||||
|
@ -11,12 +10,11 @@ const { BuildSchemaErrors, InvalidColumns } = require("../../constants")
|
||||||
const { integrations } = require("../../integrations")
|
const { integrations } = require("../../integrations")
|
||||||
const { getDatasourceAndQuery } = require("./row/utils")
|
const { getDatasourceAndQuery } = require("./row/utils")
|
||||||
const { invalidateDynamicVariables } = require("../../threads/utils")
|
const { invalidateDynamicVariables } = require("../../threads/utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
exports.fetch = async function (ctx) {
|
||||||
const database = new CouchDB(ctx.appId)
|
|
||||||
|
|
||||||
// Get internal tables
|
// Get internal tables
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const internalTables = await db.allDocs(
|
const internalTables = await db.allDocs(
|
||||||
getTableParams(null, {
|
getTableParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -31,7 +29,7 @@ exports.fetch = async function (ctx) {
|
||||||
|
|
||||||
// Get external datasources
|
// Get external datasources
|
||||||
const datasources = (
|
const datasources = (
|
||||||
await database.allDocs(
|
await db.allDocs(
|
||||||
getDatasourceParams(null, {
|
getDatasourceParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
})
|
})
|
||||||
|
@ -49,7 +47,7 @@ exports.fetch = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.buildSchemaFromDb = async function (ctx) {
|
exports.buildSchemaFromDb = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const datasource = await db.get(ctx.params.datasourceId)
|
const datasource = await db.get(ctx.params.datasourceId)
|
||||||
|
|
||||||
const { tables, error } = await buildSchemaHelper(datasource)
|
const { tables, error } = await buildSchemaHelper(datasource)
|
||||||
|
@ -98,7 +96,7 @@ const invalidateVariables = async (existingDatasource, updatedDatasource) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.update = async function (ctx) {
|
exports.update = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const datasourceId = ctx.params.datasourceId
|
const datasourceId = ctx.params.datasourceId
|
||||||
let datasource = await db.get(datasourceId)
|
let datasource = await db.get(datasourceId)
|
||||||
const auth = datasource.config.auth
|
const auth = datasource.config.auth
|
||||||
|
@ -126,7 +124,7 @@ exports.update = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const plus = ctx.request.body.datasource.plus
|
const plus = ctx.request.body.datasource.plus
|
||||||
const fetchSchema = ctx.request.body.fetchSchema
|
const fetchSchema = ctx.request.body.fetchSchema
|
||||||
|
|
||||||
|
@ -162,7 +160,7 @@ exports.save = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
exports.destroy = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
|
|
||||||
// Delete all queries for the datasource
|
// Delete all queries for the datasource
|
||||||
const queries = await db.allDocs(
|
const queries = await db.allDocs(
|
||||||
|
@ -184,7 +182,7 @@ exports.destroy = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
exports.find = async function (ctx) {
|
||||||
const database = new CouchDB(ctx.appId)
|
const database = getAppDB()
|
||||||
ctx.body = await database.get(ctx.params.datasourceId)
|
ctx.body = await database.get(ctx.params.datasourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +190,7 @@ exports.find = async function (ctx) {
|
||||||
exports.query = async function (ctx) {
|
exports.query = async function (ctx) {
|
||||||
const queryJson = ctx.request.body
|
const queryJson = ctx.request.body
|
||||||
try {
|
try {
|
||||||
ctx.body = await getDatasourceAndQuery(ctx.appId, queryJson)
|
ctx.body = await getDatasourceAndQuery(queryJson)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.throw(400, err)
|
ctx.throw(400, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ const {
|
||||||
EMPTY_LAYOUT,
|
EMPTY_LAYOUT,
|
||||||
BASE_LAYOUT_PROP_IDS,
|
BASE_LAYOUT_PROP_IDS,
|
||||||
} = require("../../constants/layouts")
|
} = require("../../constants/layouts")
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const { generateLayoutID, getScreenParams } = require("../../db/utils")
|
const { generateLayoutID, getScreenParams } = require("../../db/utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
let layout = ctx.request.body
|
let layout = ctx.request.body
|
||||||
|
|
||||||
if (!layout.props) {
|
if (!layout.props) {
|
||||||
|
@ -26,7 +26,7 @@ exports.save = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
exports.destroy = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const layoutId = ctx.params.layoutId,
|
const layoutId = ctx.params.layoutId,
|
||||||
layoutRev = ctx.params.layoutRev
|
layoutRev = ctx.params.layoutRev
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ const {
|
||||||
getBuiltinRoles,
|
getBuiltinRoles,
|
||||||
} = require("@budibase/backend-core/roles")
|
} = require("@budibase/backend-core/roles")
|
||||||
const { getRoleParams } = require("../../db/utils")
|
const { getRoleParams } = require("../../db/utils")
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const {
|
const {
|
||||||
CURRENTLY_SUPPORTED_LEVELS,
|
CURRENTLY_SUPPORTED_LEVELS,
|
||||||
getBasePermissions,
|
getBasePermissions,
|
||||||
} = require("../../utilities/security")
|
} = require("../../utilities/security")
|
||||||
const { removeFromArray } = require("../../utilities")
|
const { removeFromArray } = require("../../utilities")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
const PermissionUpdateType = {
|
const PermissionUpdateType = {
|
||||||
REMOVE: "remove",
|
REMOVE: "remove",
|
||||||
|
@ -35,7 +35,7 @@ async function updatePermissionOnRole(
|
||||||
{ roleId, resourceId, level },
|
{ roleId, resourceId, level },
|
||||||
updateType
|
updateType
|
||||||
) {
|
) {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const remove = updateType === PermissionUpdateType.REMOVE
|
const remove = updateType === PermissionUpdateType.REMOVE
|
||||||
const isABuiltin = isBuiltin(roleId)
|
const isABuiltin = isBuiltin(roleId)
|
||||||
const dbRoleId = getDBRoleID(roleId)
|
const dbRoleId = getDBRoleID(roleId)
|
||||||
|
@ -106,7 +106,7 @@ exports.fetchLevels = function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
exports.fetch = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const roles = await getAllDBRoles(db)
|
const roles = await getAllDBRoles(db)
|
||||||
let permissions = {}
|
let permissions = {}
|
||||||
// create an object with structure role ID -> resource ID -> level
|
// create an object with structure role ID -> resource ID -> level
|
||||||
|
@ -133,7 +133,7 @@ exports.fetch = async function (ctx) {
|
||||||
|
|
||||||
exports.getResourcePerms = async function (ctx) {
|
exports.getResourcePerms = async function (ctx) {
|
||||||
const resourceId = ctx.params.resourceId
|
const resourceId = ctx.params.resourceId
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const body = await db.allDocs(
|
const body = await db.allDocs(
|
||||||
getRoleParams(null, {
|
getRoleParams(null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const {
|
const {
|
||||||
Role,
|
Role,
|
||||||
getRole,
|
getRole,
|
||||||
|
@ -10,6 +9,7 @@ const {
|
||||||
getUserMetadataParams,
|
getUserMetadataParams,
|
||||||
InternalTables,
|
InternalTables,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
const UpdateRolesOptions = {
|
const UpdateRolesOptions = {
|
||||||
CREATED: "created",
|
CREATED: "created",
|
||||||
|
@ -40,15 +40,15 @@ async function updateRolesOnUserTable(db, roleId, updateOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
exports.fetch = async function (ctx) {
|
||||||
ctx.body = await getAllRoles(ctx.appId)
|
ctx.body = await getAllRoles()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
exports.find = async function (ctx) {
|
||||||
ctx.body = await getRole(ctx.appId, ctx.params.roleId)
|
ctx.body = await getRole(ctx.params.roleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
let { _id, name, inherits, permissionId } = ctx.request.body
|
let { _id, name, inherits, permissionId } = ctx.request.body
|
||||||
if (!_id) {
|
if (!_id) {
|
||||||
_id = generateRoleID()
|
_id = generateRoleID()
|
||||||
|
@ -69,7 +69,7 @@ exports.save = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
exports.destroy = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const roleId = ctx.params.roleId
|
const roleId = ctx.params.roleId
|
||||||
if (isBuiltin(roleId)) {
|
if (isBuiltin(roleId)) {
|
||||||
ctx.throw(400, "Cannot delete builtin role.")
|
ctx.throw(400, "Cannot delete builtin role.")
|
||||||
|
|
|
@ -63,7 +63,7 @@ exports.fetch = async ctx => {
|
||||||
exports.clientFetch = async ctx => {
|
exports.clientFetch = async ctx => {
|
||||||
const routing = await getRoutingStructure(ctx.appId)
|
const routing = await getRoutingStructure(ctx.appId)
|
||||||
let roleId = ctx.user.role._id
|
let roleId = ctx.user.role._id
|
||||||
const roleIds = await getUserRoleHierarchy(ctx.appId, roleId)
|
const roleIds = await getUserRoleHierarchy(roleId)
|
||||||
for (let topLevel of Object.values(routing.routes)) {
|
for (let topLevel of Object.values(routing.routes)) {
|
||||||
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
||||||
let found = false
|
let found = false
|
||||||
|
|
|
@ -19,6 +19,19 @@ import {
|
||||||
isRowId,
|
isRowId,
|
||||||
convertRowId,
|
convertRowId,
|
||||||
} from "../../../integrations/utils"
|
} from "../../../integrations/utils"
|
||||||
|
import { getDatasourceAndQuery } from "./utils"
|
||||||
|
import {
|
||||||
|
DataSourceOperation,
|
||||||
|
FieldTypes,
|
||||||
|
RelationshipTypes,
|
||||||
|
} from "../../../constants"
|
||||||
|
import { breakExternalTableId, isSQL } from "../../../integrations/utils"
|
||||||
|
import { processObjectSync } from "@budibase/string-templates"
|
||||||
|
// @ts-ignore
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import { processFormulas } from "../../../utilities/rowProcessor/utils"
|
||||||
|
// @ts-ignore
|
||||||
|
import { getAppDB } from "@budibase/backend-core/context"
|
||||||
|
|
||||||
interface ManyRelationship {
|
interface ManyRelationship {
|
||||||
tableId?: string
|
tableId?: string
|
||||||
|
@ -38,18 +51,6 @@ interface RunConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
module External {
|
module External {
|
||||||
const { getDatasourceAndQuery } = require("./utils")
|
|
||||||
const {
|
|
||||||
DataSourceOperation,
|
|
||||||
FieldTypes,
|
|
||||||
RelationshipTypes,
|
|
||||||
} = require("../../../constants")
|
|
||||||
const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
|
|
||||||
const { processObjectSync } = require("@budibase/string-templates")
|
|
||||||
const { cloneDeep } = require("lodash/fp")
|
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const { processFormulas } = require("../../../utilities/rowProcessor/utils")
|
|
||||||
|
|
||||||
function buildFilters(
|
function buildFilters(
|
||||||
id: string | undefined,
|
id: string | undefined,
|
||||||
filters: SearchFilters,
|
filters: SearchFilters,
|
||||||
|
@ -210,19 +211,16 @@ module External {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExternalRequest {
|
class ExternalRequest {
|
||||||
private readonly appId: string
|
|
||||||
private operation: Operation
|
private operation: Operation
|
||||||
private tableId: string
|
private tableId: string
|
||||||
private datasource: Datasource
|
private datasource: Datasource
|
||||||
private tables: { [key: string]: Table } = {}
|
private tables: { [key: string]: Table } = {}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
appId: string,
|
|
||||||
operation: Operation,
|
operation: Operation,
|
||||||
tableId: string,
|
tableId: string,
|
||||||
datasource: Datasource
|
datasource: Datasource
|
||||||
) {
|
) {
|
||||||
this.appId = appId
|
|
||||||
this.operation = operation
|
this.operation = operation
|
||||||
this.tableId = tableId
|
this.tableId = tableId
|
||||||
this.datasource = datasource
|
this.datasource = datasource
|
||||||
|
@ -231,12 +229,14 @@ module External {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getTable(tableId: string | undefined): Table {
|
getTable(tableId: string | undefined): Table | undefined {
|
||||||
if (!tableId) {
|
if (!tableId) {
|
||||||
throw "Table ID is unknown, cannot find table"
|
throw "Table ID is unknown, cannot find table"
|
||||||
}
|
}
|
||||||
const { tableName } = breakExternalTableId(tableId)
|
const { tableName } = breakExternalTableId(tableId)
|
||||||
return this.tables[tableName]
|
if (tableName) {
|
||||||
|
return this.tables[tableName]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputProcessing(row: Row | undefined, table: Table) {
|
inputProcessing(row: Row | undefined, table: Table) {
|
||||||
|
@ -272,9 +272,9 @@ module External {
|
||||||
newRow[key] = row[key]
|
newRow[key] = row[key]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field?.tableId)
|
||||||
// table has to exist for many to many
|
// table has to exist for many to many
|
||||||
if (!this.tables[linkTableName]) {
|
if (!linkTableName || !this.tables[linkTableName]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const linkTable = this.tables[linkTableName]
|
const linkTable = this.tables[linkTableName]
|
||||||
|
@ -422,7 +422,7 @@ module External {
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
||||||
// no table to link to, this is not a valid relationships
|
// no table to link to, this is not a valid relationships
|
||||||
if (!this.tables[linkTableName]) {
|
if (!linkTableName || !this.tables[linkTableName]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const linkTable = this.tables[linkTableName]
|
const linkTable = this.tables[linkTableName]
|
||||||
|
@ -460,6 +460,9 @@ module External {
|
||||||
async lookupRelations(tableId: string, row: Row) {
|
async lookupRelations(tableId: string, row: Row) {
|
||||||
const related: { [key: string]: any } = {}
|
const related: { [key: string]: any } = {}
|
||||||
const { tableName } = breakExternalTableId(tableId)
|
const { tableName } = breakExternalTableId(tableId)
|
||||||
|
if (!tableName) {
|
||||||
|
return related
|
||||||
|
}
|
||||||
const table = this.tables[tableName]
|
const table = this.tables[tableName]
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const primaryKey = table.primary[0]
|
const primaryKey = table.primary[0]
|
||||||
|
@ -484,7 +487,7 @@ module External {
|
||||||
if (!lookupField || !row[lookupField]) {
|
if (!lookupField || !row[lookupField]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const response = await getDatasourceAndQuery(this.appId, {
|
const response = await getDatasourceAndQuery({
|
||||||
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
|
endpoint: getEndpoint(tableId, DataSourceOperation.READ),
|
||||||
filters: {
|
filters: {
|
||||||
equal: {
|
equal: {
|
||||||
|
@ -515,28 +518,30 @@ module External {
|
||||||
row: Row,
|
row: Row,
|
||||||
relationships: ManyRelationship[]
|
relationships: ManyRelationship[]
|
||||||
) {
|
) {
|
||||||
const { appId } = this
|
|
||||||
// if we're creating (in a through table) need to wipe the existing ones first
|
// if we're creating (in a through table) need to wipe the existing ones first
|
||||||
const promises = []
|
const promises = []
|
||||||
const related = await this.lookupRelations(mainTableId, row)
|
const related = await this.lookupRelations(mainTableId, row)
|
||||||
for (let relationship of relationships) {
|
for (let relationship of relationships) {
|
||||||
const { key, tableId, isUpdate, id, ...rest } = relationship
|
const { key, tableId, isUpdate, id, ...rest } = relationship
|
||||||
const body = processObjectSync(rest, row)
|
const body: { [key: string]: any } = processObjectSync(rest, row, {})
|
||||||
const linkTable = this.getTable(tableId)
|
const linkTable = this.getTable(tableId)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const linkPrimary = linkTable.primary[0]
|
const linkPrimary = linkTable?.primary[0]
|
||||||
|
if (!linkTable || !linkPrimary) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const rows = related[key].rows || []
|
const rows = related[key].rows || []
|
||||||
const found = rows.find(
|
const found = rows.find(
|
||||||
(row: { [key: string]: any }) =>
|
(row: { [key: string]: any }) =>
|
||||||
row[linkPrimary] === relationship.id ||
|
row[linkPrimary] === relationship.id ||
|
||||||
row[linkPrimary] === body[linkPrimary]
|
row[linkPrimary] === body?.[linkPrimary]
|
||||||
)
|
)
|
||||||
const operation = isUpdate
|
const operation = isUpdate
|
||||||
? DataSourceOperation.UPDATE
|
? DataSourceOperation.UPDATE
|
||||||
: DataSourceOperation.CREATE
|
: DataSourceOperation.CREATE
|
||||||
if (!found) {
|
if (!found) {
|
||||||
promises.push(
|
promises.push(
|
||||||
getDatasourceAndQuery(appId, {
|
getDatasourceAndQuery({
|
||||||
endpoint: getEndpoint(tableId, operation),
|
endpoint: getEndpoint(tableId, operation),
|
||||||
// if we're doing many relationships then we're writing, only one response
|
// if we're doing many relationships then we're writing, only one response
|
||||||
body,
|
body,
|
||||||
|
@ -552,9 +557,9 @@ module External {
|
||||||
for (let [colName, { isMany, rows, tableId }] of Object.entries(
|
for (let [colName, { isMany, rows, tableId }] of Object.entries(
|
||||||
related
|
related
|
||||||
)) {
|
)) {
|
||||||
const table: Table = this.getTable(tableId)
|
const table: Table | undefined = this.getTable(tableId)
|
||||||
// if its not the foreign key skip it, nothing to do
|
// if its not the foreign key skip it, nothing to do
|
||||||
if (table.primary && table.primary.indexOf(colName) !== -1) {
|
if (!table || (table.primary && table.primary.indexOf(colName) !== -1)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
|
@ -566,7 +571,7 @@ module External {
|
||||||
: DataSourceOperation.UPDATE
|
: DataSourceOperation.UPDATE
|
||||||
const body = isMany ? null : { [colName]: null }
|
const body = isMany ? null : { [colName]: null }
|
||||||
promises.push(
|
promises.push(
|
||||||
getDatasourceAndQuery(this.appId, {
|
getDatasourceAndQuery({
|
||||||
endpoint: getEndpoint(tableId, op),
|
endpoint: getEndpoint(tableId, op),
|
||||||
body,
|
body,
|
||||||
filters,
|
filters,
|
||||||
|
@ -605,20 +610,25 @@ module External {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
const { tableName: linkTableName } = breakExternalTableId(field.tableId)
|
||||||
const linkTable = this.tables[linkTableName]
|
if (linkTableName) {
|
||||||
if (linkTable) {
|
const linkTable = this.tables[linkTableName]
|
||||||
const linkedFields = extractRealFields(linkTable, fields)
|
if (linkTable) {
|
||||||
fields = fields.concat(linkedFields)
|
const linkedFields = extractRealFields(linkTable, fields)
|
||||||
|
fields = fields.concat(linkedFields)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(config: RunConfig) {
|
async run(config: RunConfig) {
|
||||||
const { appId, operation, tableId } = this
|
const { operation, tableId } = this
|
||||||
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
||||||
|
if (!tableName) {
|
||||||
|
throw "Unable to run without a table name"
|
||||||
|
}
|
||||||
if (!this.datasource) {
|
if (!this.datasource) {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
this.datasource = await db.get(datasourceId)
|
this.datasource = await db.get(datasourceId)
|
||||||
if (!this.datasource || !this.datasource.entities) {
|
if (!this.datasource || !this.datasource.entities) {
|
||||||
throw "No tables found, fetch tables before query."
|
throw "No tables found, fetch tables before query."
|
||||||
|
@ -670,7 +680,7 @@ module External {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// can't really use response right now
|
// can't really use response right now
|
||||||
const response = await getDatasourceAndQuery(appId, json)
|
const response = await getDatasourceAndQuery(json)
|
||||||
// handle many to many relationships now if we know the ID (could be auto increment)
|
// handle many to many relationships now if we know the ID (could be auto increment)
|
||||||
if (
|
if (
|
||||||
operation !== DataSourceOperation.READ &&
|
operation !== DataSourceOperation.READ &&
|
||||||
|
|
|
@ -11,7 +11,7 @@ const {
|
||||||
const ExternalRequest = require("./ExternalRequest")
|
const ExternalRequest = require("./ExternalRequest")
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
|
|
||||||
async function handleRequest(appId, operation, tableId, opts = {}) {
|
async function handleRequest(operation, tableId, opts = {}) {
|
||||||
// make sure the filters are cleaned up, no empty strings for equals, fuzzy or string
|
// make sure the filters are cleaned up, no empty strings for equals, fuzzy or string
|
||||||
if (opts && opts.filters) {
|
if (opts && opts.filters) {
|
||||||
for (let filterField of NoEmptyFilterStrings) {
|
for (let filterField of NoEmptyFilterStrings) {
|
||||||
|
@ -25,9 +25,7 @@ async function handleRequest(appId, operation, tableId, opts = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ExternalRequest(appId, operation, tableId, opts.datasource).run(
|
return new ExternalRequest(operation, tableId, opts.datasource).run(opts)
|
||||||
opts
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.handleRequest = handleRequest
|
exports.handleRequest = handleRequest
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const linkRows = require("../../../db/linkedRows")
|
const linkRows = require("../../../db/linkedRows")
|
||||||
const {
|
const {
|
||||||
getRowParams,
|
getRowParams,
|
||||||
|
@ -27,6 +26,7 @@ const {
|
||||||
getFromMemoryDoc,
|
getFromMemoryDoc,
|
||||||
} = require("../view/utils")
|
} = require("../view/utils")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
const CALCULATION_TYPES = {
|
const CALCULATION_TYPES = {
|
||||||
SUM: "sum",
|
SUM: "sum",
|
||||||
|
@ -106,8 +106,7 @@ async function getView(db, viewName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.patch = async ctx => {
|
exports.patch = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const inputs = ctx.request.body
|
const inputs = ctx.request.body
|
||||||
const tableId = inputs.tableId
|
const tableId = inputs.tableId
|
||||||
const isUserTable = tableId === InternalTables.USER_METADATA
|
const isUserTable = tableId === InternalTables.USER_METADATA
|
||||||
|
@ -146,14 +145,13 @@ exports.patch = async ctx => {
|
||||||
|
|
||||||
// returned row is cleaned and prepared for writing to DB
|
// returned row is cleaned and prepared for writing to DB
|
||||||
row = await linkRows.updateLinks({
|
row = await linkRows.updateLinks({
|
||||||
appId,
|
|
||||||
eventType: linkRows.EventType.ROW_UPDATE,
|
eventType: linkRows.EventType.ROW_UPDATE,
|
||||||
row,
|
row,
|
||||||
tableId: row.tableId,
|
tableId: row.tableId,
|
||||||
table,
|
table,
|
||||||
})
|
})
|
||||||
// check if any attachments removed
|
// check if any attachments removed
|
||||||
await cleanupAttachments(appId, table, { oldRow, row })
|
await cleanupAttachments(table, { oldRow, row })
|
||||||
|
|
||||||
if (isUserTable) {
|
if (isUserTable) {
|
||||||
// the row has been updated, need to put it into the ctx
|
// the row has been updated, need to put it into the ctx
|
||||||
|
@ -166,8 +164,7 @@ exports.patch = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
let inputs = ctx.request.body
|
let inputs = ctx.request.body
|
||||||
inputs.tableId = ctx.params.tableId
|
inputs.tableId = ctx.params.tableId
|
||||||
|
|
||||||
|
@ -189,7 +186,6 @@ exports.save = async function (ctx) {
|
||||||
|
|
||||||
// make sure link rows are up to date
|
// make sure link rows are up to date
|
||||||
row = await linkRows.updateLinks({
|
row = await linkRows.updateLinks({
|
||||||
appId,
|
|
||||||
eventType: linkRows.EventType.ROW_SAVE,
|
eventType: linkRows.EventType.ROW_SAVE,
|
||||||
row,
|
row,
|
||||||
tableId: row.tableId,
|
tableId: row.tableId,
|
||||||
|
@ -200,7 +196,6 @@ exports.save = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchView = async ctx => {
|
exports.fetchView = async ctx => {
|
||||||
const appId = ctx.appId
|
|
||||||
const viewName = ctx.params.viewName
|
const viewName = ctx.params.viewName
|
||||||
|
|
||||||
// if this is a table view being looked for just transfer to that
|
// if this is a table view being looked for just transfer to that
|
||||||
|
@ -209,7 +204,7 @@ exports.fetchView = async ctx => {
|
||||||
return exports.fetch(ctx)
|
return exports.fetch(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const { calculation, group, field } = ctx.query
|
const { calculation, group, field } = ctx.query
|
||||||
const viewInfo = await getView(db, viewName)
|
const viewInfo = await getView(db, viewName)
|
||||||
let response
|
let response
|
||||||
|
@ -263,8 +258,7 @@ exports.fetchView = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
|
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
let table = await db.get(tableId)
|
let table = await db.get(tableId)
|
||||||
|
@ -273,17 +267,15 @@ exports.fetch = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async ctx => {
|
exports.find = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const table = await db.get(ctx.params.tableId)
|
const table = await db.get(ctx.params.tableId)
|
||||||
let row = await findRow(ctx, db, ctx.params.tableId, ctx.params.rowId)
|
let row = await findRow(ctx, ctx.params.tableId, ctx.params.rowId)
|
||||||
row = await outputProcessing(ctx, table, row)
|
row = await outputProcessing(ctx, table, row)
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
exports.destroy = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const { _id, _rev } = ctx.request.body
|
const { _id, _rev } = ctx.request.body
|
||||||
let row = await db.get(_id)
|
let row = await db.get(_id)
|
||||||
|
|
||||||
|
@ -295,13 +287,12 @@ exports.destroy = async function (ctx) {
|
||||||
row = await outputProcessing(ctx, table, row, { squash: false })
|
row = await outputProcessing(ctx, table, row, { squash: false })
|
||||||
// now remove the relationships
|
// now remove the relationships
|
||||||
await linkRows.updateLinks({
|
await linkRows.updateLinks({
|
||||||
appId,
|
|
||||||
eventType: linkRows.EventType.ROW_DELETE,
|
eventType: linkRows.EventType.ROW_DELETE,
|
||||||
row,
|
row,
|
||||||
tableId: row.tableId,
|
tableId: row.tableId,
|
||||||
})
|
})
|
||||||
// remove any attachments that were on the row from object storage
|
// remove any attachments that were on the row from object storage
|
||||||
await cleanupAttachments(appId, table, { row })
|
await cleanupAttachments(table, { row })
|
||||||
|
|
||||||
let response
|
let response
|
||||||
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
if (ctx.params.tableId === InternalTables.USER_METADATA) {
|
||||||
|
@ -317,8 +308,7 @@ exports.destroy = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.bulkDestroy = async ctx => {
|
exports.bulkDestroy = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const table = await db.get(tableId)
|
const table = await db.get(tableId)
|
||||||
let { rows } = ctx.request.body
|
let { rows } = ctx.request.body
|
||||||
|
@ -330,7 +320,6 @@ exports.bulkDestroy = async ctx => {
|
||||||
// remove the relationships first
|
// remove the relationships first
|
||||||
let updates = rows.map(row =>
|
let updates = rows.map(row =>
|
||||||
linkRows.updateLinks({
|
linkRows.updateLinks({
|
||||||
appId,
|
|
||||||
eventType: linkRows.EventType.ROW_DELETE,
|
eventType: linkRows.EventType.ROW_DELETE,
|
||||||
row,
|
row,
|
||||||
tableId: row.tableId,
|
tableId: row.tableId,
|
||||||
|
@ -349,7 +338,7 @@ exports.bulkDestroy = async ctx => {
|
||||||
await db.bulkDocs(rows.map(row => ({ ...row, _deleted: true })))
|
await db.bulkDocs(rows.map(row => ({ ...row, _deleted: true })))
|
||||||
}
|
}
|
||||||
// remove any attachments that were on the rows from object storage
|
// remove any attachments that were on the rows from object storage
|
||||||
await cleanupAttachments(appId, table, { rows })
|
await cleanupAttachments(table, { rows })
|
||||||
await Promise.all(updates)
|
await Promise.all(updates)
|
||||||
return { response: { ok: true }, rows }
|
return { response: { ok: true }, rows }
|
||||||
}
|
}
|
||||||
|
@ -360,25 +349,24 @@ exports.search = async ctx => {
|
||||||
return { rows: await exports.fetch(ctx) }
|
return { rows: await exports.fetch(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
const appId = ctx.appId
|
|
||||||
const { tableId } = ctx.params
|
const { tableId } = ctx.params
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const { paginate, query, ...params } = ctx.request.body
|
const { paginate, query, ...params } = ctx.request.body
|
||||||
params.version = ctx.version
|
params.version = ctx.version
|
||||||
params.tableId = tableId
|
params.tableId = tableId
|
||||||
|
|
||||||
let response
|
let response
|
||||||
if (paginate) {
|
if (paginate) {
|
||||||
response = await paginatedSearch(appId, query, params)
|
response = await paginatedSearch(query, params)
|
||||||
} else {
|
} else {
|
||||||
response = await fullSearch(appId, query, params)
|
response = await fullSearch(query, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enrich search results with relationships
|
// Enrich search results with relationships
|
||||||
if (response.rows && response.rows.length) {
|
if (response.rows && response.rows.length) {
|
||||||
// enrich with global users if from users table
|
// enrich with global users if from users table
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (tableId === InternalTables.USER_METADATA) {
|
||||||
response.rows = await getGlobalUsersFromMetadata(appId, response.rows)
|
response.rows = await getGlobalUsersFromMetadata(response.rows)
|
||||||
}
|
}
|
||||||
const table = await db.get(tableId)
|
const table = await db.get(tableId)
|
||||||
response.rows = await outputProcessing(ctx, table, response.rows)
|
response.rows = await outputProcessing(ctx, table, response.rows)
|
||||||
|
@ -389,25 +377,22 @@ exports.search = async ctx => {
|
||||||
|
|
||||||
exports.validate = async ctx => {
|
exports.validate = async ctx => {
|
||||||
return validate({
|
return validate({
|
||||||
appId: ctx.appId,
|
|
||||||
tableId: ctx.params.tableId,
|
tableId: ctx.params.tableId,
|
||||||
row: ctx.request.body,
|
row: ctx.request.body,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchEnrichedRow = async ctx => {
|
exports.fetchEnrichedRow = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const rowId = ctx.params.rowId
|
const rowId = ctx.params.rowId
|
||||||
// need table to work out where links go in row
|
// need table to work out where links go in row
|
||||||
let [table, row] = await Promise.all([
|
let [table, row] = await Promise.all([
|
||||||
db.get(tableId),
|
db.get(tableId),
|
||||||
findRow(ctx, db, tableId, rowId),
|
findRow(ctx, tableId, rowId),
|
||||||
])
|
])
|
||||||
// get the link docs
|
// get the link docs
|
||||||
const linkVals = await linkRows.getLinkDocuments({
|
const linkVals = await linkRows.getLinkDocuments({
|
||||||
appId,
|
|
||||||
tableId,
|
tableId,
|
||||||
rowId,
|
rowId,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
const { SearchIndexes } = require("../../../db/utils")
|
const { SearchIndexes } = require("../../../db/utils")
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const { getCouchUrl } = require("@budibase/backend-core/db")
|
const { getCouchUrl } = require("@budibase/backend-core/db")
|
||||||
|
const { getAppId } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to build lucene query URLs.
|
* Class to build lucene query URLs.
|
||||||
* Optionally takes a base lucene query object.
|
* Optionally takes a base lucene query object.
|
||||||
*/
|
*/
|
||||||
class QueryBuilder {
|
class QueryBuilder {
|
||||||
constructor(appId, base) {
|
constructor(base) {
|
||||||
this.appId = appId
|
|
||||||
this.query = {
|
this.query = {
|
||||||
string: {},
|
string: {},
|
||||||
fuzzy: {},
|
fuzzy: {},
|
||||||
|
@ -233,7 +233,8 @@ class QueryBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
const url = `${getCouchUrl()}/${this.appId}/_design/database/_search/${
|
const appId = getAppId()
|
||||||
|
const url = `${getCouchUrl()}/${appId}/_design/database/_search/${
|
||||||
SearchIndexes.ROWS
|
SearchIndexes.ROWS
|
||||||
}`
|
}`
|
||||||
const body = this.buildSearchBody()
|
const body = this.buildSearchBody()
|
||||||
|
@ -270,7 +271,6 @@ const runQuery = async (url, body) => {
|
||||||
* Gets round the fixed limit of 200 results from a query by fetching as many
|
* Gets round the fixed limit of 200 results from a query by fetching as many
|
||||||
* pages as required and concatenating the results. This recursively operates
|
* pages as required and concatenating the results. This recursively operates
|
||||||
* until enough results have been found.
|
* until enough results have been found.
|
||||||
* @param appId {string} The app ID to search
|
|
||||||
* @param query {object} The JSON query structure
|
* @param query {object} The JSON query structure
|
||||||
* @param params {object} The search params including:
|
* @param params {object} The search params including:
|
||||||
* tableId {string} The table ID to search
|
* tableId {string} The table ID to search
|
||||||
|
@ -283,7 +283,7 @@ const runQuery = async (url, body) => {
|
||||||
* rows {array|null} Current results in the recursive search
|
* rows {array|null} Current results in the recursive search
|
||||||
* @returns {Promise<*[]|*>}
|
* @returns {Promise<*[]|*>}
|
||||||
*/
|
*/
|
||||||
const recursiveSearch = async (appId, query, params) => {
|
const recursiveSearch = async (query, params) => {
|
||||||
const bookmark = params.bookmark
|
const bookmark = params.bookmark
|
||||||
const rows = params.rows || []
|
const rows = params.rows || []
|
||||||
if (rows.length >= params.limit) {
|
if (rows.length >= params.limit) {
|
||||||
|
@ -293,7 +293,7 @@ const recursiveSearch = async (appId, query, params) => {
|
||||||
if (rows.length > params.limit - 200) {
|
if (rows.length > params.limit - 200) {
|
||||||
pageSize = params.limit - rows.length
|
pageSize = params.limit - rows.length
|
||||||
}
|
}
|
||||||
const page = await new QueryBuilder(appId, query)
|
const page = await new QueryBuilder(query)
|
||||||
.setVersion(params.version)
|
.setVersion(params.version)
|
||||||
.setTable(params.tableId)
|
.setTable(params.tableId)
|
||||||
.setBookmark(bookmark)
|
.setBookmark(bookmark)
|
||||||
|
@ -313,14 +313,13 @@ const recursiveSearch = async (appId, query, params) => {
|
||||||
bookmark: page.bookmark,
|
bookmark: page.bookmark,
|
||||||
rows: [...rows, ...page.rows],
|
rows: [...rows, ...page.rows],
|
||||||
}
|
}
|
||||||
return await recursiveSearch(appId, query, newParams)
|
return await recursiveSearch(query, newParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a paginated search. A bookmark will be returned to allow the next
|
* Performs a paginated search. A bookmark will be returned to allow the next
|
||||||
* page to be fetched. There is a max limit off 200 results per page in a
|
* page to be fetched. There is a max limit off 200 results per page in a
|
||||||
* paginated search.
|
* paginated search.
|
||||||
* @param appId {string} The app ID to search
|
|
||||||
* @param query {object} The JSON query structure
|
* @param query {object} The JSON query structure
|
||||||
* @param params {object} The search params including:
|
* @param params {object} The search params including:
|
||||||
* tableId {string} The table ID to search
|
* tableId {string} The table ID to search
|
||||||
|
@ -332,13 +331,13 @@ const recursiveSearch = async (appId, query, params) => {
|
||||||
* bookmark {string} The bookmark to resume from
|
* bookmark {string} The bookmark to resume from
|
||||||
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
|
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
|
||||||
*/
|
*/
|
||||||
exports.paginatedSearch = async (appId, query, params) => {
|
exports.paginatedSearch = async (query, params) => {
|
||||||
let limit = params.limit
|
let limit = params.limit
|
||||||
if (limit == null || isNaN(limit) || limit < 0) {
|
if (limit == null || isNaN(limit) || limit < 0) {
|
||||||
limit = 50
|
limit = 50
|
||||||
}
|
}
|
||||||
limit = Math.min(limit, 200)
|
limit = Math.min(limit, 200)
|
||||||
const search = new QueryBuilder(appId, query)
|
const search = new QueryBuilder(query)
|
||||||
.setVersion(params.version)
|
.setVersion(params.version)
|
||||||
.setTable(params.tableId)
|
.setTable(params.tableId)
|
||||||
.setSort(params.sort)
|
.setSort(params.sort)
|
||||||
|
@ -367,7 +366,6 @@ exports.paginatedSearch = async (appId, query, params) => {
|
||||||
* desired amount of results. There is a limit of 1000 results to avoid
|
* desired amount of results. There is a limit of 1000 results to avoid
|
||||||
* heavy performance hits, and to avoid client components breaking from
|
* heavy performance hits, and to avoid client components breaking from
|
||||||
* handling too much data.
|
* handling too much data.
|
||||||
* @param appId {string} The app ID to search
|
|
||||||
* @param query {object} The JSON query structure
|
* @param query {object} The JSON query structure
|
||||||
* @param params {object} The search params including:
|
* @param params {object} The search params including:
|
||||||
* tableId {string} The table ID to search
|
* tableId {string} The table ID to search
|
||||||
|
@ -378,12 +376,12 @@ exports.paginatedSearch = async (appId, query, params) => {
|
||||||
* limit {number} The desired number of results
|
* limit {number} The desired number of results
|
||||||
* @returns {Promise<{rows: *}>}
|
* @returns {Promise<{rows: *}>}
|
||||||
*/
|
*/
|
||||||
exports.fullSearch = async (appId, query, params) => {
|
exports.fullSearch = async (query, params) => {
|
||||||
let limit = params.limit
|
let limit = params.limit
|
||||||
if (limit == null || isNaN(limit) || limit < 0) {
|
if (limit == null || isNaN(limit) || limit < 0) {
|
||||||
limit = 1000
|
limit = 1000
|
||||||
}
|
}
|
||||||
params.limit = Math.min(limit, 1000)
|
params.limit = Math.min(limit, 1000)
|
||||||
const rows = await recursiveSearch(appId, query, params)
|
const rows = await recursiveSearch(query, params)
|
||||||
return { rows }
|
return { rows }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
const validateJs = require("validate.js")
|
const validateJs = require("validate.js")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const { InternalTables } = require("../../../db/utils")
|
const { InternalTables } = require("../../../db/utils")
|
||||||
const userController = require("../user")
|
const userController = require("../user")
|
||||||
const { FieldTypes } = require("../../../constants")
|
const { FieldTypes } = require("../../../constants")
|
||||||
const { processStringSync } = require("@budibase/string-templates")
|
const { processStringSync } = require("@budibase/string-templates")
|
||||||
const { makeExternalQuery } = require("../../../integrations/base/utils")
|
const { makeExternalQuery } = require("../../../integrations/base/utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
validateJs.extend(validateJs.validators.datetime, {
|
validateJs.extend(validateJs.validators.datetime, {
|
||||||
parse: function (value) {
|
parse: function (value) {
|
||||||
|
@ -17,14 +17,15 @@ validateJs.extend(validateJs.validators.datetime, {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
exports.getDatasourceAndQuery = async (appId, json) => {
|
exports.getDatasourceAndQuery = async json => {
|
||||||
const datasourceId = json.endpoint.datasourceId
|
const datasourceId = json.endpoint.datasourceId
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
return makeExternalQuery(datasource, json)
|
return makeExternalQuery(datasource, json)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.findRow = async (ctx, db, tableId, rowId) => {
|
exports.findRow = async (ctx, tableId, rowId) => {
|
||||||
|
const db = getAppDB()
|
||||||
let row
|
let row
|
||||||
// TODO remove special user case in future
|
// TODO remove special user case in future
|
||||||
if (tableId === InternalTables.USER_METADATA) {
|
if (tableId === InternalTables.USER_METADATA) {
|
||||||
|
@ -42,9 +43,9 @@ exports.findRow = async (ctx, db, tableId, rowId) => {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.validate = async ({ appId, tableId, row, table }) => {
|
exports.validate = async ({ tableId, row, table }) => {
|
||||||
if (!table) {
|
if (!table) {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
table = await db.get(tableId)
|
table = await db.get(tableId)
|
||||||
}
|
}
|
||||||
const errors = {}
|
const errors = {}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
||||||
const { AccessController } = require("@budibase/backend-core/roles")
|
const { AccessController } = require("@budibase/backend-core/roles")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
|
|
||||||
const screens = (
|
const screens = (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
|
@ -14,15 +13,14 @@ exports.fetch = async ctx => {
|
||||||
)
|
)
|
||||||
).rows.map(element => element.doc)
|
).rows.map(element => element.doc)
|
||||||
|
|
||||||
ctx.body = await new AccessController(appId).checkScreensAccess(
|
ctx.body = await new AccessController().checkScreensAccess(
|
||||||
screens,
|
screens,
|
||||||
ctx.user.role._id
|
ctx.user.role._id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async ctx => {
|
exports.save = async ctx => {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
let screen = ctx.request.body
|
let screen = ctx.request.body
|
||||||
|
|
||||||
if (!screen._id) {
|
if (!screen._id) {
|
||||||
|
@ -39,7 +37,7 @@ exports.save = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async ctx => {
|
exports.destroy = async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
await db.remove(ctx.params.screenId, ctx.params.screenRev)
|
await db.remove(ctx.params.screenId, ctx.params.screenRev)
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
message: "Screen deleted successfully",
|
message: "Screen deleted successfully",
|
||||||
|
|
|
@ -6,7 +6,6 @@ const uuid = require("uuid")
|
||||||
const { ObjectStoreBuckets } = require("../../../constants")
|
const { ObjectStoreBuckets } = require("../../../constants")
|
||||||
const { processString } = require("@budibase/string-templates")
|
const { processString } = require("@budibase/string-templates")
|
||||||
const { getAllApps } = require("@budibase/backend-core/db")
|
const { getAllApps } = require("@budibase/backend-core/db")
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const {
|
const {
|
||||||
loadHandlebarsFile,
|
loadHandlebarsFile,
|
||||||
NODE_MODULES_PATH,
|
NODE_MODULES_PATH,
|
||||||
|
@ -17,6 +16,7 @@ const { clientLibraryPath } = require("../../../utilities")
|
||||||
const { upload } = require("../../../utilities/fileSystem")
|
const { upload } = require("../../../utilities/fileSystem")
|
||||||
const { attachmentsRelativeURL } = require("../../../utilities")
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
||||||
const { DocumentTypes } = require("../../../db/utils")
|
const { DocumentTypes } = require("../../../db/utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
const AWS = require("aws-sdk")
|
const AWS = require("aws-sdk")
|
||||||
const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1"
|
const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1"
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ async function getAppIdFromUrl(ctx) {
|
||||||
let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
|
let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
|
||||||
|
|
||||||
// search prod apps for a url that matches, exclude dev where id is always used
|
// search prod apps for a url that matches, exclude dev where id is always used
|
||||||
const apps = await getAllApps(CouchDB, { dev: false })
|
const apps = await getAllApps({ dev: false })
|
||||||
const app = apps.filter(
|
const app = apps.filter(
|
||||||
a => a.url && a.url.toLowerCase() === possibleAppUrl
|
a => a.url && a.url.toLowerCase() === possibleAppUrl
|
||||||
)[0]
|
)[0]
|
||||||
|
@ -85,7 +85,7 @@ exports.uploadFile = async function (ctx) {
|
||||||
exports.serveApp = async function (ctx) {
|
exports.serveApp = async function (ctx) {
|
||||||
let appId = await getAppIdFromUrl(ctx)
|
let appId = await getAppIdFromUrl(ctx)
|
||||||
const App = require("./templates/BudibaseApp.svelte").default
|
const App = require("./templates/BudibaseApp.svelte").default
|
||||||
const db = new CouchDB(appId, { skip_setup: true })
|
const db = getAppDB({ skip_setup: true })
|
||||||
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
const appInfo = await db.get(DocumentTypes.APP_METADATA)
|
||||||
|
|
||||||
const { head, html, css } = App.render({
|
const { head, html, css } = App.render({
|
||||||
|
@ -111,7 +111,7 @@ exports.serveClientLibrary = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getSignedUploadURL = async function (ctx) {
|
exports.getSignedUploadURL = async function (ctx) {
|
||||||
const database = new CouchDB(ctx.appId)
|
const database = getAppDB()
|
||||||
|
|
||||||
// Ensure datasource is valid
|
// Ensure datasource is valid
|
||||||
let datasource
|
let datasource
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const {
|
const {
|
||||||
buildExternalTableId,
|
buildExternalTableId,
|
||||||
breakExternalTableId,
|
breakExternalTableId,
|
||||||
|
@ -19,6 +18,7 @@ const { makeExternalQuery } = require("../../../integrations/base/utils")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
const csvParser = require("../../../utilities/csvParser")
|
const csvParser = require("../../../utilities/csvParser")
|
||||||
const { handleRequest } = require("../row/external")
|
const { handleRequest } = require("../row/external")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
async function makeTableRequest(
|
async function makeTableRequest(
|
||||||
datasource,
|
datasource,
|
||||||
|
@ -159,7 +159,6 @@ function isRelationshipSetup(column) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
const appId = ctx.appId
|
|
||||||
const table = ctx.request.body
|
const table = ctx.request.body
|
||||||
// can't do this right now
|
// can't do this right now
|
||||||
delete table.dataImport
|
delete table.dataImport
|
||||||
|
@ -176,14 +175,14 @@ exports.save = async function (ctx) {
|
||||||
|
|
||||||
let oldTable
|
let oldTable
|
||||||
if (ctx.request.body && ctx.request.body._id) {
|
if (ctx.request.body && ctx.request.body._id) {
|
||||||
oldTable = await getTable(appId, ctx.request.body._id)
|
oldTable = await getTable(ctx.request.body._id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasTypeChanged(tableToSave, oldTable)) {
|
if (hasTypeChanged(tableToSave, oldTable)) {
|
||||||
ctx.throw(400, "A column type has changed.")
|
ctx.throw(400, "A column type has changed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
const oldTables = cloneDeep(datasource.entities)
|
const oldTables = cloneDeep(datasource.entities)
|
||||||
const tables = datasource.entities
|
const tables = datasource.entities
|
||||||
|
@ -267,14 +266,13 @@ exports.save = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async function (ctx) {
|
exports.destroy = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const tableToDelete = await getTable(ctx.params.tableId)
|
||||||
const tableToDelete = await getTable(appId, ctx.params.tableId)
|
|
||||||
if (!tableToDelete || !tableToDelete.created) {
|
if (!tableToDelete || !tableToDelete.created) {
|
||||||
ctx.throw(400, "Cannot delete tables which weren't created in Budibase.")
|
ctx.throw(400, "Cannot delete tables which weren't created in Budibase.")
|
||||||
}
|
}
|
||||||
const datasourceId = getDatasourceId(tableToDelete)
|
const datasourceId = getDatasourceId(tableToDelete)
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
const tables = datasource.entities
|
const tables = datasource.entities
|
||||||
|
|
||||||
|
@ -290,8 +288,7 @@ exports.destroy = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.bulkImport = async function (ctx) {
|
exports.bulkImport = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const table = await getTable(ctx.params.tableId)
|
||||||
const table = await getTable(appId, ctx.params.tableId)
|
|
||||||
const { dataImport } = ctx.request.body
|
const { dataImport } = ctx.request.body
|
||||||
if (!dataImport || !dataImport.schema || !dataImport.csvString) {
|
if (!dataImport || !dataImport.schema || !dataImport.csvString) {
|
||||||
ctx.throw(400, "Provided data import information is invalid.")
|
ctx.throw(400, "Provided data import information is invalid.")
|
||||||
|
@ -300,7 +297,7 @@ exports.bulkImport = async function (ctx) {
|
||||||
...dataImport,
|
...dataImport,
|
||||||
existingTable: table,
|
existingTable: table,
|
||||||
})
|
})
|
||||||
await handleRequest(appId, DataSourceOperation.BULK_CREATE, table._id, {
|
await handleRequest(DataSourceOperation.BULK_CREATE, table._id, {
|
||||||
rows,
|
rows,
|
||||||
})
|
})
|
||||||
return table
|
return table
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const internal = require("./internal")
|
const internal = require("./internal")
|
||||||
const external = require("./external")
|
const external = require("./external")
|
||||||
const csvParser = require("../../../utilities/csvParser")
|
const csvParser = require("../../../utilities/csvParser")
|
||||||
|
@ -9,6 +8,7 @@ const {
|
||||||
BudibaseInternalDB,
|
BudibaseInternalDB,
|
||||||
} = require("../../../db/utils")
|
} = require("../../../db/utils")
|
||||||
const { getTable } = require("./utils")
|
const { getTable } = require("./utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
function pickApi({ tableId, table }) {
|
function pickApi({ tableId, table }) {
|
||||||
if (table && !tableId) {
|
if (table && !tableId) {
|
||||||
|
@ -24,7 +24,7 @@ function pickApi({ tableId, table }) {
|
||||||
|
|
||||||
// covers both internal and external
|
// covers both internal and external
|
||||||
exports.fetch = async function (ctx) {
|
exports.fetch = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
|
|
||||||
const internalTables = await db.allDocs(
|
const internalTables = await db.allDocs(
|
||||||
getTableParams(null, {
|
getTableParams(null, {
|
||||||
|
@ -63,7 +63,7 @@ exports.fetch = async function (ctx) {
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
exports.find = async function (ctx) {
|
||||||
const tableId = ctx.params.id
|
const tableId = ctx.params.id
|
||||||
ctx.body = await getTable(ctx.appId, tableId)
|
ctx.body = await getTable(tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
|
@ -102,7 +102,7 @@ exports.validateCSVSchema = async function (ctx) {
|
||||||
const { csvString, schema = {}, tableId } = ctx.request.body
|
const { csvString, schema = {}, tableId } = ctx.request.body
|
||||||
let existingTable
|
let existingTable
|
||||||
if (tableId) {
|
if (tableId) {
|
||||||
existingTable = await getTable(ctx.appId, tableId)
|
existingTable = await getTable(tableId)
|
||||||
}
|
}
|
||||||
let result = await csvParser.parse(csvString, schema)
|
let result = await csvParser.parse(csvString, schema)
|
||||||
if (existingTable) {
|
if (existingTable) {
|
||||||
|
|
|
@ -34,8 +34,7 @@ exports.save = async function (ctx) {
|
||||||
// saving a table is a complex operation, involving many different steps, this
|
// saving a table is a complex operation, involving many different steps, this
|
||||||
// has been broken out into a utility to make it more obvious/easier to manipulate
|
// has been broken out into a utility to make it more obvious/easier to manipulate
|
||||||
const tableSaveFunctions = new TableSaveFunctions({
|
const tableSaveFunctions = new TableSaveFunctions({
|
||||||
db,
|
user: ctx.user,
|
||||||
ctx,
|
|
||||||
oldTable,
|
oldTable,
|
||||||
dataImport,
|
dataImport,
|
||||||
})
|
})
|
||||||
|
@ -145,9 +144,8 @@ exports.destroy = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.bulkImport = async function (ctx) {
|
exports.bulkImport = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const table = await getTable(ctx.params.tableId)
|
||||||
const table = await getTable(appId, ctx.params.tableId)
|
|
||||||
const { dataImport } = ctx.request.body
|
const { dataImport } = ctx.request.body
|
||||||
await handleDataImport(appId, ctx.user, table, dataImport)
|
await handleDataImport(ctx.user, table, dataImport)
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const csvParser = require("../../../utilities/csvParser")
|
const csvParser = require("../../../utilities/csvParser")
|
||||||
const {
|
const {
|
||||||
getRowParams,
|
getRowParams,
|
||||||
|
@ -17,8 +16,10 @@ const {
|
||||||
const { getViews, saveView } = require("../view/utils")
|
const { getViews, saveView } = require("../view/utils")
|
||||||
const viewTemplate = require("../view/viewBuilder")
|
const viewTemplate = require("../view/viewBuilder")
|
||||||
const usageQuota = require("../../../utilities/usageQuota")
|
const usageQuota = require("../../../utilities/usageQuota")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
exports.checkForColumnUpdates = async (oldTable, updatedTable) => {
|
||||||
|
const db = getAppDB()
|
||||||
let updatedRows = []
|
let updatedRows = []
|
||||||
const rename = updatedTable._rename
|
const rename = updatedTable._rename
|
||||||
let deletedColumns = []
|
let deletedColumns = []
|
||||||
|
@ -46,7 +47,7 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Update views
|
// Update views
|
||||||
await exports.checkForViewUpdates(db, updatedTable, rename, deletedColumns)
|
await exports.checkForViewUpdates(updatedTable, rename, deletedColumns)
|
||||||
delete updatedTable._rename
|
delete updatedTable._rename
|
||||||
}
|
}
|
||||||
return { rows: updatedRows, table: updatedTable }
|
return { rows: updatedRows, table: updatedTable }
|
||||||
|
@ -73,12 +74,12 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
|
||||||
return tableToSave
|
return tableToSave
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.handleDataImport = async (appId, user, table, dataImport) => {
|
exports.handleDataImport = async (user, table, dataImport) => {
|
||||||
if (!dataImport || !dataImport.csvString) {
|
if (!dataImport || !dataImport.csvString) {
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
// Populate the table with rows imported from CSV in a bulk update
|
// Populate the table with rows imported from CSV in a bulk update
|
||||||
const data = await csvParser.transform({
|
const data = await csvParser.transform({
|
||||||
...dataImport,
|
...dataImport,
|
||||||
|
@ -123,8 +124,8 @@ exports.handleDataImport = async (appId, user, table, dataImport) => {
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.handleSearchIndexes = async (appId, table) => {
|
exports.handleSearchIndexes = async table => {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
// create relevant search indexes
|
// create relevant search indexes
|
||||||
if (table.indexes && table.indexes.length > 0) {
|
if (table.indexes && table.indexes.length > 0) {
|
||||||
const currentIndexes = await db.getIndexes()
|
const currentIndexes = await db.getIndexes()
|
||||||
|
@ -181,12 +182,9 @@ exports.checkStaticTables = table => {
|
||||||
}
|
}
|
||||||
|
|
||||||
class TableSaveFunctions {
|
class TableSaveFunctions {
|
||||||
constructor({ db, ctx, oldTable, dataImport }) {
|
constructor({ user, oldTable, dataImport }) {
|
||||||
this.db = db
|
this.db = getAppDB()
|
||||||
this.ctx = ctx
|
this.user = user
|
||||||
if (this.ctx && this.ctx.user) {
|
|
||||||
this.appId = this.ctx.appId
|
|
||||||
}
|
|
||||||
this.oldTable = oldTable
|
this.oldTable = oldTable
|
||||||
this.dataImport = dataImport
|
this.dataImport = dataImport
|
||||||
// any rows that need updated
|
// any rows that need updated
|
||||||
|
@ -204,24 +202,15 @@ class TableSaveFunctions {
|
||||||
|
|
||||||
// when confirmed valid
|
// when confirmed valid
|
||||||
async mid(table) {
|
async mid(table) {
|
||||||
let response = await exports.checkForColumnUpdates(
|
let response = await exports.checkForColumnUpdates(this.oldTable, table)
|
||||||
this.db,
|
|
||||||
this.oldTable,
|
|
||||||
table
|
|
||||||
)
|
|
||||||
this.rows = this.rows.concat(response.rows)
|
this.rows = this.rows.concat(response.rows)
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
// after saving
|
// after saving
|
||||||
async after(table) {
|
async after(table) {
|
||||||
table = await exports.handleSearchIndexes(this.appId, table)
|
table = await exports.handleSearchIndexes(table)
|
||||||
table = await exports.handleDataImport(
|
table = await exports.handleDataImport(this.user, table, this.dataImport)
|
||||||
this.appId,
|
|
||||||
this.ctx.user,
|
|
||||||
table,
|
|
||||||
this.dataImport
|
|
||||||
)
|
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +219,8 @@ class TableSaveFunctions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getAllExternalTables = async (appId, datasourceId) => {
|
exports.getAllExternalTables = async datasourceId => {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
if (!datasource || !datasource.entities) {
|
if (!datasource || !datasource.entities) {
|
||||||
throw "Datasource is not configured fully."
|
throw "Datasource is not configured fully."
|
||||||
|
@ -239,25 +228,25 @@ exports.getAllExternalTables = async (appId, datasourceId) => {
|
||||||
return datasource.entities
|
return datasource.entities
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getExternalTable = async (appId, datasourceId, tableName) => {
|
exports.getExternalTable = async (datasourceId, tableName) => {
|
||||||
const entities = await exports.getAllExternalTables(appId, datasourceId)
|
const entities = await exports.getAllExternalTables(datasourceId)
|
||||||
return entities[tableName]
|
return entities[tableName]
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getTable = async (appId, tableId) => {
|
exports.getTable = async tableId => {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
if (isExternalTable(tableId)) {
|
if (isExternalTable(tableId)) {
|
||||||
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
||||||
const datasource = await db.get(datasourceId)
|
const datasource = await db.get(datasourceId)
|
||||||
const table = await exports.getExternalTable(appId, datasourceId, tableName)
|
const table = await exports.getExternalTable(datasourceId, tableName)
|
||||||
return { ...table, sql: isSQL(datasource) }
|
return { ...table, sql: isSQL(datasource) }
|
||||||
} else {
|
} else {
|
||||||
return db.get(tableId)
|
return db.get(tableId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
|
exports.checkForViewUpdates = async (table, rename, deletedColumns) => {
|
||||||
const views = await getViews(db)
|
const views = await getViews()
|
||||||
const tableViews = views.filter(view => view.meta.tableId === table._id)
|
const tableViews = views.filter(view => view.meta.tableId === table._id)
|
||||||
|
|
||||||
// Check each table view to see if impacted by this table action
|
// Check each table view to see if impacted by this table action
|
||||||
|
@ -319,7 +308,7 @@ exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
|
||||||
// Update view if required
|
// Update view if required
|
||||||
if (needsUpdated) {
|
if (needsUpdated) {
|
||||||
const newViewTemplate = viewTemplate(view.meta)
|
const newViewTemplate = viewTemplate(view.meta)
|
||||||
await saveView(db, null, view.name, newViewTemplate)
|
await saveView(null, view.name, newViewTemplate)
|
||||||
if (!newViewTemplate.meta.schema) {
|
if (!newViewTemplate.meta.schema) {
|
||||||
newViewTemplate.meta.schema = table.schema
|
newViewTemplate.meta.schema = table.schema
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const {
|
const {
|
||||||
generateUserMetadataID,
|
generateUserMetadataID,
|
||||||
getUserMetadataParams,
|
getUserMetadataParams,
|
||||||
|
@ -15,8 +14,10 @@ const {
|
||||||
} = require("@budibase/backend-core/db")
|
} = require("@budibase/backend-core/db")
|
||||||
const { doesDatabaseExist } = require("../../utilities")
|
const { doesDatabaseExist } = require("../../utilities")
|
||||||
const { UserStatus } = require("@budibase/backend-core/constants")
|
const { UserStatus } = require("@budibase/backend-core/constants")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
async function rawMetadata(db) {
|
async function rawMetadata() {
|
||||||
|
const db = getAppDB()
|
||||||
return (
|
return (
|
||||||
await db.allDocs(
|
await db.allDocs(
|
||||||
getUserMetadataParams(null, {
|
getUserMetadataParams(null, {
|
||||||
|
@ -54,13 +55,10 @@ function combineMetadataAndUser(user, metadata) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.syncGlobalUsers = async appId => {
|
exports.syncGlobalUsers = async () => {
|
||||||
// sync user metadata
|
// sync user metadata
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const [users, metadata] = await Promise.all([
|
const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()])
|
||||||
getGlobalUsers(appId),
|
|
||||||
rawMetadata(db),
|
|
||||||
])
|
|
||||||
const toWrite = []
|
const toWrite = []
|
||||||
for (let user of users) {
|
for (let user of users) {
|
||||||
const combined = await combineMetadataAndUser(user, metadata)
|
const combined = await combineMetadataAndUser(user, metadata)
|
||||||
|
@ -94,7 +92,7 @@ exports.syncUser = async function (ctx) {
|
||||||
let prodAppIds
|
let prodAppIds
|
||||||
// if they are a builder then get all production app IDs
|
// if they are a builder then get all production app IDs
|
||||||
if ((user.builder && user.builder.global) || deleting) {
|
if ((user.builder && user.builder.global) || deleting) {
|
||||||
prodAppIds = await getDeployedAppIDs(CouchDB)
|
prodAppIds = await getDeployedAppIDs()
|
||||||
} else {
|
} else {
|
||||||
prodAppIds = Object.entries(roles)
|
prodAppIds = Object.entries(roles)
|
||||||
.filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
|
.filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
|
||||||
|
@ -107,7 +105,7 @@ exports.syncUser = async function (ctx) {
|
||||||
if (!(await doesDatabaseExist(appId))) {
|
if (!(await doesDatabaseExist(appId))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const metadataId = generateUserMetadataID(userId)
|
const metadataId = generateUserMetadataID(userId)
|
||||||
let metadata
|
let metadata
|
||||||
try {
|
try {
|
||||||
|
@ -143,8 +141,8 @@ exports.syncUser = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.fetchMetadata = async function (ctx) {
|
exports.fetchMetadata = async function (ctx) {
|
||||||
const database = new CouchDB(ctx.appId)
|
const database = getAppDB()
|
||||||
const global = await getGlobalUsers(ctx.appId)
|
const global = await getGlobalUsers()
|
||||||
const metadata = await rawMetadata(database)
|
const metadata = await rawMetadata(database)
|
||||||
const users = []
|
const users = []
|
||||||
for (let user of global) {
|
for (let user of global) {
|
||||||
|
@ -171,8 +169,7 @@ exports.updateSelfMetadata = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updateMetadata = async function (ctx) {
|
exports.updateMetadata = async function (ctx) {
|
||||||
const appId = ctx.appId
|
const db = getAppDB()
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const user = ctx.request.body
|
const user = ctx.request.body
|
||||||
// this isn't applicable to the user
|
// this isn't applicable to the user
|
||||||
delete user.roles
|
delete user.roles
|
||||||
|
@ -184,7 +181,7 @@ exports.updateMetadata = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroyMetadata = async function (ctx) {
|
exports.destroyMetadata = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
try {
|
try {
|
||||||
const dbUser = await db.get(ctx.params.id)
|
const dbUser = await db.get(ctx.params.id)
|
||||||
await db.remove(dbUser._id, dbUser._rev)
|
await db.remove(dbUser._id, dbUser._rev)
|
||||||
|
@ -207,7 +204,7 @@ exports.setFlag = async function (ctx) {
|
||||||
ctx.throw(400, "Must supply a 'flag' field in request body.")
|
ctx.throw(400, "Must supply a 'flag' field in request body.")
|
||||||
}
|
}
|
||||||
const flagDocId = generateUserFlagID(userId)
|
const flagDocId = generateUserFlagID(userId)
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
let doc
|
let doc
|
||||||
try {
|
try {
|
||||||
doc = await db.get(flagDocId)
|
doc = await db.get(flagDocId)
|
||||||
|
@ -222,7 +219,7 @@ exports.setFlag = async function (ctx) {
|
||||||
exports.getFlags = async function (ctx) {
|
exports.getFlags = async function (ctx) {
|
||||||
const userId = ctx.user._id
|
const userId = ctx.user._id
|
||||||
const docId = generateUserFlagID(userId)
|
const docId = generateUserFlagID(userId)
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
let doc
|
let doc
|
||||||
try {
|
try {
|
||||||
doc = await db.get(docId)
|
doc = await db.get(docId)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const viewTemplate = require("./viewBuilder")
|
const viewTemplate = require("./viewBuilder")
|
||||||
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
const { apiFileReturn } = require("../../../utilities/fileSystem")
|
||||||
const exporters = require("./exporters")
|
const exporters = require("./exporters")
|
||||||
|
@ -6,14 +5,14 @@ const { saveView, getView, getViews, deleteView } = require("./utils")
|
||||||
const { fetchView } = require("../row")
|
const { fetchView } = require("../row")
|
||||||
const { getTable } = require("../table/utils")
|
const { getTable } = require("../table/utils")
|
||||||
const { FieldTypes } = require("../../../constants")
|
const { FieldTypes } = require("../../../constants")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
ctx.body = await getViews()
|
||||||
ctx.body = await getViews(db)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async ctx => {
|
exports.save = async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const { originalName, ...viewToSave } = ctx.request.body
|
const { originalName, ...viewToSave } = ctx.request.body
|
||||||
const view = viewTemplate(viewToSave)
|
const view = viewTemplate(viewToSave)
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ exports.save = async ctx => {
|
||||||
ctx.throw(400, "Cannot create view without a name")
|
ctx.throw(400, "Cannot create view without a name")
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveView(db, originalName, viewToSave.name, view)
|
await saveView(originalName, viewToSave.name, view)
|
||||||
|
|
||||||
// add views to table document
|
// add views to table document
|
||||||
const table = await db.get(ctx.request.body.tableId)
|
const table = await db.get(ctx.request.body.tableId)
|
||||||
|
@ -42,9 +41,9 @@ exports.save = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.destroy = async ctx => {
|
exports.destroy = async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
const viewName = decodeURI(ctx.params.viewName)
|
const viewName = decodeURI(ctx.params.viewName)
|
||||||
const view = await deleteView(db, viewName)
|
const view = await deleteView(viewName)
|
||||||
const table = await db.get(view.meta.tableId)
|
const table = await db.get(view.meta.tableId)
|
||||||
delete table.views[viewName]
|
delete table.views[viewName]
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
|
@ -53,9 +52,8 @@ exports.destroy = async ctx => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.exportView = async ctx => {
|
exports.exportView = async ctx => {
|
||||||
const db = new CouchDB(ctx.appId)
|
|
||||||
const viewName = decodeURI(ctx.query.view)
|
const viewName = decodeURI(ctx.query.view)
|
||||||
const view = await getView(db, viewName)
|
const view = await getView(viewName)
|
||||||
|
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
|
if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
|
||||||
|
@ -83,7 +81,7 @@ exports.exportView = async ctx => {
|
||||||
let schema = view && view.meta && view.meta.schema
|
let schema = view && view.meta && view.meta.schema
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
const tableId = ctx.params.tableId || view.meta.tableId
|
const tableId = ctx.params.tableId || view.meta.tableId
|
||||||
const table = await getTable(ctx.appId, tableId)
|
const table = await getTable(tableId)
|
||||||
schema = table.schema
|
schema = table.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,10 @@ const {
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
} = require("../../../db/utils")
|
} = require("../../../db/utils")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.getView = async (db, viewName) => {
|
exports.getView = async viewName => {
|
||||||
|
const db = getAppDB()
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
return designDoc.views[viewName]
|
return designDoc.views[viewName]
|
||||||
|
@ -22,7 +24,8 @@ exports.getView = async (db, viewName) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getViews = async db => {
|
exports.getViews = async () => {
|
||||||
|
const db = getAppDB()
|
||||||
const response = []
|
const response = []
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
|
@ -54,7 +57,8 @@ exports.getViews = async db => {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.saveView = async (db, originalName, viewName, viewTemplate) => {
|
exports.saveView = async (originalName, viewName, viewTemplate) => {
|
||||||
|
const db = getAppDB()
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
designDoc.views = {
|
designDoc.views = {
|
||||||
|
@ -91,7 +95,8 @@ exports.saveView = async (db, originalName, viewName, viewTemplate) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.deleteView = async (db, viewName) => {
|
exports.deleteView = async viewName => {
|
||||||
|
const db = getAppDB()
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
const view = designDoc.views[viewName]
|
const view = designDoc.views[viewName]
|
||||||
|
|
|
@ -82,7 +82,6 @@ describe("run misc tests", () => {
|
||||||
dataImport.schema[col] = { type: "string" }
|
dataImport.schema[col] = { type: "string" }
|
||||||
}
|
}
|
||||||
await tableUtils.handleDataImport(
|
await tableUtils.handleDataImport(
|
||||||
config.getAppId(),
|
|
||||||
{ userId: "test" },
|
{ userId: "test" },
|
||||||
table,
|
table,
|
||||||
dataImport
|
dataImport
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const rowController = require("../../../controllers/row")
|
const rowController = require("../../../controllers/row")
|
||||||
const appController = require("../../../controllers/application")
|
const appController = require("../../../controllers/application")
|
||||||
const CouchDB = require("../../../../db")
|
|
||||||
const { AppStatus } = require("../../../../db/utils")
|
const { AppStatus } = require("../../../../db/utils")
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
||||||
const { TENANT_ID } = require("../../../../tests/utilities/structures")
|
const { TENANT_ID } = require("../../../../tests/utilities/structures")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
function Request(appId, params) {
|
function Request(appId, params) {
|
||||||
this.appId = appId
|
this.appId = appId
|
||||||
|
@ -96,8 +96,8 @@ exports.checkPermissionsEndpoint = async ({
|
||||||
.expect(403)
|
.expect(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getDB = config => {
|
exports.getDB = () => {
|
||||||
return new CouchDB(config.getAppId())
|
return getAppDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.testAutomation = async (config, automation) => {
|
exports.testAutomation = async (config, automation) => {
|
||||||
|
|
|
@ -53,13 +53,12 @@ exports.cleanInputValues = (inputs, schema) => {
|
||||||
* the automation but is instead part of the Table/Table. This function will get the table schema and use it to instead
|
* the automation but is instead part of the Table/Table. This function will get the table schema and use it to instead
|
||||||
* perform the cleanInputValues function on the input row.
|
* perform the cleanInputValues function on the input row.
|
||||||
*
|
*
|
||||||
* @param {string} appId The instance which the Table/Table is contained under.
|
|
||||||
* @param {string} tableId The ID of the Table/Table which the schema is to be retrieved for.
|
* @param {string} tableId The ID of the Table/Table which the schema is to be retrieved for.
|
||||||
* @param {object} row The input row structure which requires clean-up after having been through template statements.
|
* @param {object} row The input row structure which requires clean-up after having been through template statements.
|
||||||
* @returns {Promise<Object>} The cleaned up rows object, will should now have all the required primitive types.
|
* @returns {Promise<Object>} The cleaned up rows object, will should now have all the required primitive types.
|
||||||
*/
|
*/
|
||||||
exports.cleanUpRow = async (appId, tableId, row) => {
|
exports.cleanUpRow = async (tableId, row) => {
|
||||||
let table = await getTable(appId, tableId)
|
let table = await getTable(tableId)
|
||||||
return exports.cleanInputValues(row, { properties: table.schema })
|
return exports.cleanInputValues(row, { properties: table.schema })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,6 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inputs.row = await automationUtils.cleanUpRow(
|
inputs.row = await automationUtils.cleanUpRow(
|
||||||
appId,
|
|
||||||
inputs.row.tableId,
|
inputs.row.tableId,
|
||||||
inputs.row
|
inputs.row
|
||||||
)
|
)
|
||||||
|
|
|
@ -87,7 +87,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (tableId) {
|
if (tableId) {
|
||||||
inputs.row = await automationUtils.cleanUpRow(appId, tableId, inputs.row)
|
inputs.row = await automationUtils.cleanUpRow(tableId, inputs.row)
|
||||||
}
|
}
|
||||||
await rowController.patch(ctx)
|
await rowController.patch(ctx)
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
const CouchDB = require("../index")
|
|
||||||
const { IncludeDocs, getLinkDocuments } = require("./linkUtils")
|
const { IncludeDocs, getLinkDocuments } = require("./linkUtils")
|
||||||
const {
|
const {
|
||||||
generateLinkID,
|
generateLinkID,
|
||||||
|
@ -7,6 +6,7 @@ const {
|
||||||
} = require("../utils")
|
} = require("../utils")
|
||||||
const Sentry = require("@sentry/node")
|
const Sentry = require("@sentry/node")
|
||||||
const { FieldTypes, RelationshipTypes } = require("../../constants")
|
const { FieldTypes, RelationshipTypes } = require("../../constants")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new link document structure which can be put to the database. It is important to
|
* Creates a new link document structure which can be put to the database. It is important to
|
||||||
|
@ -52,9 +52,8 @@ function LinkDocument(
|
||||||
}
|
}
|
||||||
|
|
||||||
class LinkController {
|
class LinkController {
|
||||||
constructor({ appId, tableId, row, table, oldTable }) {
|
constructor({ tableId, row, table, oldTable }) {
|
||||||
this._appId = appId
|
this._db = getAppDB()
|
||||||
this._db = new CouchDB(appId)
|
|
||||||
this._tableId = tableId
|
this._tableId = tableId
|
||||||
this._row = row
|
this._row = row
|
||||||
this._table = table
|
this._table = table
|
||||||
|
@ -99,7 +98,6 @@ class LinkController {
|
||||||
*/
|
*/
|
||||||
getRowLinkDocs(rowId) {
|
getRowLinkDocs(rowId) {
|
||||||
return getLinkDocuments({
|
return getLinkDocuments({
|
||||||
appId: this._appId,
|
|
||||||
tableId: this._tableId,
|
tableId: this._tableId,
|
||||||
rowId,
|
rowId,
|
||||||
includeDocs: IncludeDocs.INCLUDE,
|
includeDocs: IncludeDocs.INCLUDE,
|
||||||
|
@ -111,7 +109,6 @@ class LinkController {
|
||||||
*/
|
*/
|
||||||
getTableLinkDocs() {
|
getTableLinkDocs() {
|
||||||
return getLinkDocuments({
|
return getLinkDocuments({
|
||||||
appId: this._appId,
|
|
||||||
tableId: this._tableId,
|
tableId: this._tableId,
|
||||||
includeDocs: IncludeDocs.INCLUDE,
|
includeDocs: IncludeDocs.INCLUDE,
|
||||||
})
|
})
|
||||||
|
@ -230,7 +227,6 @@ class LinkController {
|
||||||
if (linkedSchema.relationshipType === RelationshipTypes.ONE_TO_MANY) {
|
if (linkedSchema.relationshipType === RelationshipTypes.ONE_TO_MANY) {
|
||||||
let links = (
|
let links = (
|
||||||
await getLinkDocuments({
|
await getLinkDocuments({
|
||||||
appId: this._appId,
|
|
||||||
tableId: field.tableId,
|
tableId: field.tableId,
|
||||||
rowId: linkId,
|
rowId: linkId,
|
||||||
includeDocs: IncludeDocs.EXCLUDE,
|
includeDocs: IncludeDocs.EXCLUDE,
|
||||||
|
|
|
@ -9,12 +9,12 @@ const {
|
||||||
getLinkedTable,
|
getLinkedTable,
|
||||||
} = require("./linkUtils")
|
} = require("./linkUtils")
|
||||||
const { flatten } = require("lodash")
|
const { flatten } = require("lodash")
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const { FieldTypes } = require("../../constants")
|
const { FieldTypes } = require("../../constants")
|
||||||
const { getMultiIDParams, USER_METDATA_PREFIX } = require("../../db/utils")
|
const { getMultiIDParams, USER_METDATA_PREFIX } = require("../../db/utils")
|
||||||
const { partition } = require("lodash")
|
const { partition } = require("lodash")
|
||||||
const { getGlobalUsersFromMetadata } = require("../../utilities/global")
|
const { getGlobalUsersFromMetadata } = require("../../utilities/global")
|
||||||
const { processFormulas } = require("../../utilities/rowProcessor/utils")
|
const { processFormulas } = require("../../utilities/rowProcessor/utils")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This functionality makes sure that when rows with links are created, updated or deleted they are processed
|
* This functionality makes sure that when rows with links are created, updated or deleted they are processed
|
||||||
|
@ -48,14 +48,13 @@ function clearRelationshipFields(table, rows) {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLinksForRows(appId, rows) {
|
async function getLinksForRows(rows) {
|
||||||
const tableIds = [...new Set(rows.map(el => el.tableId))]
|
const tableIds = [...new Set(rows.map(el => el.tableId))]
|
||||||
// start by getting all the link values for performance reasons
|
// start by getting all the link values for performance reasons
|
||||||
const responses = flatten(
|
const responses = flatten(
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
tableIds.map(tableId =>
|
tableIds.map(tableId =>
|
||||||
getLinkDocuments({
|
getLinkDocuments({
|
||||||
appId,
|
|
||||||
tableId: tableId,
|
tableId: tableId,
|
||||||
includeDocs: IncludeDocs.EXCLUDE,
|
includeDocs: IncludeDocs.EXCLUDE,
|
||||||
})
|
})
|
||||||
|
@ -72,9 +71,9 @@ async function getLinksForRows(appId, rows) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getFullLinkedDocs(ctx, appId, links) {
|
async function getFullLinkedDocs(links) {
|
||||||
// create DBs
|
// create DBs
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const linkedRowIds = links.map(link => link.id)
|
const linkedRowIds = links.map(link => link.id)
|
||||||
const uniqueRowIds = [...new Set(linkedRowIds)]
|
const uniqueRowIds = [...new Set(linkedRowIds)]
|
||||||
let dbRows = (await db.allDocs(getMultiIDParams(uniqueRowIds))).rows.map(
|
let dbRows = (await db.allDocs(getMultiIDParams(uniqueRowIds))).rows.map(
|
||||||
|
@ -88,7 +87,7 @@ async function getFullLinkedDocs(ctx, appId, links) {
|
||||||
let [users, other] = partition(linked, linkRow =>
|
let [users, other] = partition(linked, linkRow =>
|
||||||
linkRow._id.startsWith(USER_METDATA_PREFIX)
|
linkRow._id.startsWith(USER_METDATA_PREFIX)
|
||||||
)
|
)
|
||||||
users = await getGlobalUsersFromMetadata(appId, users)
|
users = await getGlobalUsersFromMetadata(users)
|
||||||
return [...other, ...users]
|
return [...other, ...users]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +95,6 @@ async function getFullLinkedDocs(ctx, appId, links) {
|
||||||
* Update link documents for a row or table - this is to be called by the API controller when a change is occurring.
|
* Update link documents for a row or table - this is to be called by the API controller when a change is occurring.
|
||||||
* @param {string} args.eventType states what type of change which is occurring, means this can be expanded upon in the
|
* @param {string} args.eventType states what type of change which is occurring, means this can be expanded upon in the
|
||||||
* future quite easily (all updates go through one function).
|
* future quite easily (all updates go through one function).
|
||||||
* @param {string} args.appId The ID of the instance in which the change is occurring.
|
|
||||||
* @param {string} args.tableId The ID of the of the table which is being changed.
|
* @param {string} args.tableId The ID of the of the table which is being changed.
|
||||||
* @param {object|null} args.row The row which is changing, e.g. created, updated or deleted.
|
* @param {object|null} args.row The row which is changing, e.g. created, updated or deleted.
|
||||||
* @param {object|null} args.table If the table has already been retrieved this can be used to reduce database gets.
|
* @param {object|null} args.table If the table has already been retrieved this can be used to reduce database gets.
|
||||||
|
@ -105,11 +103,8 @@ async function getFullLinkedDocs(ctx, appId, links) {
|
||||||
* row operations and the table for table operations.
|
* row operations and the table for table operations.
|
||||||
*/
|
*/
|
||||||
exports.updateLinks = async function (args) {
|
exports.updateLinks = async function (args) {
|
||||||
const { eventType, appId, row, tableId, table, oldTable } = args
|
const { eventType, row, tableId, table, oldTable } = args
|
||||||
const baseReturnObj = row == null ? table : row
|
const baseReturnObj = row == null ? table : row
|
||||||
if (appId == null) {
|
|
||||||
throw "Cannot operate without an instance ID."
|
|
||||||
}
|
|
||||||
// make sure table ID is set
|
// make sure table ID is set
|
||||||
if (tableId == null && table != null) {
|
if (tableId == null && table != null) {
|
||||||
args.tableId = table._id
|
args.tableId = table._id
|
||||||
|
@ -146,27 +141,23 @@ exports.updateLinks = async function (args) {
|
||||||
/**
|
/**
|
||||||
* Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row.
|
* Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row.
|
||||||
* This is required for formula fields, this may only be utilised internally (for now).
|
* This is required for formula fields, this may only be utilised internally (for now).
|
||||||
* @param {object} ctx The request which is looking for rows.
|
|
||||||
* @param {object} table The table from which the rows originated.
|
* @param {object} table The table from which the rows originated.
|
||||||
* @param {array<object>} rows The rows which are to be enriched.
|
* @param {array<object>} rows The rows which are to be enriched.
|
||||||
* @return {Promise<*>} returns the rows with all of the enriched relationships on it.
|
* @return {Promise<*>} returns the rows with all of the enriched relationships on it.
|
||||||
*/
|
*/
|
||||||
exports.attachFullLinkedDocs = async (ctx, table, rows) => {
|
exports.attachFullLinkedDocs = async (table, rows) => {
|
||||||
const appId = ctx.appId
|
|
||||||
const linkedTableIds = getLinkedTableIDs(table)
|
const linkedTableIds = getLinkedTableIDs(table)
|
||||||
if (linkedTableIds.length === 0) {
|
if (linkedTableIds.length === 0) {
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
// create DBs
|
|
||||||
const db = new CouchDB(appId)
|
|
||||||
// get all the links
|
// get all the links
|
||||||
const links = (await getLinksForRows(appId, rows)).filter(link =>
|
const links = (await getLinksForRows(rows)).filter(link =>
|
||||||
rows.some(row => row._id === link.thisId)
|
rows.some(row => row._id === link.thisId)
|
||||||
)
|
)
|
||||||
// clear any existing links that could be dupe'd
|
// clear any existing links that could be dupe'd
|
||||||
rows = clearRelationshipFields(table, rows)
|
rows = clearRelationshipFields(table, rows)
|
||||||
// now get the docs and combine into the rows
|
// now get the docs and combine into the rows
|
||||||
let linked = await getFullLinkedDocs(ctx, appId, links)
|
let linked = await getFullLinkedDocs(links)
|
||||||
const linkedTables = []
|
const linkedTables = []
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
for (let link of links.filter(link => link.thisId === row._id)) {
|
for (let link of links.filter(link => link.thisId === row._id)) {
|
||||||
|
@ -177,11 +168,7 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => {
|
||||||
if (linkedRow) {
|
if (linkedRow) {
|
||||||
const linkedTableId =
|
const linkedTableId =
|
||||||
linkedRow.tableId || getRelatedTableForField(table, link.fieldName)
|
linkedRow.tableId || getRelatedTableForField(table, link.fieldName)
|
||||||
const linkedTable = await getLinkedTable(
|
const linkedTable = await getLinkedTable(linkedTableId, linkedTables)
|
||||||
db,
|
|
||||||
linkedTableId,
|
|
||||||
linkedTables
|
|
||||||
)
|
|
||||||
if (linkedTable) {
|
if (linkedTable) {
|
||||||
row[link.fieldName].push(processFormulas(linkedTable, linkedRow))
|
row[link.fieldName].push(processFormulas(linkedTable, linkedRow))
|
||||||
}
|
}
|
||||||
|
@ -193,18 +180,16 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function will take the given enriched rows and squash the links to only contain the primary display field.
|
* This function will take the given enriched rows and squash the links to only contain the primary display field.
|
||||||
* @param {string} appId The app in which the tables/rows/links exist.
|
|
||||||
* @param {object} table The table from which the rows originated.
|
* @param {object} table The table from which the rows originated.
|
||||||
* @param {array<object>} enriched The pre-enriched rows (full docs) which are to be squashed.
|
* @param {array<object>} enriched The pre-enriched rows (full docs) which are to be squashed.
|
||||||
* @returns {Promise<Array>} The rows after having their links squashed to only contain the ID and primary display.
|
* @returns {Promise<Array>} The rows after having their links squashed to only contain the ID and primary display.
|
||||||
*/
|
*/
|
||||||
exports.squashLinksToPrimaryDisplay = async (appId, table, enriched) => {
|
exports.squashLinksToPrimaryDisplay = async (table, enriched) => {
|
||||||
const db = new CouchDB(appId)
|
|
||||||
// will populate this as we find them
|
// will populate this as we find them
|
||||||
const linkedTables = [table]
|
const linkedTables = [table]
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
// this only fetches the table if its not already in array
|
// this only fetches the table if its not already in array
|
||||||
const rowTable = await getLinkedTable(db, row.tableId, linkedTables)
|
const rowTable = await getLinkedTable(row.tableId, linkedTables)
|
||||||
for (let [column, schema] of Object.entries(rowTable.schema)) {
|
for (let [column, schema] of Object.entries(rowTable.schema)) {
|
||||||
if (schema.type !== FieldTypes.LINK || !Array.isArray(row[column])) {
|
if (schema.type !== FieldTypes.LINK || !Array.isArray(row[column])) {
|
||||||
continue
|
continue
|
||||||
|
@ -212,7 +197,7 @@ exports.squashLinksToPrimaryDisplay = async (appId, table, enriched) => {
|
||||||
const newLinks = []
|
const newLinks = []
|
||||||
for (let link of row[column]) {
|
for (let link of row[column]) {
|
||||||
const linkTblId = link.tableId || getRelatedTableForField(table, column)
|
const linkTblId = link.tableId || getRelatedTableForField(table, column)
|
||||||
const linkedTable = await getLinkedTable(db, linkTblId, linkedTables)
|
const linkedTable = await getLinkedTable(linkTblId, linkedTables)
|
||||||
const obj = { _id: link._id }
|
const obj = { _id: link._id }
|
||||||
if (link[linkedTable.primaryDisplay]) {
|
if (link[linkedTable.primaryDisplay]) {
|
||||||
obj.primaryDisplay = link[linkedTable.primaryDisplay]
|
obj.primaryDisplay = link[linkedTable.primaryDisplay]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const CouchDB = require("../index")
|
|
||||||
const Sentry = require("@sentry/node")
|
const Sentry = require("@sentry/node")
|
||||||
const { ViewNames, getQueryIndex } = require("../utils")
|
const { ViewNames, getQueryIndex } = require("../utils")
|
||||||
const { FieldTypes } = require("../../constants")
|
const { FieldTypes } = require("../../constants")
|
||||||
const { createLinkView } = require("../views/staticViews")
|
const { createLinkView } = require("../views/staticViews")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only needed so that boolean parameters are being used for includeDocs
|
* Only needed so that boolean parameters are being used for includeDocs
|
||||||
|
@ -17,7 +17,6 @@ exports.createLinkView = createLinkView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the linking documents, not the linked documents themselves.
|
* Gets the linking documents, not the linked documents themselves.
|
||||||
* @param {string} args.appId The instance in which we are searching for linked rows.
|
|
||||||
* @param {string} args.tableId The table which we are searching for linked rows against.
|
* @param {string} args.tableId The table which we are searching for linked rows against.
|
||||||
* @param {string|null} args.fieldName The name of column/field which is being altered, only looking for
|
* @param {string|null} args.fieldName The name of column/field which is being altered, only looking for
|
||||||
* linking documents that are related to it. If this is not specified then the table level will be assumed.
|
* linking documents that are related to it. If this is not specified then the table level will be assumed.
|
||||||
|
@ -30,8 +29,8 @@ exports.createLinkView = createLinkView
|
||||||
* (if any).
|
* (if any).
|
||||||
*/
|
*/
|
||||||
exports.getLinkDocuments = async function (args) {
|
exports.getLinkDocuments = async function (args) {
|
||||||
const { appId, tableId, rowId, includeDocs } = args
|
const { tableId, rowId, includeDocs } = args
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
let params
|
let params
|
||||||
if (rowId != null) {
|
if (rowId != null) {
|
||||||
params = { key: [tableId, rowId] }
|
params = { key: [tableId, rowId] }
|
||||||
|
@ -68,7 +67,7 @@ exports.getLinkDocuments = async function (args) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// check if the view doesn't exist, it should for all new instances
|
// check if the view doesn't exist, it should for all new instances
|
||||||
if (err != null && err.name === "not_found") {
|
if (err != null && err.name === "not_found") {
|
||||||
await exports.createLinkView(appId)
|
await exports.createLinkView()
|
||||||
return exports.getLinkDocuments(arguments[0])
|
return exports.getLinkDocuments(arguments[0])
|
||||||
} else {
|
} else {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
|
@ -89,7 +88,8 @@ exports.getLinkedTableIDs = table => {
|
||||||
.map(column => column.tableId)
|
.map(column => column.tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getLinkedTable = async (db, id, tables) => {
|
exports.getLinkedTable = async (id, tables) => {
|
||||||
|
const db = getAppDB()
|
||||||
let linkedTable = tables.find(table => table._id === id)
|
let linkedTable = tables.find(table => table._id === id)
|
||||||
if (linkedTable) {
|
if (linkedTable) {
|
||||||
return linkedTable
|
return linkedTable
|
||||||
|
|
|
@ -20,7 +20,6 @@ describe("test the link controller", () => {
|
||||||
|
|
||||||
function createLinkController(table, row = null, oldTable = null) {
|
function createLinkController(table, row = null, oldTable = null) {
|
||||||
const linkConfig = {
|
const linkConfig = {
|
||||||
appId: config.getAppId(),
|
|
||||||
tableId: table._id,
|
tableId: table._id,
|
||||||
table,
|
table,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
const TestConfig = require("../../tests/utilities/TestConfiguration")
|
||||||
const { basicTable, basicLinkedRow } = require("../../tests/utilities/structures")
|
const { basicTable } = require("../../tests/utilities/structures")
|
||||||
const linkUtils = require("../linkedRows/linkUtils")
|
const linkUtils = require("../linkedRows/linkUtils")
|
||||||
const links = require("../linkedRows")
|
|
||||||
const CouchDB = require("../index")
|
const CouchDB = require("../index")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
describe("test link functionality", () => {
|
describe("test link functionality", () => {
|
||||||
const config = new TestConfig(false)
|
const config = new TestConfig(false)
|
||||||
|
@ -11,18 +11,18 @@ describe("test link functionality", () => {
|
||||||
let db, table
|
let db, table
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
db = new CouchDB(config.getAppId())
|
db = getAppDB()
|
||||||
table = await config.createTable()
|
table = await config.createTable()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to retrieve a linked table from a list", async () => {
|
it("should be able to retrieve a linked table from a list", async () => {
|
||||||
const retrieved = await linkUtils.getLinkedTable(db, table._id, [table])
|
const retrieved = await linkUtils.getLinkedTable(table._id, [table])
|
||||||
expect(retrieved._id).toBe(table._id)
|
expect(retrieved._id).toBe(table._id)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should be able to retrieve a table from DB and update list", async () => {
|
it("should be able to retrieve a table from DB and update list", async () => {
|
||||||
const tables = []
|
const tables = []
|
||||||
const retrieved = await linkUtils.getLinkedTable(db, table._id, tables)
|
const retrieved = await linkUtils.getLinkedTable(table._id, tables)
|
||||||
expect(retrieved._id).toBe(table._id)
|
expect(retrieved._id).toBe(table._id)
|
||||||
expect(tables[0]).toBeDefined()
|
expect(tables[0]).toBeDefined()
|
||||||
})
|
})
|
||||||
|
@ -51,7 +51,6 @@ describe("test link functionality", () => {
|
||||||
const db = new CouchDB("test")
|
const db = new CouchDB("test")
|
||||||
await db.put({ _id: "_design/database", views: {} })
|
await db.put({ _id: "_design/database", views: {} })
|
||||||
const output = await linkUtils.getLinkDocuments({
|
const output = await linkUtils.getLinkDocuments({
|
||||||
appId: "test",
|
|
||||||
tableId: "test",
|
tableId: "test",
|
||||||
rowId: "test",
|
rowId: "test",
|
||||||
includeDocs: false,
|
includeDocs: false,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const CouchDB = require("../index")
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
const {
|
const {
|
||||||
DocumentTypes,
|
DocumentTypes,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
|
@ -21,12 +21,11 @@ const SCREEN_PREFIX = DocumentTypes.SCREEN + SEPARATOR
|
||||||
/**
|
/**
|
||||||
* Creates the link view for the instance, this will overwrite the existing one, but this should only
|
* Creates the link view for the instance, this will overwrite the existing one, but this should only
|
||||||
* be called if it is found that the view does not exist.
|
* be called if it is found that the view does not exist.
|
||||||
* @param {string} appId The instance to which the view should be added.
|
|
||||||
* @returns {Promise<void>} The view now exists, please note that the next view of this query will actually build it,
|
* @returns {Promise<void>} The view now exists, please note that the next view of this query will actually build it,
|
||||||
* so it may be slow.
|
* so it may be slow.
|
||||||
*/
|
*/
|
||||||
exports.createLinkView = async appId => {
|
exports.createLinkView = async () => {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
const view = {
|
const view = {
|
||||||
map: function (doc) {
|
map: function (doc) {
|
||||||
|
@ -57,8 +56,8 @@ exports.createLinkView = async appId => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createRoutingView = async appId => {
|
exports.createRoutingView = async () => {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
const view = {
|
const view = {
|
||||||
// if using variables in a map function need to inject them before use
|
// if using variables in a map function need to inject them before use
|
||||||
|
@ -78,8 +77,8 @@ exports.createRoutingView = async appId => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchIndex(appId, indexName, fnString) {
|
async function searchIndex(indexName, fnString) {
|
||||||
const db = new CouchDB(appId)
|
const db = getAppDB()
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
designDoc.indexes = {
|
designDoc.indexes = {
|
||||||
[indexName]: {
|
[indexName]: {
|
||||||
|
@ -90,9 +89,8 @@ async function searchIndex(appId, indexName, fnString) {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createAllSearchIndex = async appId => {
|
exports.createAllSearchIndex = async () => {
|
||||||
await searchIndex(
|
await searchIndex(
|
||||||
appId,
|
|
||||||
SearchIndexes.ROWS,
|
SearchIndexes.ROWS,
|
||||||
function (doc) {
|
function (doc) {
|
||||||
function idx(input, prev) {
|
function idx(input, prev) {
|
||||||
|
|
|
@ -52,7 +52,10 @@ export function buildExternalTableId(datasourceId: string, tableName: string) {
|
||||||
return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}`
|
return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function breakExternalTableId(tableId: string) {
|
export function breakExternalTableId(tableId: string | undefined) {
|
||||||
|
if (!tableId) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
const parts = tableId.split(DOUBLE_SEPARATOR)
|
const parts = tableId.split(DOUBLE_SEPARATOR)
|
||||||
let tableName = parts.pop()
|
let tableName = parts.pop()
|
||||||
// if they need joined
|
// if they need joined
|
||||||
|
|
|
@ -43,13 +43,13 @@ module.exports =
|
||||||
|
|
||||||
// need to check this first, in-case public access, don't check authed until last
|
// need to check this first, in-case public access, don't check authed until last
|
||||||
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
||||||
const hierarchy = await getUserRoleHierarchy(ctx.appId, roleId, {
|
const hierarchy = await getUserRoleHierarchy(roleId, {
|
||||||
idOnly: false,
|
idOnly: false,
|
||||||
})
|
})
|
||||||
const permError = "User does not have permission"
|
const permError = "User does not have permission"
|
||||||
let possibleRoleIds = []
|
let possibleRoleIds = []
|
||||||
if (hasResource(ctx)) {
|
if (hasResource(ctx)) {
|
||||||
possibleRoleIds = await getRequiredResourceRole(ctx.appId, permLevel, ctx)
|
possibleRoleIds = await getRequiredResourceRole(permLevel, ctx)
|
||||||
}
|
}
|
||||||
// check if we found a role, if not fallback to base permissions
|
// check if we found a role, if not fallback to base permissions
|
||||||
if (possibleRoleIds.length > 0) {
|
if (possibleRoleIds.length > 0) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ const { generateUserMetadataID, isDevAppID } = require("../db/utils")
|
||||||
const { dbExists } = require("@budibase/backend-core/db")
|
const { dbExists } = require("@budibase/backend-core/db")
|
||||||
const { isUserInAppTenant } = require("@budibase/backend-core/tenancy")
|
const { isUserInAppTenant } = require("@budibase/backend-core/tenancy")
|
||||||
const { getCachedSelf } = require("../utilities/global")
|
const { getCachedSelf } = require("../utilities/global")
|
||||||
const CouchDB = require("../db")
|
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const { isWebhookEndpoint } = require("./utils")
|
const { isWebhookEndpoint } = require("./utils")
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ module.exports = async (ctx, next) => {
|
||||||
// check the app exists referenced in cookie
|
// check the app exists referenced in cookie
|
||||||
if (appCookie) {
|
if (appCookie) {
|
||||||
const appId = appCookie.appId
|
const appId = appCookie.appId
|
||||||
const exists = await dbExists(CouchDB, appId)
|
const exists = await dbExists(appId)
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
clearCookie(ctx, Cookies.CurrentApp)
|
clearCookie(ctx, Cookies.CurrentApp)
|
||||||
return next()
|
return next()
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
||||||
const { getAllApps } = require("@budibase/backend-core/db")
|
const { getAllApps } = require("@budibase/backend-core/db")
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
|
const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
|
||||||
|
|
||||||
exports.run = async () => {
|
exports.run = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
// get app count
|
// get app count
|
||||||
const devApps = await getAllApps(CouchDB, { dev: true })
|
const devApps = await getAllApps({ dev: true })
|
||||||
const appCount = devApps ? devApps.length : 0
|
const appCount = devApps ? devApps.length : 0
|
||||||
|
|
||||||
// sync app count
|
// sync app count
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
||||||
const { getAllApps } = require("@budibase/backend-core/db")
|
const { getAllApps } = require("@budibase/backend-core/db")
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
|
const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
|
||||||
const { getUniqueRows } = require("../../utilities/usageQuota/rows")
|
const { getUniqueRows } = require("../../utilities/usageQuota/rows")
|
||||||
|
|
||||||
exports.run = async () => {
|
exports.run = async () => {
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
// get all rows in all apps
|
// get all rows in all apps
|
||||||
const allApps = await getAllApps(CouchDB, { all: true })
|
const allApps = await getAllApps({ all: true })
|
||||||
const appIds = allApps ? allApps.map(app => app.appId) : []
|
const appIds = allApps ? allApps.map(app => app.appId) : []
|
||||||
const rows = await getUniqueRows(appIds)
|
const rows = await getUniqueRows(appIds)
|
||||||
const rowCount = rows ? rows.length : 0
|
const rowCount = rows ? rows.length : 0
|
||||||
|
|
|
@ -23,6 +23,7 @@ const { createASession } = require("@budibase/backend-core/sessions")
|
||||||
const { user: userCache } = require("@budibase/backend-core/cache")
|
const { user: userCache } = require("@budibase/backend-core/cache")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const newid = require("../../db/newid")
|
const newid = require("../../db/newid")
|
||||||
|
const context = require("@budibase/backend-core/context")
|
||||||
core.init(CouchDB)
|
core.init(CouchDB)
|
||||||
|
|
||||||
const GLOBAL_USER_ID = "us_uuid1"
|
const GLOBAL_USER_ID = "us_uuid1"
|
||||||
|
@ -50,6 +51,7 @@ class TestConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _req(config, params, controlFunc) {
|
async _req(config, params, controlFunc) {
|
||||||
|
context.updateAppId(this.appId)
|
||||||
const request = {}
|
const request = {}
|
||||||
// fake cookies, we don't need them
|
// fake cookies, we don't need them
|
||||||
request.cookies = { set: () => {}, get: () => {} }
|
request.cookies = { set: () => {}, get: () => {} }
|
||||||
|
@ -165,6 +167,7 @@ class TestConfiguration {
|
||||||
// create dev app
|
// create dev app
|
||||||
this.app = await this._req({ name: appName }, null, controllers.app.create)
|
this.app = await this._req({ name: appName }, null, controllers.app.create)
|
||||||
this.appId = this.app.appId
|
this.appId = this.app.appId
|
||||||
|
context.updateAppId(this.appId)
|
||||||
|
|
||||||
// create production app
|
// create production app
|
||||||
this.prodApp = await this.deploy()
|
this.prodApp = await this.deploy()
|
||||||
|
|
|
@ -20,6 +20,7 @@ const {
|
||||||
LINK_USER_METADATA_PREFIX,
|
LINK_USER_METADATA_PREFIX,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
const MemoryStream = require("memorystream")
|
const MemoryStream = require("memorystream")
|
||||||
|
const { getAppId } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..")
|
const TOP_LEVEL_PATH = join(__dirname, "..", "..", "..")
|
||||||
const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules")
|
const NODE_MODULES_PATH = join(TOP_LEVEL_PATH, "node_modules")
|
||||||
|
@ -251,7 +252,8 @@ exports.downloadTemplate = async (type, name) => {
|
||||||
/**
|
/**
|
||||||
* Retrieves component libraries from object store (or tmp symlink if in local)
|
* Retrieves component libraries from object store (or tmp symlink if in local)
|
||||||
*/
|
*/
|
||||||
exports.getComponentLibraryManifest = async (appId, library) => {
|
exports.getComponentLibraryManifest = async library => {
|
||||||
|
const appId = getAppId()
|
||||||
const filename = "manifest.json"
|
const filename = "manifest.json"
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
// when testing in cypress and so on we need to get the package
|
// when testing in cypress and so on we need to get the package
|
||||||
|
|
|
@ -11,8 +11,10 @@ const {
|
||||||
isUserInAppTenant,
|
isUserInAppTenant,
|
||||||
} = require("@budibase/backend-core/tenancy")
|
} = require("@budibase/backend-core/tenancy")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
|
const { getAppId } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.updateAppRole = (appId, user) => {
|
exports.updateAppRole = (user, { appId } = {}) => {
|
||||||
|
appId = appId || getAppId()
|
||||||
if (!user || !user.roles) {
|
if (!user || !user.roles) {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
@ -35,18 +37,18 @@ exports.updateAppRole = (appId, user) => {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
function processUser(appId, user) {
|
function processUser(user, { appId } = {}) {
|
||||||
if (user) {
|
if (user) {
|
||||||
delete user.password
|
delete user.password
|
||||||
}
|
}
|
||||||
return exports.updateAppRole(appId, user)
|
return exports.updateAppRole(user, { appId })
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getCachedSelf = async (ctx, appId) => {
|
exports.getCachedSelf = async (ctx, appId) => {
|
||||||
// this has to be tenant aware, can't depend on the context to find it out
|
// this has to be tenant aware, can't depend on the context to find it out
|
||||||
// running some middlewares before the tenancy causes context to break
|
// running some middlewares before the tenancy causes context to break
|
||||||
const user = await userCache.getUser(ctx.user._id)
|
const user = await userCache.getUser(ctx.user._id)
|
||||||
return processUser(appId, user)
|
return processUser(user, appId)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getRawGlobalUser = async userId => {
|
exports.getRawGlobalUser = async userId => {
|
||||||
|
@ -54,12 +56,13 @@ exports.getRawGlobalUser = async userId => {
|
||||||
return db.get(getGlobalIDFromUserMetadataID(userId))
|
return db.get(getGlobalIDFromUserMetadataID(userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getGlobalUser = async (appId, userId) => {
|
exports.getGlobalUser = async userId => {
|
||||||
let user = await exports.getRawGlobalUser(userId)
|
let user = await exports.getRawGlobalUser(userId)
|
||||||
return processUser(appId, user)
|
return processUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getGlobalUsers = async (appId = null, users = null) => {
|
exports.getGlobalUsers = async (users = null) => {
|
||||||
|
const appId = getAppId()
|
||||||
const db = getGlobalDB()
|
const db = getGlobalDB()
|
||||||
let globalUsers
|
let globalUsers
|
||||||
if (users) {
|
if (users) {
|
||||||
|
@ -86,11 +89,11 @@ exports.getGlobalUsers = async (appId = null, users = null) => {
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
return globalUsers
|
return globalUsers
|
||||||
}
|
}
|
||||||
return globalUsers.map(user => exports.updateAppRole(appId, user))
|
return globalUsers.map(user => exports.updateAppRole(user))
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getGlobalUsersFromMetadata = async (appId, users) => {
|
exports.getGlobalUsersFromMetadata = async users => {
|
||||||
const globalUsers = await exports.getGlobalUsers(appId, users)
|
const globalUsers = await exports.getGlobalUsers(users)
|
||||||
return users.map(user => {
|
return users.map(user => {
|
||||||
const globalUser = globalUsers.find(
|
const globalUser = globalUsers.find(
|
||||||
globalUser => globalUser && user._id.includes(globalUser._id)
|
globalUser => globalUser && user._id.includes(globalUser._id)
|
||||||
|
|
|
@ -10,7 +10,7 @@ const {
|
||||||
getDeployedAppID,
|
getDeployedAppID,
|
||||||
dbExists,
|
dbExists,
|
||||||
} = require("@budibase/backend-core/db")
|
} = require("@budibase/backend-core/db")
|
||||||
const CouchDB = require("../../db")
|
const { getAppId } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
const BASE_AUTO_ID = 1
|
const BASE_AUTO_ID = 1
|
||||||
|
|
||||||
|
@ -263,14 +263,13 @@ exports.outputProcessing = async (
|
||||||
rows,
|
rows,
|
||||||
opts = { squash: true }
|
opts = { squash: true }
|
||||||
) => {
|
) => {
|
||||||
const appId = ctx.appId
|
|
||||||
let wasArray = true
|
let wasArray = true
|
||||||
if (!(rows instanceof Array)) {
|
if (!(rows instanceof Array)) {
|
||||||
rows = [rows]
|
rows = [rows]
|
||||||
wasArray = false
|
wasArray = false
|
||||||
}
|
}
|
||||||
// attach any linked row information
|
// attach any linked row information
|
||||||
let enriched = await linkRows.attachFullLinkedDocs(ctx, table, rows)
|
let enriched = await linkRows.attachFullLinkedDocs(table, rows)
|
||||||
|
|
||||||
// process formulas
|
// process formulas
|
||||||
enriched = processFormulas(table, enriched)
|
enriched = processFormulas(table, enriched)
|
||||||
|
@ -289,29 +288,25 @@ exports.outputProcessing = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opts.squash) {
|
if (opts.squash) {
|
||||||
enriched = await linkRows.squashLinksToPrimaryDisplay(
|
enriched = await linkRows.squashLinksToPrimaryDisplay(table, enriched)
|
||||||
appId,
|
|
||||||
table,
|
|
||||||
enriched
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return wasArray ? enriched : enriched[0]
|
return wasArray ? enriched : enriched[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up any attachments that were attached to a row.
|
* Clean up any attachments that were attached to a row.
|
||||||
* @param {string} appId The ID of the app from which a row is being deleted.
|
|
||||||
* @param {object} table The table from which a row is being removed.
|
* @param {object} table The table from which a row is being removed.
|
||||||
* @param {any} row optional - the row being removed.
|
* @param {any} row optional - the row being removed.
|
||||||
* @param {any} rows optional - if multiple rows being deleted can do this in bulk.
|
* @param {any} rows optional - if multiple rows being deleted can do this in bulk.
|
||||||
* @param {any} oldRow optional - if updating a row this will determine the difference.
|
* @param {any} oldRow optional - if updating a row this will determine the difference.
|
||||||
* @return {Promise<void>} When all attachments have been removed this will return.
|
* @return {Promise<void>} When all attachments have been removed this will return.
|
||||||
*/
|
*/
|
||||||
exports.cleanupAttachments = async (appId, table, { row, rows, oldRow }) => {
|
exports.cleanupAttachments = async (table, { row, rows, oldRow }) => {
|
||||||
|
const appId = getAppId()
|
||||||
if (!isProdAppID(appId)) {
|
if (!isProdAppID(appId)) {
|
||||||
const prodAppId = getDeployedAppID(appId)
|
const prodAppId = getDeployedAppID(appId)
|
||||||
// if prod exists, then don't allow deleting
|
// if prod exists, then don't allow deleting
|
||||||
const exists = await dbExists(CouchDB, prodAppId)
|
const exists = await dbExists(prodAppId)
|
||||||
if (exists) {
|
if (exists) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
const CouchDB = require("../db")
|
|
||||||
const { InternalTables } = require("../db/utils")
|
const { InternalTables } = require("../db/utils")
|
||||||
const { getGlobalUser } = require("../utilities/global")
|
const { getGlobalUser } = require("../utilities/global")
|
||||||
|
const { getAppDB } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
exports.getFullUser = async (ctx, userId) => {
|
exports.getFullUser = async (ctx, userId) => {
|
||||||
const global = await getGlobalUser(ctx.appId, userId)
|
const global = await getGlobalUser(userId)
|
||||||
let metadata
|
let metadata
|
||||||
try {
|
try {
|
||||||
// this will throw an error if the db doesn't exist, or there is no appId
|
// this will throw an error if the db doesn't exist, or there is no appId
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = getAppDB()
|
||||||
metadata = await db.get(userId)
|
metadata = await db.get(userId)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// it is fine if there is no user metadata, just remove global db info
|
// it is fine if there is no user metadata, just remove global db info
|
||||||
|
|
|
@ -70,7 +70,7 @@ exports.getGlobalSelf = async (ctx, appId = null) => {
|
||||||
}
|
}
|
||||||
let json = await response.json()
|
let json = await response.json()
|
||||||
if (appId) {
|
if (appId) {
|
||||||
json = updateAppRole(appId, json)
|
json = updateAppRole(json)
|
||||||
}
|
}
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ const {
|
||||||
upload,
|
upload,
|
||||||
ObjectStoreBuckets,
|
ObjectStoreBuckets,
|
||||||
} = require("@budibase/backend-core/objectStore")
|
} = require("@budibase/backend-core/objectStore")
|
||||||
const CouchDB = require("../../../db")
|
|
||||||
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
const { googleCallbackUrl, oidcCallbackUrl } = require("./auth")
|
const { googleCallbackUrl, oidcCallbackUrl } = require("./auth")
|
||||||
|
@ -252,7 +251,7 @@ exports.configChecklist = async function (ctx) {
|
||||||
// TODO: Watch get started video
|
// TODO: Watch get started video
|
||||||
|
|
||||||
// Apps exist
|
// Apps exist
|
||||||
const apps = await getAllApps(CouchDB, { idsOnly: true })
|
const apps = await getAllApps({ idsOnly: true })
|
||||||
|
|
||||||
// They have set up SMTP
|
// They have set up SMTP
|
||||||
const smtpConfig = await getScopedFullConfig(db, {
|
const smtpConfig = await getScopedFullConfig(db, {
|
||||||
|
|
|
@ -9,7 +9,7 @@ const CouchDB = require("../../../db")
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const tenantId = ctx.user.tenantId
|
const tenantId = ctx.user.tenantId
|
||||||
// always use the dev apps as they'll be most up to date (true)
|
// always use the dev apps as they'll be most up to date (true)
|
||||||
const apps = await getAllApps(CouchDB, { tenantId, all: true })
|
const apps = await getAllApps({ tenantId, all: true })
|
||||||
const promises = []
|
const promises = []
|
||||||
for (let app of apps) {
|
for (let app of apps) {
|
||||||
// use dev app IDs
|
// use dev app IDs
|
||||||
|
|
Loading…
Reference in New Issue