From 6d24a30d91af98cb78611f893bb3442522b8af50 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 5 Jul 2023 18:28:04 +0100 Subject: [PATCH 001/117] Basic refactor work, the types required for the new API endpoints. --- packages/types/src/api/web/user.ts | 7 +++++++ packages/types/src/documents/global/user.ts | 2 ++ packages/worker/src/api/controllers/global/users.ts | 8 ++++++++ packages/worker/src/api/routes/global/users.ts | 3 +++ 4 files changed, 20 insertions(+) diff --git a/packages/types/src/api/web/user.ts b/packages/types/src/api/web/user.ts index 619362805a..4a27f781af 100644 --- a/packages/types/src/api/web/user.ts +++ b/packages/types/src/api/web/user.ts @@ -85,3 +85,10 @@ export interface AcceptUserInviteResponse { export interface SyncUserRequest { previousUser?: User } + +export interface AddAppBuilderRequest { + userId: string + appId: string +} + +export interface RemoveAppBuilderRequest {} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 9b4aadf404..cae3c0e6e2 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -43,9 +43,11 @@ export interface User extends Document { roles: UserRoles builder?: { global: boolean + apps?: string[] } admin?: { global: boolean + apps?: string[] } password?: string status?: UserStatus diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 320f7be01a..b3720d578f 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -8,6 +8,8 @@ import env from "../../../environment" import { AcceptUserInviteRequest, AcceptUserInviteResponse, + AddAppBuilderRequest, + RemoveAppBuilderRequest, BulkUserRequest, BulkUserResponse, CloudAccount, @@ -431,3 +433,9 @@ export const inviteAccept = async ( ctx.throw(400, "Unable to create new user, invitation invalid.") } } + +export const addAppBuilder = async (ctx: Ctx) => {} + +export const removeAppBuilder = async ( + ctx: Ctx +) => {} diff --git a/packages/worker/src/api/routes/global/users.ts b/packages/worker/src/api/routes/global/users.ts index 47e76c17be..557065e9a4 100644 --- a/packages/worker/src/api/routes/global/users.ts +++ b/packages/worker/src/api/routes/global/users.ts @@ -5,6 +5,7 @@ import Joi from "joi" import cloudRestricted from "../../../middleware/cloudRestricted" import { users } from "../validation" import * as selfController from "../../controllers/global/self" +import { addAppBuilder } from "../../controllers/global/users" const router: Router = new Router() @@ -131,5 +132,7 @@ router users.buildUserSaveValidation(), selfController.updateSelf ) + .post("/api/global/users/builder", controller.addAppBuilder) + .delete("/api/global/users/builder", controller.removeAppBuilder) export default router From 39746e0bf0db3633723ee2df60551ec3b25469df Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 18 Jul 2023 16:57:48 +0100 Subject: [PATCH 002/117] Main body of work to handle the new approach of per app builders support. --- packages/backend-core/src/constants/db.ts | 2 - packages/backend-core/src/db/views.ts | 11 ---- .../backend-core/src/events/identification.ts | 5 +- .../src/middleware/builderOnly.ts | 12 ++-- .../src/middleware/builderOrAdmin.ts | 15 +++-- packages/backend-core/src/users.ts | 62 ++++++++++++++++++- .../server/src/api/controllers/application.ts | 4 +- packages/server/src/middleware/authorized.ts | 11 +++- packages/server/src/middleware/currentapp.ts | 14 ++--- .../functions/backfill/global/users.ts | 10 ++- packages/server/src/utilities/global.ts | 6 +- packages/types/src/documents/global/user.ts | 1 - packages/worker/src/sdk/users/events.ts | 20 +++--- packages/worker/src/sdk/users/users.ts | 4 +- 14 files changed, 115 insertions(+), 62 deletions(-) diff --git a/packages/backend-core/src/constants/db.ts b/packages/backend-core/src/constants/db.ts index be49b9f261..34dab6c088 100644 --- a/packages/backend-core/src/constants/db.ts +++ b/packages/backend-core/src/constants/db.ts @@ -14,8 +14,6 @@ export enum ViewName { USER_BY_APP = "by_app", USER_BY_EMAIL = "by_email2", BY_API_KEY = "by_api_key", - /** @deprecated - could be deleted */ - USER_BY_BUILDERS = "by_builders", LINK = "by_link", ROUTING = "screen_routes", AUTOMATION_LOGS = "automation_logs", diff --git a/packages/backend-core/src/db/views.ts b/packages/backend-core/src/db/views.ts index fddb1ab34b..7f5ef29a0a 100644 --- a/packages/backend-core/src/db/views.ts +++ b/packages/backend-core/src/db/views.ts @@ -105,16 +105,6 @@ export const createApiKeyView = async () => { await createView(db, viewJs, ViewName.BY_API_KEY) } -export const createUserBuildersView = async () => { - const db = getGlobalDB() - const viewJs = `function(doc) { - if (doc.builder && doc.builder.global === true) { - emit(doc._id, doc._id) - } - }` - await createView(db, viewJs, ViewName.USER_BY_BUILDERS) -} - export interface QueryViewOptions { arrayResponse?: boolean } @@ -223,7 +213,6 @@ export const queryPlatformView = async ( const CreateFuncByName: any = { [ViewName.USER_BY_EMAIL]: createNewUserEmailView, [ViewName.BY_API_KEY]: createApiKeyView, - [ViewName.USER_BY_BUILDERS]: createUserBuildersView, [ViewName.USER_BY_APP]: createUserAppView, } diff --git a/packages/backend-core/src/events/identification.ts b/packages/backend-core/src/events/identification.ts index 5eb11d1354..52fcb2431f 100644 --- a/packages/backend-core/src/events/identification.ts +++ b/packages/backend-core/src/events/identification.ts @@ -21,6 +21,7 @@ import { processors } from "./processors" import { newid } from "../utils" import * as installation from "../installation" import * as configs from "../configs" +import * as users from "../users" import { withCache, TTL, CacheKey } from "../cache/generic" /** @@ -164,8 +165,8 @@ const identifyUser = async ( const id = user._id as string const tenantId = await getEventTenantId(user.tenantId) const type = IdentityType.USER - let builder = user.builder?.global || false - let admin = user.admin?.global || false + let builder = users.hasBuilderPermissions(user) + let admin = users.hasAdminPermissions(user) let providerType if (isSSOUser(user)) { providerType = user.providerType diff --git a/packages/backend-core/src/middleware/builderOnly.ts b/packages/backend-core/src/middleware/builderOnly.ts index a00fd63a22..744321252e 100644 --- a/packages/backend-core/src/middleware/builderOnly.ts +++ b/packages/backend-core/src/middleware/builderOnly.ts @@ -1,10 +1,10 @@ -import { BBContext } from "@budibase/types" +import { UserCtx } from "@budibase/types" +import { isBuilder } from "../users" +import { getAppId } from "../context" -export default async (ctx: BBContext, next: any) => { - if ( - !ctx.internal && - (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) - ) { +export default async (ctx: UserCtx, next: any) => { + const appId = getAppId() + if (!ctx.internal && !isBuilder(ctx.user, appId)) { ctx.throw(403, "Builder user only endpoint.") } return next() diff --git a/packages/backend-core/src/middleware/builderOrAdmin.ts b/packages/backend-core/src/middleware/builderOrAdmin.ts index 26bb3a1bda..2ba5bfe1e2 100644 --- a/packages/backend-core/src/middleware/builderOrAdmin.ts +++ b/packages/backend-core/src/middleware/builderOrAdmin.ts @@ -1,12 +1,11 @@ -import { BBContext } from "@budibase/types" +import { UserCtx } from "@budibase/types" +import { isBuilder, isAdmin } from "../users" +import { getAppId } from "../context" -export default async (ctx: BBContext, next: any) => { - if ( - !ctx.internal && - (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) && - (!ctx.user || !ctx.user.admin || !ctx.user.admin.global) - ) { - ctx.throw(403, "Builder user only endpoint.") +export default async (ctx: UserCtx, next: any) => { + const appId = getAppId() + if (!ctx.internal && !isBuilder(ctx.user, appId) && !isAdmin(ctx.user)) { + ctx.throw(403, "Admin/Builder user only endpoint.") } return next() } diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index b49058f546..2e6ede3cf3 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -12,7 +12,12 @@ import { UNICODE_MAX, ViewName, } from "./db" -import { BulkDocsResponse, SearchUsersRequest, User } from "@budibase/types" +import { + BulkDocsResponse, + SearchUsersRequest, + User, + ContextUser, +} from "@budibase/types" import { getGlobalDB } from "./context" import * as context from "./context" @@ -178,7 +183,7 @@ export const getGlobalUserByAppPage = (appId: string, user: User) => { * Performs a starts with search on the global email view. */ export const searchGlobalUsersByEmail = async ( - email: string, + email: string | unknown, opts: any, getOpts?: GetOpts ) => { @@ -248,3 +253,56 @@ export async function getUserCount() { }) return response.total_rows } + +// checks if a user is specifically a builder, given an app ID +export function isBuilder(user: User | ContextUser, appId?: string) { + if (user.builder?.global) { + return true + } else if (appId && user.builder?.apps?.includes(appId)) { + return true + } + return false +} + +// alias for hasAdminPermission, currently do the same thing +// in future whether someone has admin permissions and whether they are +// an admin for a specific resource could be separated +export function isAdmin(user: User | ContextUser) { + return hasAdminPermissions(user) +} + +// checks if a user is capable of building any app +export function hasBuilderPermissions(user?: User | ContextUser) { + if (!user) { + return false + } + return user.builder?.global || user.builder?.apps?.length !== 0 +} + +// checks if a user is capable of being an admin +export function hasAdminPermissions(user?: User | ContextUser) { + if (!user) { + return false + } + return user.admin?.global +} + +// used to remove the builder/admin permissions, for processing the +// user as an app user (they may have some specific role/group +export function removePortalUserPermissions(user: User | ContextUser) { + delete user.admin + delete user.builder + return user +} + +export function cleanseUserObject(user: User | ContextUser, base?: User) { + delete user.admin + delete user.builder + delete user.roles + if (base) { + user.admin = base.admin + user.builder = base.builder + user.roles = base.roles + } + return user +} diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 418ccf637e..6a0088d4dc 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -30,6 +30,7 @@ import { objectStore, roles, tenancy, + users, } from "@budibase/backend-core" import { USERS_TABLE_SCHEMA } from "../../constants" import { @@ -222,6 +223,7 @@ export async function fetchAppDefinition(ctx: UserCtx) { export async function fetchAppPackage(ctx: UserCtx) { const db = context.getAppDB() + const appId = context.getAppId() let application = await db.get(DocumentType.APP_METADATA) const layouts = await getLayouts() let screens = await getScreens() @@ -233,7 +235,7 @@ export async function fetchAppPackage(ctx: UserCtx) { ) // Only filter screens if the user is not a builder - if (!(ctx.user.builder && ctx.user.builder.global)) { + if (!users.isBuilder(ctx.user, appId)) { const userRoleId = getUserRoleId(ctx) const accessController = new roles.AccessController() screens = await accessController.checkScreensAccess(screens, userRoleId) diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index 6268163fb2..81e42604bc 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -1,4 +1,10 @@ -import { roles, permissions, auth, context } from "@budibase/backend-core" +import { + roles, + permissions, + auth, + context, + users, +} from "@budibase/backend-core" import { Role } from "@budibase/types" import builderMiddleware from "./builder" import { isWebhookEndpoint } from "./utils" @@ -21,8 +27,9 @@ const checkAuthorized = async ( permType: any, permLevel: any ) => { + const appId = context.getAppId() // check if this is a builder api and the user is not a builder - const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global + const isBuilder = users.isBuilder(ctx.user, appId) const isBuilderApi = permType === permissions.PermissionType.BUILDER if (isBuilderApi && !isBuilder) { return ctx.throw(403, "Not Authorized") diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts index 6879a103bc..10ee13a67a 100644 --- a/packages/server/src/middleware/currentapp.ts +++ b/packages/server/src/middleware/currentapp.ts @@ -4,12 +4,14 @@ import { roles, tenancy, context, + users, } from "@budibase/backend-core" import { generateUserMetadataID, isDevAppID } from "../db/utils" import { getCachedSelf } from "../utilities/global" import env from "../environment" import { isWebhookEndpoint } from "./utils" -import { UserCtx } from "@budibase/types" +import { UserCtx, ContextUser } from "@budibase/types" +import { removePortalUserPermissions } from "@budibase/backend-core/src/users" export default async (ctx: UserCtx, next: any) => { // try to get the appID from the request @@ -42,8 +44,7 @@ export default async (ctx: UserCtx, next: any) => { roleId = globalUser.roleId || roleId // Allow builders to specify their role via a header - const isBuilder = - globalUser && globalUser.builder && globalUser.builder.global + const isBuilder = users.isBuilder(globalUser, appId) const isDevApp = appId && isDevAppID(appId) const roleHeader = ctx.request && @@ -56,8 +57,7 @@ export default async (ctx: UserCtx, next: any) => { roleId = roleHeader // Delete admin and builder flags so that the specified role is honoured - delete ctx.user.builder - delete ctx.user.admin + ctx.user = users.removePortalUserPermissions(ctx.user) as ContextUser } } catch (error) { // Swallow error and do nothing @@ -81,9 +81,7 @@ export default async (ctx: UserCtx, next: any) => { !tenancy.isUserInAppTenant(requestAppId, ctx.user) ) { // don't error, simply remove the users rights (they are a public user) - delete ctx.user.builder - delete ctx.user.admin - delete ctx.user.roles + ctx.user = users.cleanseUserObject(ctx.user) as ContextUser ctx.isAuthenticated = false roleId = roles.BUILTIN_ROLE_IDS.PUBLIC skipCookie = true diff --git a/packages/server/src/migrations/functions/backfill/global/users.ts b/packages/server/src/migrations/functions/backfill/global/users.ts index 05c5f8f56e..9f536a97a5 100644 --- a/packages/server/src/migrations/functions/backfill/global/users.ts +++ b/packages/server/src/migrations/functions/backfill/global/users.ts @@ -1,4 +1,8 @@ -import { events, db as dbUtils } from "@budibase/backend-core" +import { + events, + db as dbUtils, + users as usersCore, +} from "@budibase/backend-core" import { User, CloudAccount } from "@budibase/types" import { DEFAULT_TIMESTAMP } from ".." @@ -30,11 +34,11 @@ export const backfill = async ( await events.identification.identifyUser(user, account, timestamp) await events.user.created(user, timestamp) - if (user.admin?.global) { + if (usersCore.hasAdminPermissions(user)) { await events.user.permissionAdminAssigned(user, timestamp) } - if (user.builder?.global) { + if (usersCore.hasBuilderPermissions(user)) { await events.user.permissionBuilderAssigned(user, timestamp) } diff --git a/packages/server/src/utilities/global.ts b/packages/server/src/utilities/global.ts index c9869b4920..63e33dafee 100644 --- a/packages/server/src/utilities/global.ts +++ b/packages/server/src/utilities/global.ts @@ -5,6 +5,7 @@ import { cache, tenancy, context, + users, } from "@budibase/backend-core" import env from "../environment" import { groups } from "@budibase/pro" @@ -22,8 +23,7 @@ export function updateAppRole( } // if in an multi-tenancy environment make sure roles are never updated if (env.MULTI_TENANCY && appId && !tenancy.isUserInAppTenant(appId, user)) { - delete user.builder - delete user.admin + user = users.removePortalUserPermissions(user) user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC return user } @@ -32,7 +32,7 @@ export function updateAppRole( user.roleId = user.roles[dbCore.getProdAppID(appId)] } // if a role wasn't found then either set as admin (builder) or public (everyone else) - if (!user.roleId && user.builder && user.builder.global) { + if (!user.roleId && users.isBuilder(user, appId)) { user.roleId = roles.BUILTIN_ROLE_IDS.ADMIN } else if (!user.roleId && !user?.userGroups?.length) { user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index cae3c0e6e2..e9e3ccc662 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -47,7 +47,6 @@ export interface User extends Document { } admin?: { global: boolean - apps?: string[] } password?: string status?: UserStatus diff --git a/packages/worker/src/sdk/users/events.ts b/packages/worker/src/sdk/users/events.ts index 7d86182a3c..d8af13a82f 100644 --- a/packages/worker/src/sdk/users/events.ts +++ b/packages/worker/src/sdk/users/events.ts @@ -1,15 +1,18 @@ import env from "../../environment" -import { events, accounts, tenancy } from "@budibase/backend-core" +import { events, accounts, tenancy, users } from "@budibase/backend-core" import { User, UserRoles, CloudAccount } from "@budibase/types" +const hasBuilderPerm = users.hasBuilderPermissions +const hasAdminPerm = users.hasAdminPermissions + export const handleDeleteEvents = async (user: any) => { await events.user.deleted(user) - if (isBuilder(user)) { + if (hasBuilderPerm(user)) { await events.user.permissionBuilderRemoved(user) } - if (isAdmin(user)) { + if (hasAdminPerm(user)) { await events.user.permissionAdminRemoved(user) } } @@ -103,23 +106,20 @@ export const handleSaveEvents = async ( await handleAppRoleEvents(user, existingUser) } -const isBuilder = (user: any) => user.builder && user.builder.global -const isAdmin = (user: any) => user.admin && user.admin.global - export const isAddingBuilder = (user: any, existingUser: any) => { - return isAddingPermission(user, existingUser, isBuilder) + return isAddingPermission(user, existingUser, hasBuilderPerm) } export const isRemovingBuilder = (user: any, existingUser: any) => { - return isRemovingPermission(user, existingUser, isBuilder) + return isRemovingPermission(user, existingUser, hasBuilderPerm) } const isAddingAdmin = (user: any, existingUser: any) => { - return isAddingPermission(user, existingUser, isAdmin) + return isAddingPermission(user, existingUser, hasAdminPerm) } const isRemovingAdmin = (user: any, existingUser: any) => { - return isRemovingPermission(user, existingUser, isAdmin) + return isRemovingPermission(user, existingUser, hasAdminPerm) } const isOnboardingComplete = (user: any, existingUser: any) => { diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index c3cb8500cb..cfa68932d2 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -252,9 +252,7 @@ export const save = async ( let builtUser = await buildUser(user, opts, tenantId, dbUser) // don't allow a user to update its own roles/perms if (opts.currentUserId && opts.currentUserId === dbUser?._id) { - builtUser.builder = dbUser.builder - builtUser.admin = dbUser.admin - builtUser.roles = dbUser.roles + builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User } if (!dbUser && roles?.length) { From 91847504c8bd4e79f278f5d5287c5c6f5deda088 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 18 Jul 2023 18:10:15 +0100 Subject: [PATCH 003/117] Adding test cases for admin/builder checking middlewares. --- .../backend-core/src/middleware/adminOnly.ts | 4 +- .../src/middleware/tests/builder.spec.ts | 141 ++++++++++++++++++ packages/backend-core/src/users.ts | 2 +- .../tests/core/utilities/structures/users.ts | 19 +++ packages/server/src/middleware/currentapp.ts | 1 - packages/types/src/documents/global/user.ts | 11 +- 6 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 packages/backend-core/src/middleware/tests/builder.spec.ts diff --git a/packages/backend-core/src/middleware/adminOnly.ts b/packages/backend-core/src/middleware/adminOnly.ts index dbe1e3a501..dc2fe9064e 100644 --- a/packages/backend-core/src/middleware/adminOnly.ts +++ b/packages/backend-core/src/middleware/adminOnly.ts @@ -1,6 +1,6 @@ -import { BBContext } from "@budibase/types" +import { UserCtx } from "@budibase/types" -export default async (ctx: BBContext, next: any) => { +export default async (ctx: UserCtx, next: any) => { if ( !ctx.internal && (!ctx.user || !ctx.user.admin || !ctx.user.admin.global) diff --git a/packages/backend-core/src/middleware/tests/builder.spec.ts b/packages/backend-core/src/middleware/tests/builder.spec.ts new file mode 100644 index 0000000000..68c72ffe8a --- /dev/null +++ b/packages/backend-core/src/middleware/tests/builder.spec.ts @@ -0,0 +1,141 @@ +import adminOnly from "../adminOnly" +import builderOnly from "../builderOnly" +import builderOrAdmin from "../builderOrAdmin" +import { structures } from "../../../tests" +import { ContextUser } from "@budibase/types" +import { doInAppContext } from "../../context" + +const appId = "app_aaa" +const basicUser = structures.users.user() +const adminUser = structures.users.adminUser() +const adminOnlyUser = structures.users.adminOnlyUser() +const builderUser = structures.users.builderUser() +const appBuilderUser = structures.users.appBuilderUser(appId) + +function buildUserCtx(user: ContextUser) { + return { + internal: false, + user, + throw: jest.fn(), + } as any +} + +function passed(throwFn: jest.Func, nextFn: jest.Func) { + expect(throwFn).not.toBeCalled() + expect(nextFn).toBeCalled() +} + +function threw(throwFn: jest.Func) { + // cant check next, the throw function doesn't actually throw - so it still continues + expect(throwFn).toBeCalled() +} + +describe("adminOnly middleware", () => { + it("should allow admin user", () => { + const ctx = buildUserCtx(adminUser), + next = jest.fn() + adminOnly(ctx, next) + passed(ctx.throw, next) + }) + + it("should not allow basic user", () => { + const ctx = buildUserCtx(basicUser), + next = jest.fn() + adminOnly(ctx, next) + threw(ctx.throw) + }) + + it("should not allow builder user", () => { + const ctx = buildUserCtx(builderUser), + next = jest.fn() + adminOnly(ctx, next) + threw(ctx.throw) + }) +}) + +describe("builderOnly middleware", () => { + it("should allow builder user", () => { + const ctx = buildUserCtx(builderUser), + next = jest.fn() + builderOnly(ctx, next) + passed(ctx.throw, next) + }) + + it("should allow app builder user", () => { + const ctx = buildUserCtx(appBuilderUser), + next = jest.fn() + doInAppContext(appId, () => { + builderOnly(ctx, next) + }) + passed(ctx.throw, next) + }) + + it("should allow admin and builder user", () => { + const ctx = buildUserCtx(adminUser), + next = jest.fn() + builderOnly(ctx, next) + passed(ctx.throw, next) + }) + + it("should not allow admin user", () => { + const ctx = buildUserCtx(adminOnlyUser), + next = jest.fn() + builderOnly(ctx, next) + threw(ctx.throw) + }) + + it("should not allow app builder user to different app", () => { + const ctx = buildUserCtx(appBuilderUser), + next = jest.fn() + doInAppContext("app_bbb", () => { + builderOnly(ctx, next) + }) + threw(ctx.throw) + }) + + it("should not allow basic user", () => { + const ctx = buildUserCtx(basicUser), + next = jest.fn() + builderOnly(ctx, next) + threw(ctx.throw) + }) +}) + +describe("builderOrAdmin middleware", () => { + it("should allow builder user", () => { + const ctx = buildUserCtx(builderUser), + next = jest.fn() + builderOrAdmin(ctx, next) + passed(ctx.throw, next) + }) + + it("should allow builder and admin user", () => { + const ctx = buildUserCtx(adminUser), + next = jest.fn() + builderOrAdmin(ctx, next) + passed(ctx.throw, next) + }) + + it("should allow admin user", () => { + const ctx = buildUserCtx(adminOnlyUser), + next = jest.fn() + builderOrAdmin(ctx, next) + passed(ctx.throw, next) + }) + + it("should allow app builder user", () => { + const ctx = buildUserCtx(appBuilderUser), + next = jest.fn() + doInAppContext(appId, () => { + builderOrAdmin(ctx, next) + }) + passed(ctx.throw, next) + }) + + it("should not allow basic user", () => { + const ctx = buildUserCtx(basicUser), + next = jest.fn() + builderOrAdmin(ctx, next) + threw(ctx.throw) + }) +}) diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index 2e6ede3cf3..71cd599f87 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -258,7 +258,7 @@ export async function getUserCount() { export function isBuilder(user: User | ContextUser, appId?: string) { if (user.builder?.global) { return true - } else if (appId && user.builder?.apps?.includes(appId)) { + } else if (appId && user.builder?.apps?.includes(getProdAppID(appId))) { return true } return false diff --git a/packages/backend-core/tests/core/utilities/structures/users.ts b/packages/backend-core/tests/core/utilities/structures/users.ts index 7a6b4f0d80..0a4f2e8b54 100644 --- a/packages/backend-core/tests/core/utilities/structures/users.ts +++ b/packages/backend-core/tests/core/utilities/structures/users.ts @@ -1,5 +1,6 @@ import { AdminUser, + AdminOnlyUser, BuilderUser, SSOAuthDetails, SSOUser, @@ -21,6 +22,15 @@ export const adminUser = (userProps?: any): AdminUser => { } } +export const adminOnlyUser = (userProps?: any): AdminOnlyUser => { + return { + ...user(userProps), + admin: { + global: true, + }, + } +} + export const builderUser = (userProps?: any): BuilderUser => { return { ...user(userProps), @@ -30,6 +40,15 @@ export const builderUser = (userProps?: any): BuilderUser => { } } +export const appBuilderUser = (appId: string, userProps?: any): BuilderUser => { + return { + ...user(userProps), + builder: { + apps: [appId], + }, + } +} + export function ssoUser( opts: { user?: any; details?: SSOAuthDetails } = {} ): SSOUser { diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts index 10ee13a67a..1f580d80e4 100644 --- a/packages/server/src/middleware/currentapp.ts +++ b/packages/server/src/middleware/currentapp.ts @@ -11,7 +11,6 @@ import { getCachedSelf } from "../utilities/global" import env from "../environment" import { isWebhookEndpoint } from "./utils" import { UserCtx, ContextUser } from "@budibase/types" -import { removePortalUserPermissions } from "@budibase/backend-core/src/users" export default async (ctx: UserCtx, next: any) => { // try to get the appID from the request diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index e9e3ccc662..2ce714801d 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -42,7 +42,7 @@ export interface User extends Document { forceResetPassword?: boolean roles: UserRoles builder?: { - global: boolean + global?: boolean apps?: string[] } admin?: { @@ -70,7 +70,8 @@ export interface UserRoles { export interface BuilderUser extends User { builder: { - global: boolean + global?: boolean + apps?: string[] } } @@ -83,6 +84,12 @@ export interface AdminUser extends User { } } +export interface AdminOnlyUser extends User { + admin: { + global: boolean + } +} + export function isUser(user: object): user is User { return !!(user as User).roles } From 85dea47a31d12b859ef4a864e17213337c2c5f47 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 19 Jul 2023 16:19:34 +0100 Subject: [PATCH 004/117] Moving user admin/builder functions to shared-core for frontend to use. --- packages/backend-core/package.json | 1 + packages/backend-core/src/constants/db.ts | 44 ++----------------- packages/backend-core/src/users.ts | 40 +++-------------- packages/server/src/middleware/currentapp.ts | 4 +- packages/shared-core/src/index.ts | 1 + .../src/sdk/documents/applications.ts | 29 ++++++++++++ .../shared-core/src/sdk/documents/index.ts | 2 + .../shared-core/src/sdk/documents/users.ts | 35 +++++++++++++++ packages/shared-core/src/sdk/index.ts | 1 + packages/types/src/documents/document.ts | 41 +++++++++++++++++ packages/worker/src/sdk/users/users.ts | 2 +- 11 files changed, 123 insertions(+), 77 deletions(-) create mode 100644 packages/shared-core/src/sdk/documents/applications.ts create mode 100644 packages/shared-core/src/sdk/documents/index.ts create mode 100644 packages/shared-core/src/sdk/documents/users.ts create mode 100644 packages/shared-core/src/sdk/index.ts diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 7f3c064c92..93f2529987 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -22,6 +22,7 @@ "dependencies": { "@budibase/nano": "10.1.2", "@budibase/pouchdb-replication-stream": "1.2.10", + "@budibase/shared-core": "0.0.0", "@budibase/types": "0.0.0", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", diff --git a/packages/backend-core/src/constants/db.ts b/packages/backend-core/src/constants/db.ts index 34dab6c088..83f8298f54 100644 --- a/packages/backend-core/src/constants/db.ts +++ b/packages/backend-core/src/constants/db.ts @@ -1,5 +1,5 @@ -export const SEPARATOR = "_" -export const UNICODE_MAX = "\ufff0" +import { prefixed, DocumentType } from "@budibase/types" +export { SEPARATOR, UNICODE_MAX, DocumentType } from "@budibase/types" /** * Can be used to create a few different forms of querying a view. @@ -34,42 +34,6 @@ export enum InternalTable { USER_METADATA = "ta_users", } -export enum DocumentType { - USER = "us", - GROUP = "gr", - WORKSPACE = "workspace", - CONFIG = "config", - TEMPLATE = "template", - APP = "app", - DEV = "dev", - APP_DEV = "app_dev", - APP_METADATA = "app_metadata", - ROLE = "role", - MIGRATIONS = "migrations", - DEV_INFO = "devinfo", - AUTOMATION_LOG = "log_au", - ACCOUNT_METADATA = "acc_metadata", - PLUGIN = "plg", - DATASOURCE = "datasource", - DATASOURCE_PLUS = "datasource_plus", - APP_BACKUP = "backup", - TABLE = "ta", - ROW = "ro", - AUTOMATION = "au", - LINK = "li", - WEBHOOK = "wh", - INSTANCE = "inst", - LAYOUT = "layout", - SCREEN = "screen", - QUERY = "query", - DEPLOYMENTS = "deployments", - METADATA = "metadata", - MEM_VIEW = "view", - USER_FLAG = "flag", - AUTOMATION_METADATA = "meta_au", - AUDIT_LOG = "al", -} - export const StaticDatabases = { GLOBAL: { name: "global-db", @@ -93,7 +57,7 @@ export const StaticDatabases = { }, } -export const APP_PREFIX = DocumentType.APP + SEPARATOR -export const APP_DEV = DocumentType.APP_DEV + SEPARATOR +export const APP_PREFIX = prefixed(DocumentType.APP) +export const APP_DEV = prefixed(DocumentType.APP_DEV) export const APP_DEV_PREFIX = APP_DEV export const BUDIBASE_DATASOURCE_TYPE = "budibase" diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index 71cd599f87..7224d827e8 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -18,6 +18,7 @@ import { User, ContextUser, } from "@budibase/types" +import { sdk } from "@budibase/shared-core" import { getGlobalDB } from "./context" import * as context from "./context" @@ -38,6 +39,12 @@ function removeUserPassword(users: User | User[]) { return users } +// extract from shared-core to make easily accessible from backend-core +export const isBuilder = sdk.users.isBuilder +export const isAdmin = sdk.users.isAdmin +export const hasAdminPermissions = sdk.users.hasAdminPermissions +export const hasBuilderPermissions = sdk.users.hasBuilderPermissions + export const bulkGetGlobalUsersById = async ( userIds: string[], opts?: GetOpts @@ -254,39 +261,6 @@ export async function getUserCount() { return response.total_rows } -// checks if a user is specifically a builder, given an app ID -export function isBuilder(user: User | ContextUser, appId?: string) { - if (user.builder?.global) { - return true - } else if (appId && user.builder?.apps?.includes(getProdAppID(appId))) { - return true - } - return false -} - -// alias for hasAdminPermission, currently do the same thing -// in future whether someone has admin permissions and whether they are -// an admin for a specific resource could be separated -export function isAdmin(user: User | ContextUser) { - return hasAdminPermissions(user) -} - -// checks if a user is capable of building any app -export function hasBuilderPermissions(user?: User | ContextUser) { - if (!user) { - return false - } - return user.builder?.global || user.builder?.apps?.length !== 0 -} - -// checks if a user is capable of being an admin -export function hasAdminPermissions(user?: User | ContextUser) { - if (!user) { - return false - } - return user.admin?.global -} - // used to remove the builder/admin permissions, for processing the // user as an app user (they may have some specific role/group export function removePortalUserPermissions(user: User | ContextUser) { diff --git a/packages/server/src/middleware/currentapp.ts b/packages/server/src/middleware/currentapp.ts index 1f580d80e4..800d43e69c 100644 --- a/packages/server/src/middleware/currentapp.ts +++ b/packages/server/src/middleware/currentapp.ts @@ -24,7 +24,7 @@ export default async (ctx: UserCtx, next: any) => { if ( isDevAppID(requestAppId) && !isWebhookEndpoint(ctx) && - (!ctx.user || !ctx.user.builder || !ctx.user.builder.global) + !users.isBuilder(ctx.user, requestAppId) ) { return ctx.redirect("/") } @@ -70,7 +70,6 @@ export default async (ctx: UserCtx, next: any) => { } return context.doInAppContext(appId, async () => { - let skipCookie = false // if the user not in the right tenant then make sure they have no permissions // need to judge this only based on the request app ID, if ( @@ -83,7 +82,6 @@ export default async (ctx: UserCtx, next: any) => { ctx.user = users.cleanseUserObject(ctx.user) as ContextUser ctx.isAuthenticated = false roleId = roles.BUILTIN_ROLE_IDS.PUBLIC - skipCookie = true } ctx.appId = appId diff --git a/packages/shared-core/src/index.ts b/packages/shared-core/src/index.ts index 21f2f2c639..4a47eda898 100644 --- a/packages/shared-core/src/index.ts +++ b/packages/shared-core/src/index.ts @@ -2,3 +2,4 @@ export * from "./constants" export * as dataFilters from "./filters" export * as helpers from "./helpers" export * as utils from "./utils" +export * as sdk from "./sdk" diff --git a/packages/shared-core/src/sdk/documents/applications.ts b/packages/shared-core/src/sdk/documents/applications.ts new file mode 100644 index 0000000000..05129f6e75 --- /dev/null +++ b/packages/shared-core/src/sdk/documents/applications.ts @@ -0,0 +1,29 @@ +import { DocumentType, prefixed } from "@budibase/types" + +const APP_PREFIX = prefixed(DocumentType.APP) +const APP_DEV_PREFIX = prefixed(DocumentType.APP_DEV) + +export function getDevAppID(appId: string) { + if (!appId || appId.startsWith(APP_DEV_PREFIX)) { + return appId + } + // split to take off the app_ element, then join it together incase any other app_ exist + const split = appId.split(APP_PREFIX) + split.shift() + const rest = split.join(APP_PREFIX) + return `${APP_DEV_PREFIX}${rest}` +} + +/** + * Convert a development app ID to a deployed app ID. + */ +export function getProdAppID(appId: string) { + if (!appId || !appId.startsWith(APP_DEV_PREFIX)) { + return appId + } + // split to take off the app_dev element, then join it together incase any other app_ exist + const split = appId.split(APP_DEV_PREFIX) + split.shift() + const rest = split.join(APP_DEV_PREFIX) + return `${APP_PREFIX}${rest}` +} diff --git a/packages/shared-core/src/sdk/documents/index.ts b/packages/shared-core/src/sdk/documents/index.ts new file mode 100644 index 0000000000..d20631eef4 --- /dev/null +++ b/packages/shared-core/src/sdk/documents/index.ts @@ -0,0 +1,2 @@ +export * as applications from "./applications" +export * as users from "./users" diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts new file mode 100644 index 0000000000..df3ef0025f --- /dev/null +++ b/packages/shared-core/src/sdk/documents/users.ts @@ -0,0 +1,35 @@ +import { ContextUser, User } from "@budibase/types" +import { getProdAppID } from "./applications" + +// checks if a user is specifically a builder, given an app ID +export function isBuilder(user: User | ContextUser, appId?: string) { + if (user.builder?.global) { + return true + } else if (appId && user.builder?.apps?.includes(getProdAppID(appId))) { + return true + } + return false +} + +// alias for hasAdminPermission, currently do the same thing +// in future whether someone has admin permissions and whether they are +// an admin for a specific resource could be separated +export function isAdmin(user: User | ContextUser) { + return hasAdminPermissions(user) +} + +// checks if a user is capable of building any app +export function hasBuilderPermissions(user?: User | ContextUser) { + if (!user) { + return false + } + return user.builder?.global || user.builder?.apps?.length !== 0 +} + +// checks if a user is capable of being an admin +export function hasAdminPermissions(user?: User | ContextUser) { + if (!user) { + return false + } + return user.admin?.global +} diff --git a/packages/shared-core/src/sdk/index.ts b/packages/shared-core/src/sdk/index.ts new file mode 100644 index 0000000000..87868e2723 --- /dev/null +++ b/packages/shared-core/src/sdk/index.ts @@ -0,0 +1 @@ +export * from "./documents" diff --git a/packages/types/src/documents/document.ts b/packages/types/src/documents/document.ts index ac05214b82..6ba318269b 100644 --- a/packages/types/src/documents/document.ts +++ b/packages/types/src/documents/document.ts @@ -1,3 +1,44 @@ +export const SEPARATOR = "_" +export const UNICODE_MAX = "\ufff0" + +export const prefixed = (type: DocumentType) => `${type}${SEPARATOR}` + +export enum DocumentType { + USER = "us", + GROUP = "gr", + WORKSPACE = "workspace", + CONFIG = "config", + TEMPLATE = "template", + APP = "app", + DEV = "dev", + APP_DEV = "app_dev", + APP_METADATA = "app_metadata", + ROLE = "role", + MIGRATIONS = "migrations", + DEV_INFO = "devinfo", + AUTOMATION_LOG = "log_au", + ACCOUNT_METADATA = "acc_metadata", + PLUGIN = "plg", + DATASOURCE = "datasource", + DATASOURCE_PLUS = "datasource_plus", + APP_BACKUP = "backup", + TABLE = "ta", + ROW = "ro", + AUTOMATION = "au", + LINK = "li", + WEBHOOK = "wh", + INSTANCE = "inst", + LAYOUT = "layout", + SCREEN = "screen", + QUERY = "query", + DEPLOYMENTS = "deployments", + METADATA = "metadata", + MEM_VIEW = "view", + USER_FLAG = "flag", + AUTOMATION_METADATA = "meta_au", + AUDIT_LOG = "al", +} + export interface Document { _id?: string _rev?: string diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index cfa68932d2..e0aa042995 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -176,7 +176,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => { export async function isPreventPasswordActions(user: User, account?: Account) { // when in maintenance mode we allow sso users with the admin role // to perform any password action - this prevents lockout - if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && user.admin?.global) { + if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && usersCore.isAdmin(user)) { return false } From e469abb6793cecd1170f7c8c129dc3955539e1f5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 19 Jul 2023 18:05:02 +0100 Subject: [PATCH 005/117] reworking frontend to use shared core functions to check if is admin or builder (needs further expansion). --- .../backend-core/src/middleware/adminOnly.ts | 6 +-- .../_components/BuilderSidePanel.svelte | 12 ++--- .../src/pages/builder/apps/index.svelte | 46 +++++++++---------- .../builder/src/pages/builder/index.svelte | 3 +- .../src/pages/builder/portal/_layout.svelte | 3 +- .../portal/users/users/[userId].svelte | 7 +-- .../_components/AppsTableRenderer.svelte | 3 +- .../users/_components/UpdateRolesModal.svelte | 3 +- packages/builder/src/stores/portal/auth.js | 9 ++-- packages/builder/src/stores/portal/users.js | 9 +++- .../shared-core/src/sdk/documents/users.ts | 4 ++ 11 files changed, 58 insertions(+), 47 deletions(-) diff --git a/packages/backend-core/src/middleware/adminOnly.ts b/packages/backend-core/src/middleware/adminOnly.ts index dc2fe9064e..6b2ee87c01 100644 --- a/packages/backend-core/src/middleware/adminOnly.ts +++ b/packages/backend-core/src/middleware/adminOnly.ts @@ -1,10 +1,8 @@ import { UserCtx } from "@budibase/types" +import { isAdmin } from "../users" export default async (ctx: UserCtx, next: any) => { - if ( - !ctx.internal && - (!ctx.user || !ctx.user.admin || !ctx.user.admin.global) - ) { + if (!ctx.internal && !isAdmin(ctx.user)) { ctx.throw(403, "Admin user only endpoint.") } return next() diff --git a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte index 1dd4453537..db56602463 100644 --- a/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte +++ b/packages/builder/src/pages/builder/app/[application]/_components/BuilderSidePanel.svelte @@ -12,12 +12,12 @@ } from "@budibase/bbui" import { store } from "builderStore" import { groups, licensing, apps, users, auth, admin } from "stores/portal" - import { fetchData } from "@budibase/frontend-core" + import { fetchData, Constants, Utils } from "@budibase/frontend-core" + import { sdk } from "@budibase/shared-core" import { API } from "api" import GroupIcon from "../../../portal/users/groups/_components/GroupIcon.svelte" import RoleSelect from "components/common/RoleSelect.svelte" import UpgradeModal from "components/common/users/UpgradeModal.svelte" - import { Constants, Utils } from "@budibase/frontend-core" import { emailValidator } from "helpers/validation" import { roles } from "stores/backend" import { fly } from "svelte/transition" @@ -108,7 +108,7 @@ await usersFetch.refresh() filteredUsers = $usersFetch.rows.map(user => { - const isBuilderOrAdmin = user.admin?.global || user.builder?.global + const isBuilderOrAdmin = sdk.users.isBuilderOrAdmin(user, prodAppId) let role = undefined if (isBuilderOrAdmin) { role = Constants.Roles.ADMIN @@ -258,7 +258,7 @@ } // Must exclude users who have explicit privileges const userByEmail = filteredUsers.reduce((acc, user) => { - if (user.role || user.admin?.global || user.builder?.global) { + if (user.role || sdk.users.isBuilderOrAdmin(user, prodAppId)) { acc.push(user.email) } return acc @@ -389,9 +389,9 @@ } const userTitle = user => { - if (user.admin?.global) { + if (sdk.users.isAdmin(user)) { return "Admin" - } else if (user.builder?.global) { + } else if (sdk.users.isBuilder(user, prodAppId)) { return "Developer" } else { return "App user" diff --git a/packages/builder/src/pages/builder/apps/index.svelte b/packages/builder/src/pages/builder/apps/index.svelte index ab75c50747..f6c5df17c9 100644 --- a/packages/builder/src/pages/builder/apps/index.svelte +++ b/packages/builder/src/pages/builder/apps/index.svelte @@ -22,7 +22,7 @@ import Spaceman from "assets/bb-space-man.svg" import Logo from "assets/bb-emblem.svg" import { UserAvatar } from "@budibase/frontend-core" - import { helpers } from "@budibase/shared-core" + import { helpers, sdk } from "@budibase/shared-core" let loaded = false let userInfoModal @@ -43,32 +43,30 @@ $: userGroups = $groups.filter(group => group.users.find(user => user._id === $auth.user?._id) ) - let userApps = [] $: publishedApps = $apps.filter(publishedAppsOnly) + $: userApps = getUserApps($auth.user) - $: { - if (!Object.keys($auth.user?.roles).length && $auth.user?.userGroups) { - userApps = - $auth.user?.builder?.global || $auth.user?.admin?.global - ? publishedApps - : publishedApps.filter(app => { - return userGroups.find(group => { - return groups.actions - .getGroupAppIds(group) - .map(role => apps.extractAppId(role)) - .includes(app.appId) - }) - }) - } else { - userApps = - $auth.user?.builder?.global || $auth.user?.admin?.global - ? publishedApps - : publishedApps.filter(app => - Object.keys($auth.user?.roles) - .map(x => apps.extractAppId(x)) - .includes(app.appId) - ) + function getUserApps(user) { + if (sdk.users.isAdmin(user)) { + return publishedApps } + return publishedApps.filter(app => { + if (sdk.users.isBuilder(user, app.appId)) { + return true + } + if (!Object.keys(user?.roles).length && user?.userGroups) { + return userGroups.find(group => { + return groups.actions + .getGroupAppIds(group) + .map(role => apps.extractAppId(role)) + .includes(app.appId) + }) + } else { + return Object.keys($auth.user?.roles) + .map(x => apps.extractAppId(x)) + .includes(app.appId) + } + }) } function getUrl(app) { diff --git a/packages/builder/src/pages/builder/index.svelte b/packages/builder/src/pages/builder/index.svelte index fcaa7fc55b..c6d9d3c1c3 100644 --- a/packages/builder/src/pages/builder/index.svelte +++ b/packages/builder/src/pages/builder/index.svelte @@ -1,11 +1,12 @@ diff --git a/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte b/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte index a9399fcca7..9ad41ad652 100644 --- a/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte +++ b/packages/builder/src/pages/builder/portal/users/users/_components/UpdateRolesModal.svelte @@ -2,6 +2,7 @@ import { createEventDispatcher } from "svelte" import { Body, Select, ModalContent, notifications } from "@budibase/bbui" import { users } from "stores/portal" + import { sdk } from "@budibase/shared-core" export let app export let user @@ -15,7 +16,7 @@ .filter(role => role._id !== "PUBLIC") .map(role => ({ value: role._id, label: role.name })) - if (!user?.builder?.global) { + if (!sdk.users.isBuilder(user, app?.appId)) { options.push({ value: NO_ACCESS, label: "No Access" }) } let selectedRole = user?.roles?.[app?._id] diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index ce64965af7..40113e2f76 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -2,6 +2,7 @@ import { derived, writable, get } from "svelte/store" import { API } from "api" import { admin } from "stores/portal" import analytics from "analytics" +import { sdk } from "@budibase/shared-core" export function createAuthStore() { const auth = writable({ @@ -17,8 +18,8 @@ export function createAuthStore() { let isBuilder = false if ($store.user) { const user = $store.user - isAdmin = !!user.admin?.global - isBuilder = !!user.builder?.global + isAdmin = sdk.users.isAdmin(user) + isBuilder = sdk.users.isBuilder(user) } return { user: $store.user, @@ -57,8 +58,8 @@ export function createAuthStore() { name: user.account?.name, user_id: user._id, tenant: user.tenantId, - admin: user?.admin?.global, - builder: user?.builder?.global, + admin: sdk.users.isAdmin(user), + builder: sdk.users.isBuilder(user), "Company size": user.account?.size, "Job role": user.account?.profession, }, diff --git a/packages/builder/src/stores/portal/users.js b/packages/builder/src/stores/portal/users.js index e522cd7958..992f6a5418 100644 --- a/packages/builder/src/stores/portal/users.js +++ b/packages/builder/src/stores/portal/users.js @@ -2,6 +2,7 @@ import { writable } from "svelte/store" import { API } from "api" import { update } from "lodash" import { licensing } from "." +import { sdk } from "@budibase/shared-core" export function createUsersStore() { const { subscribe, set } = writable({}) @@ -111,8 +112,12 @@ export function createUsersStore() { return await API.saveUser(user) } - const getUserRole = ({ admin, builder }) => - admin?.global ? "admin" : builder?.global ? "developer" : "appUser" + const getUserRole = user => + sdk.users.isAdmin(user) + ? "admin" + : sdk.users.isBuilder(user) + ? "developer" + : "appUser" const refreshUsage = fn => diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts index df3ef0025f..931f651a0e 100644 --- a/packages/shared-core/src/sdk/documents/users.ts +++ b/packages/shared-core/src/sdk/documents/users.ts @@ -18,6 +18,10 @@ export function isAdmin(user: User | ContextUser) { return hasAdminPermissions(user) } +export function isAdminOrBuilder(user: User | ContextUser, appId?: string) { + return isBuilder(user, appId) || isAdmin(user) +} + // checks if a user is capable of building any app export function hasBuilderPermissions(user?: User | ContextUser) { if (!user) { From 3abe5d4cb21dda1a9cba0f1764499ad96d87f817 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 20 Jul 2023 18:34:12 +0100 Subject: [PATCH 006/117] Frontend work to support logging in as an app builder - also making sure when a new app is created that the user is assigned app access to it. --- packages/backend-core/src/users.ts | 25 +++++++++ packages/builder/src/stores/portal/menu.js | 44 ++++++++------- .../server/src/api/controllers/application.ts | 37 ++++--------- packages/server/src/db/utils.ts | 8 +-- packages/server/src/middleware/authorized.ts | 11 ++-- .../src/sdk/app/applications/applications.ts | 54 +++++++++++++++++++ .../server/src/sdk/app/applications/index.ts | 2 + .../shared-core/src/sdk/documents/users.ts | 16 +++++- 8 files changed, 143 insertions(+), 54 deletions(-) create mode 100644 packages/server/src/sdk/app/applications/applications.ts diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users.ts index 7224d827e8..05abe70fe3 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users.ts @@ -2,6 +2,7 @@ import { directCouchFind, DocumentType, generateAppUserID, + getGlobalIDFromUserMetadataID, getGlobalUserParams, getProdAppID, getUsersByAppParams, @@ -21,6 +22,7 @@ import { import { sdk } from "@budibase/shared-core" import { getGlobalDB } from "./context" import * as context from "./context" +import { user as userCache } from "./cache" type GetOpts = { cleanup?: boolean } @@ -42,8 +44,10 @@ function removeUserPassword(users: User | User[]) { // extract from shared-core to make easily accessible from backend-core export const isBuilder = sdk.users.isBuilder export const isAdmin = sdk.users.isAdmin +export const isAdminOrBuilder = sdk.users.isAdminOrBuilder export const hasAdminPermissions = sdk.users.hasAdminPermissions export const hasBuilderPermissions = sdk.users.hasBuilderPermissions +export const hasAppBuilderPermissions = sdk.users.hasAppBuilderPermissions export const bulkGetGlobalUsersById = async ( userIds: string[], @@ -77,6 +81,27 @@ export const bulkUpdateGlobalUsers = async (users: User[]) => { return (await db.bulkDocs(users)) as BulkDocsResponse } +export const grantAppBuilderAccess = async (userId: string, appId: string) => { + const prodAppId = getProdAppID(appId) + const db = getGlobalDB() + const user = (await db.get(userId)) as User + if (!user.builder) { + user.builder = {} + } + if (!user.builder.apps) { + user.builder.apps = [] + } + if (!user.builder.apps.includes(prodAppId)) { + user.builder.apps.push(prodAppId) + } + try { + await db.put(user) + await userCache.invalidateUser(userId) + } catch (err: any) { + throw new Error(`Unable to grant user access: ${err.message}`) + } +} + export async function getById(id: string, opts?: GetOpts): Promise { const db = context.getGlobalDB() let user = await db.get(id) diff --git a/packages/builder/src/stores/portal/menu.js b/packages/builder/src/stores/portal/menu.js index c66c98d00f..2c17ce0b36 100644 --- a/packages/builder/src/stores/portal/menu.js +++ b/packages/builder/src/stores/portal/menu.js @@ -2,8 +2,12 @@ import { derived } from "svelte/store" import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags" import { admin } from "./admin" import { auth } from "./auth" +import { sdk } from "@budibase/shared-core" export const menu = derived([admin, auth], ([$admin, $auth]) => { + const user = $auth?.user + const isAdmin = sdk.users.isAdmin(user) + const cloud = $admin?.cloud // Determine user sub pages let userSubPages = [ { @@ -24,19 +28,24 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => { title: "Apps", href: "/builder/portal/apps", }, - { + ] + if ( + sdk.users.hasBuilderPermissions(user) && + !sdk.users.hasAppBuilderPermissions(user) + ) { + menu.push({ title: "Users", href: "/builder/portal/users", subPages: userSubPages, - }, - { - title: "Plugins", - href: "/builder/portal/plugins", - }, - ] + }) + } + menu.push({ + title: "Plugins", + href: "/builder/portal/plugins", + }) // Add settings page for admins - if ($auth.isAdmin) { + if (isAdmin) { let settingsSubPages = [ { title: "Auth", @@ -59,7 +68,7 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => { href: "/builder/portal/settings/environment", }, ] - if (!$admin.cloud) { + if (!cloud) { settingsSubPages.push({ title: "Version", href: "/builder/portal/settings/version", @@ -84,38 +93,35 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => { href: "/builder/portal/account/usage", }, ] - if ($auth.isAdmin) { + if (isAdmin) { accountSubPages.push({ title: "Audit Logs", href: "/builder/portal/account/auditLogs", }) - if (!$admin.cloud) { + if (!cloud) { accountSubPages.push({ title: "System Logs", href: "/builder/portal/account/systemLogs", }) } } - if ($admin.cloud && $auth?.user?.accountPortalAccess) { + if (cloud && user?.accountPortalAccess) { accountSubPages.push({ title: "Upgrade", - href: $admin.accountPortalUrl + "/portal/upgrade", + href: $admin?.accountPortalUrl + "/portal/upgrade", }) - } else if (!$admin.cloud && $auth.isAdmin) { + } else if (!cloud && isAdmin) { accountSubPages.push({ title: "Upgrade", href: "/builder/portal/account/upgrade", }) } // add license check here - if ( - $auth?.user?.accountPortalAccess && - $auth.user.account.stripeCustomerId - ) { + if (user?.accountPortalAccess && user.account.stripeCustomerId) { accountSubPages.push({ title: "Billing", - href: $admin.accountPortalUrl + "/portal/billing", + href: $admin?.accountPortalUrl + "/portal/billing", }) } menu.push({ diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 6a0088d4dc..9538790827 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -50,12 +50,13 @@ import { MigrationType, PlanType, Screen, - SocketSession, UserCtx, + ContextUser, } from "@budibase/types" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import sdk from "../../sdk" import { builderSocket } from "../../websockets" +import { grantAppBuilderAccess } from "@budibase/backend-core/src/users" // utility function, need to do away with this async function getLayouts() { @@ -178,32 +179,10 @@ export const addSampleData = async (ctx: UserCtx) => { } export async function fetch(ctx: UserCtx) { - const dev = ctx.query && ctx.query.status === AppStatus.DEV - const all = ctx.query && ctx.query.status === AppStatus.ALL - const apps = (await dbCore.getAllApps({ dev, all })) as App[] - - const appIds = apps - .filter((app: any) => app.status === "development") - .map((app: any) => app.appId) - - // get the locks for all the dev apps - if (dev || all) { - const locks = await getLocksById(appIds) - for (let app of apps) { - const lock = locks[app.appId] - if (lock) { - app.lockedBy = lock - } else { - // make sure its definitely not present - delete app.lockedBy - } - } - } - - // Enrich apps with all builder user sessions - const enrichedApps = await sdk.users.sessions.enrichApps(apps) - - ctx.body = await checkAppMetadata(enrichedApps) + ctx.body = await sdk.applications.fetch( + ctx.query.status as AppStatus, + ctx.user + ) } export async function fetchAppDefinition(ctx: UserCtx) { @@ -395,6 +374,10 @@ async function appPostCreate(ctx: UserCtx, app: App) { tenantId, appId: app.appId, }) + // they are an app builder, creating a new app, make sure they can access it + if (users.hasAppBuilderPermissions(ctx.user)) { + await users.grantAppBuilderAccess(ctx.user._id!, app.appId) + } await creationEvents(ctx.request, app) // app import & template creation if (ctx.request.body.useTemplate === "true") { diff --git a/packages/server/src/db/utils.ts b/packages/server/src/db/utils.ts index e08392c3a1..eb5cbc27ef 100644 --- a/packages/server/src/db/utils.ts +++ b/packages/server/src/db/utils.ts @@ -3,10 +3,10 @@ import { db as dbCore } from "@budibase/backend-core" type Optional = string | null -export const AppStatus = { - DEV: "development", - ALL: "all", - DEPLOYED: "published", +export enum AppStatus { + DEV = "development", + ALL = "all", + DEPLOYED = "published", } export const BudibaseInternalDB = { diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index 81e42604bc..dba5d47cb9 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -5,7 +5,7 @@ import { context, users, } from "@budibase/backend-core" -import { Role } from "@budibase/types" +import { Role, UserCtx } from "@budibase/types" import builderMiddleware from "./builder" import { isWebhookEndpoint } from "./utils" @@ -22,14 +22,19 @@ const csrf = auth.buildCsrfMiddleware() * - Otherwise the user must have the required role. */ const checkAuthorized = async ( - ctx: any, + ctx: UserCtx, resourceRoles: any, permType: any, permLevel: any ) => { const appId = context.getAppId() // check if this is a builder api and the user is not a builder - const isBuilder = users.isBuilder(ctx.user, appId) + let isBuilder + if (!appId) { + isBuilder = users.hasBuilderPermissions(ctx.user) + } else { + isBuilder = users.isBuilder(ctx.user, appId) + } const isBuilderApi = permType === permissions.PermissionType.BUILDER if (isBuilderApi && !isBuilder) { return ctx.throw(403, "Not Authorized") diff --git a/packages/server/src/sdk/app/applications/applications.ts b/packages/server/src/sdk/app/applications/applications.ts new file mode 100644 index 0000000000..73cacb8983 --- /dev/null +++ b/packages/server/src/sdk/app/applications/applications.ts @@ -0,0 +1,54 @@ +import { AppStatus } from "../../../db/utils" +import { App, ContextUser } from "@budibase/types" +import { getLocksById } from "../../../utilities/redis" +import { enrichApps } from "../../users/sessions" +import { checkAppMetadata } from "../../../automations/logging" +import { db as dbCore, users } from "@budibase/backend-core" + +export function filterAppList(user: ContextUser, apps: App[]) { + let appList: string[] = [] + if (users.hasAppBuilderPermissions(user)) { + appList = user.builder?.apps! + } else if (!users.isAdminOrBuilder(user)) { + appList = Object.keys(user.roles || {}) + } else { + return apps + } + const finalApps: App[] = [] + for (let app of apps) { + if (appList.includes(dbCore.getProdAppID(app.appId))) { + finalApps.push(app) + } + } + return finalApps +} + +export async function fetch(status: AppStatus, user: ContextUser) { + const dev = status === AppStatus.DEV + const all = status === AppStatus.ALL + let apps = (await dbCore.getAllApps({ dev, all })) as App[] + apps = filterAppList(user, apps) + + const appIds = apps + .filter((app: any) => app.status === "development") + .map((app: any) => app.appId) + + // get the locks for all the dev apps + if (dev || all) { + const locks = await getLocksById(appIds) + for (let app of apps) { + const lock = locks[app.appId] + if (lock) { + app.lockedBy = lock + } else { + // make sure its definitely not present + delete app.lockedBy + } + } + } + + // Enrich apps with all builder user sessions + const enrichedApps = await enrichApps(apps) + + return await checkAppMetadata(enrichedApps) +} diff --git a/packages/server/src/sdk/app/applications/index.ts b/packages/server/src/sdk/app/applications/index.ts index d917225e52..963d065ce2 100644 --- a/packages/server/src/sdk/app/applications/index.ts +++ b/packages/server/src/sdk/app/applications/index.ts @@ -1,7 +1,9 @@ import * as sync from "./sync" import * as utils from "./utils" +import * as applications from "./applications" export default { ...sync, ...utils, + ...applications, } diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts index 931f651a0e..1a9314f731 100644 --- a/packages/shared-core/src/sdk/documents/users.ts +++ b/packages/shared-core/src/sdk/documents/users.ts @@ -3,6 +3,9 @@ import { getProdAppID } from "./applications" // checks if a user is specifically a builder, given an app ID export function isBuilder(user: User | ContextUser, appId?: string) { + if (!user) { + return false + } if (user.builder?.global) { return true } else if (appId && user.builder?.apps?.includes(getProdAppID(appId))) { @@ -15,6 +18,9 @@ export function isBuilder(user: User | ContextUser, appId?: string) { // in future whether someone has admin permissions and whether they are // an admin for a specific resource could be separated export function isAdmin(user: User | ContextUser) { + if (!user) { + return false + } return hasAdminPermissions(user) } @@ -22,12 +28,20 @@ export function isAdminOrBuilder(user: User | ContextUser, appId?: string) { return isBuilder(user, appId) || isAdmin(user) } +// check if they are a builder within an app (not necessarily a global builder) +export function hasAppBuilderPermissions(user?: User | ContextUser) { + if (!user) { + return false + } + return !user.builder?.global && user.builder?.apps?.length !== 0 +} + // checks if a user is capable of building any app export function hasBuilderPermissions(user?: User | ContextUser) { if (!user) { return false } - return user.builder?.global || user.builder?.apps?.length !== 0 + return user.builder?.global || hasAppBuilderPermissions(user) } // checks if a user is capable of being an admin From d9c8e26f65467869c35317d31a0153d2cbdf9807 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 24 Jul 2023 18:29:46 +0100 Subject: [PATCH 007/117] Re-writing APIs based on most recent discussion about RBAC and per app builders. --- packages/types/src/api/web/user.ts | 7 --- packages/types/src/documents/global/user.ts | 1 + .../src/api/controllers/global/users.ts | 60 +++++++++++++++++-- .../worker/src/api/routes/global/users.ts | 11 +++- 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/packages/types/src/api/web/user.ts b/packages/types/src/api/web/user.ts index 4a27f781af..619362805a 100644 --- a/packages/types/src/api/web/user.ts +++ b/packages/types/src/api/web/user.ts @@ -85,10 +85,3 @@ export interface AcceptUserInviteResponse { export interface SyncUserRequest { previousUser?: User } - -export interface AddAppBuilderRequest { - userId: string - appId: string -} - -export interface RemoveAppBuilderRequest {} diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 2ce714801d..3249660624 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -43,6 +43,7 @@ export interface User extends Document { roles: UserRoles builder?: { global?: boolean + appBuilder?: boolean apps?: string[] } admin?: { diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 38406dc239..6862e44b05 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -8,8 +8,6 @@ import env from "../../../environment" import { AcceptUserInviteRequest, AcceptUserInviteResponse, - AddAppBuilderRequest, - RemoveAppBuilderRequest, BulkUserRequest, BulkUserResponse, CloudAccount, @@ -32,6 +30,7 @@ import { tenancy, platform, ErrorCode, + db as dbCore, } from "@budibase/backend-core" import { checkAnyUserExists } from "../../../utilities/users" import { isEmailConfigured } from "../../../utilities/email" @@ -434,8 +433,57 @@ export const inviteAccept = async ( } } -export const addAppBuilder = async (ctx: Ctx) => {} +export const grantAppBuilder = async (ctx: Ctx) => { + const { userId } = ctx.params + const user = await userSdk.getUser(userId) + if (!user.builder) { + user.builder = {} + } + user.builder.appBuilder = true + await userSdk.save(user, { hashPassword: false }) + ctx.body = { message: `User "${user.email}" granted app builder permissions` } +} -export const removeAppBuilder = async ( - ctx: Ctx -) => {} +export const addAppBuilder = async (ctx: Ctx) => { + const { userId, appId } = ctx.params + const user = await userSdk.getUser(userId) + if (!user.builder?.global || user.admin?.global) { + ctx.body = { message: "User already admin - no permissions updated." } + return + } + if (!user.builder?.appBuilder) { + ctx.throw( + 400, + "Unable to update access, user must be granted app builder permissions." + ) + } + const prodAppId = dbCore.getProdAppID(appId) + if (!user.builder.apps) { + user.builder.apps = [] + } + user.builder.apps.push(prodAppId) + await userSdk.save(user, { hashPassword: false }) + ctx.body = { message: `User "${user.email}" app builder access updated.` } +} + +export const removeAppBuilder = async (ctx: Ctx) => { + const { userId, appId } = ctx.params + const user = await userSdk.getUser(userId) + if (!user.builder?.global || user.admin?.global) { + ctx.body = { message: "User already admin - no permissions removed." } + return + } + if (!user.builder?.appBuilder) { + ctx.throw( + 400, + "Unable to update access, user must be granted app builder permissions." + ) + } + const prodAppId = dbCore.getProdAppID(appId) + const indexOf = user.builder?.apps?.indexOf(prodAppId) + if (indexOf && indexOf !== -1) { + user.builder.apps = user.builder.apps!.splice(indexOf, 1) + } + await userSdk.save(user, { hashPassword: false }) + ctx.body = { message: `User "${user.email}" app builder access removed.` } +} diff --git a/packages/worker/src/api/routes/global/users.ts b/packages/worker/src/api/routes/global/users.ts index 557065e9a4..9c1b5d9acb 100644 --- a/packages/worker/src/api/routes/global/users.ts +++ b/packages/worker/src/api/routes/global/users.ts @@ -122,6 +122,15 @@ router buildAdminInitValidation(), controller.adminUser ) + .post("/api/global/users/:userId/app/builder", controller.grantAppBuilder) + .patch( + "/api/global/users/:userId/app/:appId/builder", + controller.addAppBuilder + ) + .delete( + "/api/global/users/:userId/app/:appId/builder", + controller.removeAppBuilder + ) .get("/api/global/users/tenant/:id", controller.tenantUserLookup) // global endpoint but needs to come at end (blocks other endpoints otherwise) .get("/api/global/users/:id", auth.builderOrAdmin, controller.find) @@ -132,7 +141,5 @@ router users.buildUserSaveValidation(), selfController.updateSelf ) - .post("/api/global/users/builder", controller.addAppBuilder) - .delete("/api/global/users/builder", controller.removeAppBuilder) export default router From 90371b9d69b232459025ec997fd6e20ce4a34201 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 25 Jul 2023 17:48:57 +0100 Subject: [PATCH 008/117] Refactoring users core to move into backend, allowing app builder endpoints to move into pro. --- packages/backend-core/src/users/db.ts | 415 ++++++++++++ .../sdk => backend-core/src}/users/events.ts | 24 +- packages/backend-core/src/users/index.ts | 4 + packages/backend-core/src/users/lookup.ts | 102 +++ .../backend-core/src/{ => users}/users.ts | 18 +- packages/backend-core/src/users/utils.ts | 85 +++ .../src/api/controllers/global/roles.ts | 12 +- .../src/api/controllers/global/users.ts | 38 +- .../routes/global/tests/appBuilder.spec.ts | 52 ++ .../src/api/routes/global/tests/users.spec.ts | 2 +- packages/worker/src/sdk/users/index.ts | 2 + .../worker/src/sdk/users/tests/users.spec.ts | 17 +- packages/worker/src/sdk/users/users.ts | 591 +----------------- .../worker/src/tests/TestConfiguration.ts | 2 +- packages/worker/src/tests/api/users.ts | 24 + 15 files changed, 740 insertions(+), 648 deletions(-) create mode 100644 packages/backend-core/src/users/db.ts rename packages/{worker/src/sdk => backend-core/src}/users/events.ts (87%) create mode 100644 packages/backend-core/src/users/index.ts create mode 100644 packages/backend-core/src/users/lookup.ts rename packages/backend-core/src/{ => users}/users.ts (91%) create mode 100644 packages/backend-core/src/users/utils.ts create mode 100644 packages/worker/src/api/routes/global/tests/appBuilder.spec.ts diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts new file mode 100644 index 0000000000..cb1129d079 --- /dev/null +++ b/packages/backend-core/src/users/db.ts @@ -0,0 +1,415 @@ +import env from "../environment" +import * as eventHelpers from "./events" +import * as accounts from "../accounts" +import * as cache from "../cache" +import { UserStatus, ViewName } from "../constants" +import { getIdentity, getTenantId, getGlobalDB } from "../context" +import * as dbUtils from "../db" +import { EmailUnavailableError, HTTPError } from "../errors" +import * as platform from "../platform" +import * as sessions from "../security/sessions" +import * as utils from "../utils" +import * as usersCore from "./users" +import { + Account, + AllDocsResponse, + BulkUserCreated, + BulkUserDeleted, + PlatformUser, + RowResponse, + SaveUserOpts, + User, +} from "@budibase/types" +import * as pro from "@budibase/pro" +import * as accountSdk from "../accounts" +import { + isPreventPasswordActions, + validateUniqueUser, + getAccountHolderFromUserIds, +} from "./utils" +import { searchExistingEmails } from "./lookup" + +export async function allUsers() { + const db = getGlobalDB() + const response = await db.allDocs( + dbUtils.getGlobalUserParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) +} + +export async function countUsersByApp(appId: string) { + let response: any = await usersCore.searchGlobalUsersByApp(appId, {}) + return { + userCount: response.length, + } +} + +export async function getUsersByAppAccess(appId?: string) { + const opts: any = { + include_docs: true, + limit: 50, + } + let response: User[] = await usersCore.searchGlobalUsersByAppAccess( + appId, + opts + ) + return response +} + +export async function getUserByEmail(email: string) { + return usersCore.getGlobalUserByEmail(email) +} + +/** + * Gets a user by ID from the global database, based on the current tenancy. + */ +export async function getUser(userId: string) { + const user = await usersCore.getById(userId) + if (user) { + delete user.password + } + return user +} + +export async function buildUser( + user: User, + opts: SaveUserOpts = { + hashPassword: true, + requirePassword: true, + }, + tenantId: string, + dbUser?: any, + account?: Account +): Promise { + let { password, _id } = user + + // don't require a password if the db user doesn't already have one + if (dbUser && !dbUser.password) { + opts.requirePassword = false + } + + let hashedPassword + if (password) { + if (await isPreventPasswordActions(user, account)) { + throw new HTTPError("Password change is disabled for this user", 400) + } + hashedPassword = opts.hashPassword ? await utils.hash(password) : password + } else if (dbUser) { + hashedPassword = dbUser.password + } + + // passwords are never required if sso is enforced + const requirePasswords = + opts.requirePassword && !(await pro.features.isSSOEnforced()) + if (!hashedPassword && requirePasswords) { + throw "Password must be specified." + } + + _id = _id || dbUtils.generateGlobalUserID() + + const fullUser = { + createdAt: Date.now(), + ...dbUser, + ...user, + _id, + password: hashedPassword, + tenantId, + } + // make sure the roles object is always present + if (!fullUser.roles) { + fullUser.roles = {} + } + // add the active status to a user if its not provided + if (fullUser.status == null) { + fullUser.status = UserStatus.ACTIVE + } + + return fullUser +} + +export const save = async ( + user: User, + opts: SaveUserOpts = {} +): Promise => { + // default booleans to true + if (opts.hashPassword == null) { + opts.hashPassword = true + } + if (opts.requirePassword == null) { + opts.requirePassword = true + } + const tenantId = getTenantId() + const db = getGlobalDB() + + let { email, _id, userGroups = [], roles } = user + + if (!email && !_id) { + throw new Error("_id or email is required") + } + + let dbUser: User | undefined + if (_id) { + // try to get existing user from db + try { + dbUser = (await db.get(_id)) as User + if (email && dbUser.email !== email) { + throw "Email address cannot be changed" + } + email = dbUser.email + } catch (e: any) { + if (e.status === 404) { + // do nothing, save this new user with the id specified - required for SSO auth + } else { + throw e + } + } + } + + if (!dbUser && email) { + // no id was specified - load from email instead + dbUser = await usersCore.getGlobalUserByEmail(email) + if (dbUser && dbUser._id !== _id) { + throw new EmailUnavailableError(email) + } + } + + const change = dbUser ? 0 : 1 // no change if there is existing user + return pro.quotas.addUsers(change, async () => { + await validateUniqueUser(email, tenantId) + + let builtUser = await buildUser(user, opts, tenantId, dbUser) + // don't allow a user to update its own roles/perms + if (opts.currentUserId && opts.currentUserId === dbUser?._id) { + builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User + } + + if (!dbUser && roles?.length) { + builtUser.roles = { ...roles } + } + + // make sure we set the _id field for a new user + // Also if this is a new user, associate groups with them + let groupPromises = [] + if (!_id) { + _id = builtUser._id! + + if (userGroups.length > 0) { + for (let groupId of userGroups) { + groupPromises.push(pro.groups.addUsers(groupId, [_id])) + } + } + } + + try { + // save the user to db + let response = await db.put(builtUser) + builtUser._rev = response.rev + + await eventHelpers.handleSaveEvents(builtUser, dbUser) + await platform.users.addUser(tenantId, builtUser._id!, builtUser.email) + await cache.user.invalidateUser(response.id) + + await Promise.all(groupPromises) + + // finally returned the saved user from the db + return db.get(builtUser._id!) + } catch (err: any) { + if (err.status === 409) { + throw "User exists already" + } else { + throw err + } + } + }) +} + +export const bulkCreate = async ( + newUsersRequested: User[], + groups: string[] +): Promise => { + const tenantId = getTenantId() + + let usersToSave: any[] = [] + let newUsers: any[] = [] + + const emails = newUsersRequested.map((user: User) => user.email) + const existingEmails = await searchExistingEmails(emails) + const unsuccessful: { email: string; reason: string }[] = [] + + for (const newUser of newUsersRequested) { + if ( + newUsers.find( + (x: User) => x.email.toLowerCase() === newUser.email.toLowerCase() + ) || + existingEmails.includes(newUser.email.toLowerCase()) + ) { + unsuccessful.push({ + email: newUser.email, + reason: `Unavailable`, + }) + continue + } + newUser.userGroups = groups + newUsers.push(newUser) + } + + const account = await accountSdk.getAccountByTenantId(tenantId) + return pro.quotas.addUsers(newUsers.length, async () => { + // create the promises array that will be called by bulkDocs + newUsers.forEach((user: any) => { + usersToSave.push( + buildUser( + user, + { + hashPassword: true, + requirePassword: user.requirePassword, + }, + tenantId, + undefined, // no dbUser + account + ) + ) + }) + + const usersToBulkSave = await Promise.all(usersToSave) + await usersCore.bulkUpdateGlobalUsers(usersToBulkSave) + + // Post-processing of bulk added users, e.g. events and cache operations + for (const user of usersToBulkSave) { + // TODO: Refactor to bulk insert users into the info db + // instead of relying on looping tenant creation + await platform.users.addUser(tenantId, user._id, user.email) + await eventHelpers.handleSaveEvents(user, undefined) + } + + const saved = usersToBulkSave.map(user => { + return { + _id: user._id, + email: user.email, + } + }) + + // now update the groups + if (Array.isArray(saved) && groups) { + const groupPromises = [] + const createdUserIds = saved.map(user => user._id) + for (let groupId of groups) { + groupPromises.push(pro.groups.addUsers(groupId, createdUserIds)) + } + await Promise.all(groupPromises) + } + + return { + successful: saved, + unsuccessful, + } + }) +} + +export const bulkDelete = async ( + userIds: string[] +): Promise => { + const db = getGlobalDB() + + const response: BulkUserDeleted = { + successful: [], + unsuccessful: [], + } + + // remove the account holder from the delete request if present + const account = await getAccountHolderFromUserIds(userIds) + if (account) { + userIds = userIds.filter(u => u !== account.budibaseUserId) + // mark user as unsuccessful + response.unsuccessful.push({ + _id: account.budibaseUserId, + email: account.email, + reason: "Account holder cannot be deleted", + }) + } + + // Get users and delete + const allDocsResponse: AllDocsResponse = await db.allDocs({ + include_docs: true, + keys: userIds, + }) + const usersToDelete: User[] = allDocsResponse.rows.map( + (user: RowResponse) => { + return user.doc + } + ) + + // Delete from DB + const toDelete = usersToDelete.map(user => ({ + ...user, + _deleted: true, + })) + const dbResponse = await usersCore.bulkUpdateGlobalUsers(toDelete) + + await pro.quotas.removeUsers(toDelete.length) + for (let user of usersToDelete) { + await bulkDeleteProcessing(user) + } + + // Build Response + // index users by id + const userIndex: { [key: string]: User } = {} + usersToDelete.reduce((prev, current) => { + prev[current._id!] = current + return prev + }, userIndex) + + // add the successful and unsuccessful users to response + dbResponse.forEach(item => { + const email = userIndex[item.id].email + if (item.ok) { + response.successful.push({ _id: item.id, email }) + } else { + response.unsuccessful.push({ + _id: item.id, + email, + reason: "Database error", + }) + } + }) + + return response +} + +export const destroy = async (id: string) => { + const db = getGlobalDB() + const dbUser = (await db.get(id)) as User + const userId = dbUser._id as string + + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + // root account holder can't be deleted from inside budibase + const email = dbUser.email + const account = await accounts.getAccount(email) + if (account) { + if (dbUser.userId === getIdentity()!._id) { + throw new HTTPError('Please visit "Account" to delete this user', 400) + } else { + throw new HTTPError("Account holder cannot be deleted", 400) + } + } + } + + await platform.users.removeUser(dbUser) + + await db.remove(userId, dbUser._rev) + + await pro.quotas.removeUsers(1) + await eventHelpers.handleDeleteEvents(dbUser) + await cache.user.invalidateUser(userId) + await sessions.invalidateSessions(userId, { reason: "deletion" }) +} + +const bulkDeleteProcessing = async (dbUser: User) => { + const userId = dbUser._id as string + await platform.users.removeUser(dbUser) + await eventHelpers.handleDeleteEvents(dbUser) + await cache.user.invalidateUser(userId) + await sessions.invalidateSessions(userId, { reason: "bulk-deletion" }) +} diff --git a/packages/worker/src/sdk/users/events.ts b/packages/backend-core/src/users/events.ts similarity index 87% rename from packages/worker/src/sdk/users/events.ts rename to packages/backend-core/src/users/events.ts index d8af13a82f..f170c9ffe9 100644 --- a/packages/worker/src/sdk/users/events.ts +++ b/packages/backend-core/src/users/events.ts @@ -1,18 +1,18 @@ -import env from "../../environment" -import { events, accounts, tenancy, users } from "@budibase/backend-core" +import env from "../environment" +import * as events from "../events" +import * as accounts from "../accounts" +import { getTenantId } from "../context" import { User, UserRoles, CloudAccount } from "@budibase/types" - -const hasBuilderPerm = users.hasBuilderPermissions -const hasAdminPerm = users.hasAdminPermissions +import { hasBuilderPermissions, hasAdminPermissions } from "./utils" export const handleDeleteEvents = async (user: any) => { await events.user.deleted(user) - if (hasBuilderPerm(user)) { + if (hasBuilderPermissions(user)) { await events.user.permissionBuilderRemoved(user) } - if (hasAdminPerm(user)) { + if (hasAdminPermissions(user)) { await events.user.permissionAdminRemoved(user) } } @@ -58,7 +58,7 @@ export const handleSaveEvents = async ( user: User, existingUser: User | undefined ) => { - const tenantId = tenancy.getTenantId() + const tenantId = getTenantId() let tenantAccount: CloudAccount | undefined if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { tenantAccount = await accounts.getAccountByTenantId(tenantId) @@ -107,19 +107,19 @@ export const handleSaveEvents = async ( } export const isAddingBuilder = (user: any, existingUser: any) => { - return isAddingPermission(user, existingUser, hasBuilderPerm) + return isAddingPermission(user, existingUser, hasBuilderPermissions) } export const isRemovingBuilder = (user: any, existingUser: any) => { - return isRemovingPermission(user, existingUser, hasBuilderPerm) + return isRemovingPermission(user, existingUser, hasBuilderPermissions) } const isAddingAdmin = (user: any, existingUser: any) => { - return isAddingPermission(user, existingUser, hasAdminPerm) + return isAddingPermission(user, existingUser, hasAdminPermissions) } const isRemovingAdmin = (user: any, existingUser: any) => { - return isRemovingPermission(user, existingUser, hasAdminPerm) + return isRemovingPermission(user, existingUser, hasAdminPermissions) } const isOnboardingComplete = (user: any, existingUser: any) => { diff --git a/packages/backend-core/src/users/index.ts b/packages/backend-core/src/users/index.ts new file mode 100644 index 0000000000..2e5e2f948c --- /dev/null +++ b/packages/backend-core/src/users/index.ts @@ -0,0 +1,4 @@ +export * from "./users" +export * from "./utils" +export * from "./lookup" +export * as db from "./db" diff --git a/packages/backend-core/src/users/lookup.ts b/packages/backend-core/src/users/lookup.ts new file mode 100644 index 0000000000..17d0e91d88 --- /dev/null +++ b/packages/backend-core/src/users/lookup.ts @@ -0,0 +1,102 @@ +import { + AccountMetadata, + PlatformUser, + PlatformUserByEmail, + User, +} from "@budibase/types" +import * as dbUtils from "../db" +import { ViewName } from "../constants" + +/** + * Apply a system-wide search on emails: + * - in tenant + * - cross tenant + * - accounts + * return an array of emails that match the supplied emails. + */ +export async function searchExistingEmails(emails: string[]) { + let matchedEmails: string[] = [] + + const existingTenantUsers = await getExistingTenantUsers(emails) + matchedEmails.push(...existingTenantUsers.map(user => user.email)) + + const existingPlatformUsers = await getExistingPlatformUsers(emails) + matchedEmails.push(...existingPlatformUsers.map(user => user._id!)) + + const existingAccounts = await getExistingAccounts(emails) + matchedEmails.push(...existingAccounts.map(account => account.email)) + + return [...new Set(matchedEmails.map(email => email.toLowerCase()))] +} + +// lookup, could be email or userId, either will return a doc +export async function getPlatformUser( + identifier: string +): Promise { + // use the view here and allow to find anyone regardless of casing + // Use lowercase to ensure email login is case insensitive + return (await dbUtils.queryPlatformView(ViewName.PLATFORM_USERS_LOWERCASE, { + keys: [identifier.toLowerCase()], + include_docs: true, + })) as PlatformUser +} + +export async function getExistingTenantUsers( + emails: string[] +): Promise { + const lcEmails = emails.map(email => email.toLowerCase()) + const params = { + keys: lcEmails, + include_docs: true, + } + + const opts = { + arrayResponse: true, + } + + return (await dbUtils.queryGlobalView( + ViewName.USER_BY_EMAIL, + params, + undefined, + opts + )) as User[] +} + +export async function getExistingPlatformUsers( + emails: string[] +): Promise { + const lcEmails = emails.map(email => email.toLowerCase()) + const params = { + keys: lcEmails, + include_docs: true, + } + + const opts = { + arrayResponse: true, + } + return (await dbUtils.queryPlatformView( + ViewName.PLATFORM_USERS_LOWERCASE, + params, + opts + )) as PlatformUserByEmail[] +} + +export async function getExistingAccounts( + emails: string[] +): Promise { + const lcEmails = emails.map(email => email.toLowerCase()) + const params = { + keys: lcEmails, + include_docs: true, + } + + const opts = { + arrayResponse: true, + } + + return (await dbUtils.queryPlatformView( + ViewName.ACCOUNT_BY_EMAIL, + params, + opts + )) as AccountMetadata[] +} diff --git a/packages/backend-core/src/users.ts b/packages/backend-core/src/users/users.ts similarity index 91% rename from packages/backend-core/src/users.ts rename to packages/backend-core/src/users/users.ts index 05abe70fe3..2f869a69d2 100644 --- a/packages/backend-core/src/users.ts +++ b/packages/backend-core/src/users/users.ts @@ -2,7 +2,6 @@ import { directCouchFind, DocumentType, generateAppUserID, - getGlobalIDFromUserMetadataID, getGlobalUserParams, getProdAppID, getUsersByAppParams, @@ -12,17 +11,16 @@ import { SEPARATOR, UNICODE_MAX, ViewName, -} from "./db" +} from "../db" import { BulkDocsResponse, SearchUsersRequest, User, ContextUser, } from "@budibase/types" -import { sdk } from "@budibase/shared-core" -import { getGlobalDB } from "./context" -import * as context from "./context" -import { user as userCache } from "./cache" +import { getGlobalDB } from "../context" +import * as context from "../context" +import { user as userCache } from "../cache" type GetOpts = { cleanup?: boolean } @@ -41,14 +39,6 @@ function removeUserPassword(users: User | User[]) { return users } -// extract from shared-core to make easily accessible from backend-core -export const isBuilder = sdk.users.isBuilder -export const isAdmin = sdk.users.isAdmin -export const isAdminOrBuilder = sdk.users.isAdminOrBuilder -export const hasAdminPermissions = sdk.users.hasAdminPermissions -export const hasBuilderPermissions = sdk.users.hasBuilderPermissions -export const hasAppBuilderPermissions = sdk.users.hasAppBuilderPermissions - export const bulkGetGlobalUsersById = async ( userIds: string[], opts?: GetOpts diff --git a/packages/backend-core/src/users/utils.ts b/packages/backend-core/src/users/utils.ts new file mode 100644 index 0000000000..0a9abd50bc --- /dev/null +++ b/packages/backend-core/src/users/utils.ts @@ -0,0 +1,85 @@ +import { + Account, + CloudAccount, + isSSOAccount, + isSSOUser, + User, +} from "@budibase/types" +import * as pro from "@budibase/pro" +import * as accountSdk from "../accounts" +import env from "../environment" +import { getPlatformUser } from "./lookup" +import { EmailUnavailableError } from "../errors" +import { getTenantId } from "../context" +import { sdk } from "@budibase/shared-core" +import { getAccountByTenantId } from "../accounts" + +// extract from shared-core to make easily accessible from backend-core +export const isBuilder = sdk.users.isBuilder +export const isAdmin = sdk.users.isAdmin +export const isAdminOrBuilder = sdk.users.isAdminOrBuilder +export const hasAdminPermissions = sdk.users.hasAdminPermissions +export const hasBuilderPermissions = sdk.users.hasBuilderPermissions +export const hasAppBuilderPermissions = sdk.users.hasAppBuilderPermissions + +export async function validateUniqueUser(email: string, tenantId: string) { + // check budibase users in other tenants + if (env.MULTI_TENANCY) { + const tenantUser = await getPlatformUser(email) + if (tenantUser != null && tenantUser.tenantId !== tenantId) { + throw new EmailUnavailableError(email) + } + } + + // check root account users in account portal + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + const account = await accountSdk.getAccount(email) + if (account && account.verified && account.tenantId !== tenantId) { + throw new EmailUnavailableError(email) + } + } +} + +export async function isPreventPasswordActions(user: User, account?: Account) { + // when in maintenance mode we allow sso users with the admin role + // to perform any password action - this prevents lockout + if (env.ENABLE_SSO_MAINTENANCE_MODE && isAdmin(user)) { + return false + } + + // SSO is enforced for all users + if (await pro.features.isSSOEnforced()) { + return true + } + + // Check local sso + if (isSSOUser(user)) { + return true + } + + // Check account sso + if (!account) { + account = await accountSdk.getAccountByTenantId(getTenantId()) + } + return !!(account && account.email === user.email && isSSOAccount(account)) +} + +/** + * For the given user id's, return the account holder if it is in the ids. + */ +export async function getAccountHolderFromUserIds( + userIds: string[] +): Promise { + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + const tenantId = getTenantId() + const account = await getAccountByTenantId(tenantId) + if (!account) { + throw new Error(`Account not found for tenantId=${tenantId}`) + } + + const budibaseUserId = account.budibaseUserId + if (userIds.includes(budibaseUserId)) { + return account + } + } +} diff --git a/packages/worker/src/api/controllers/global/roles.ts b/packages/worker/src/api/controllers/global/roles.ts index 572c3328b6..457587120f 100644 --- a/packages/worker/src/api/controllers/global/roles.ts +++ b/packages/worker/src/api/controllers/global/roles.ts @@ -3,12 +3,12 @@ import { roles, context, cache, + users as usersCore, tenancy, } from "@budibase/backend-core" -import { BBContext, App } from "@budibase/types" -import { allUsers } from "../../../sdk/users" +import { Ctx, App } from "@budibase/types" -export async function fetch(ctx: BBContext) { +export async function fetch(ctx: Ctx) { const tenantId = ctx.user!.tenantId // always use the dev apps as they'll be most up to date (true) const apps = (await dbCore.getAllApps({ tenantId, all: true })) as App[] @@ -31,7 +31,7 @@ export async function fetch(ctx: BBContext) { ctx.body = response } -export async function find(ctx: BBContext) { +export async function find(ctx: Ctx) { const appId = ctx.params.appId await context.doInAppContext(dbCore.getDevAppID(appId), async () => { const db = context.getAppDB() @@ -45,10 +45,10 @@ export async function find(ctx: BBContext) { }) } -export async function removeAppRole(ctx: BBContext) { +export async function removeAppRole(ctx: Ctx) { const { appId } = ctx.params const db = tenancy.getGlobalDB() - const users = await allUsers() + const users = await usersCore.db.allUsers() const bulk = [] const cacheInvalidations = [] for (let user of users) { diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 6862e44b05..5984f39ef8 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -42,7 +42,7 @@ export const save = async (ctx: UserCtx) => { const currentUserId = ctx.user?._id const requestUser = ctx.request.body - const user = await userSdk.save(requestUser, { currentUserId }) + const user = await userSdk.db.save(requestUser, { currentUserId }) ctx.body = { _id: user._id!, @@ -58,7 +58,7 @@ const bulkDelete = async (userIds: string[], currentUserId: string) => { if (userIds?.indexOf(currentUserId) !== -1) { throw new Error("Unable to delete self.") } - return await userSdk.bulkDelete(userIds) + return await userSdk.db.bulkDelete(userIds) } const bulkCreate = async (users: User[], groupIds: string[]) => { @@ -67,7 +67,7 @@ const bulkCreate = async (users: User[], groupIds: string[]) => { "Max limit for upload is 1000 users. Please reduce file size and try again." ) } - return await userSdk.bulkCreate(users, groupIds) + return await userSdk.db.bulkCreate(users, groupIds) } export const bulkUpdate = async ( @@ -142,7 +142,7 @@ export const adminUser = async ( // always bust checklist beforehand, if an error occurs but can proceed, don't get // stuck in a cycle await cache.bustCache(cache.CacheKey.CHECKLIST) - const finalUser = await userSdk.save(user, { + const finalUser = await userSdk.db.save(user, { hashPassword, requirePassword, }) @@ -168,7 +168,7 @@ export const adminUser = async ( export const countByApp = async (ctx: any) => { const appId = ctx.params.appId try { - ctx.body = await userSdk.countUsersByApp(appId) + ctx.body = await userSdk.db.countUsersByApp(appId) } catch (err: any) { ctx.throw(err.status || 400, err) } @@ -180,7 +180,7 @@ export const destroy = async (ctx: any) => { ctx.throw(400, "Unable to delete self.") } - await userSdk.destroy(id) + await userSdk.db.destroy(id) ctx.body = { message: `User ${id} deleted.`, @@ -189,7 +189,7 @@ export const destroy = async (ctx: any) => { export const getAppUsers = async (ctx: Ctx) => { const body = ctx.request.body - const users = await userSdk.getUsersByAppAccess(body?.appId) + const users = await userSdk.db.getUsersByAppAccess(body?.appId) ctx.body = { data: users } } @@ -213,7 +213,7 @@ export const search = async (ctx: Ctx) => { // called internally by app server user fetch export const fetch = async (ctx: any) => { - const all = await userSdk.allUsers() + const all = await userSdk.db.allUsers() // user hashed password shouldn't ever be returned for (let user of all) { if (user) { @@ -225,12 +225,12 @@ export const fetch = async (ctx: any) => { // called internally by app server user find export const find = async (ctx: any) => { - ctx.body = await userSdk.getUser(ctx.params.id) + ctx.body = await userSdk.db.getUser(ctx.params.id) } export const tenantUserLookup = async (ctx: any) => { const id = ctx.params.id - const user = await userSdk.getPlatformUser(id) + const user = await userSdk.core.getPlatformUser(id) if (user) { ctx.body = user } else { @@ -253,7 +253,7 @@ export const onboardUsers = async (ctx: Ctx) => { // @ts-ignore const { users, groups, roles } = request.create const assignUsers = users.map((user: User) => (user.roles = roles)) - onboardingResponse = await userSdk.bulkCreate(assignUsers, groups) + onboardingResponse = await userSdk.db.bulkCreate(assignUsers, groups) ctx.body = onboardingResponse } else if (emailConfigured) { onboardingResponse = await inviteMultiple(ctx) @@ -278,7 +278,7 @@ export const onboardUsers = async (ctx: Ctx) => { tenantId: tenancy.getTenantId(), } }) - let bulkCreateReponse = await userSdk.bulkCreate(users, []) + let bulkCreateReponse = await userSdk.db.bulkCreate(users, []) // Apply temporary credentials let createWithCredentials = { @@ -411,7 +411,7 @@ export const inviteAccept = async ( ...info, } - const saved = await userSdk.save(request) + const saved = await userSdk.db.save(request) const db = tenancy.getGlobalDB() const user = await db.get(saved._id) await events.user.inviteAccepted(user) @@ -435,18 +435,18 @@ export const inviteAccept = async ( export const grantAppBuilder = async (ctx: Ctx) => { const { userId } = ctx.params - const user = await userSdk.getUser(userId) + const user = await userSdk.db.getUser(userId) if (!user.builder) { user.builder = {} } user.builder.appBuilder = true - await userSdk.save(user, { hashPassword: false }) + await userSdk.db.save(user, { hashPassword: false }) ctx.body = { message: `User "${user.email}" granted app builder permissions` } } export const addAppBuilder = async (ctx: Ctx) => { const { userId, appId } = ctx.params - const user = await userSdk.getUser(userId) + const user = await userSdk.db.getUser(userId) if (!user.builder?.global || user.admin?.global) { ctx.body = { message: "User already admin - no permissions updated." } return @@ -462,13 +462,13 @@ export const addAppBuilder = async (ctx: Ctx) => { user.builder.apps = [] } user.builder.apps.push(prodAppId) - await userSdk.save(user, { hashPassword: false }) + await userSdk.db.save(user, { hashPassword: false }) ctx.body = { message: `User "${user.email}" app builder access updated.` } } export const removeAppBuilder = async (ctx: Ctx) => { const { userId, appId } = ctx.params - const user = await userSdk.getUser(userId) + const user = await userSdk.db.getUser(userId) if (!user.builder?.global || user.admin?.global) { ctx.body = { message: "User already admin - no permissions removed." } return @@ -484,6 +484,6 @@ export const removeAppBuilder = async (ctx: Ctx) => { if (indexOf && indexOf !== -1) { user.builder.apps = user.builder.apps!.splice(indexOf, 1) } - await userSdk.save(user, { hashPassword: false }) + await userSdk.db.save(user, { hashPassword: false }) ctx.body = { message: `User "${user.email}" app builder access removed.` } } diff --git a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts new file mode 100644 index 0000000000..2974588cae --- /dev/null +++ b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts @@ -0,0 +1,52 @@ +import { TestConfiguration, structures } from "../../../../tests" +import { User } from "@budibase/types" + +const MOCK_APP_ID = "app_a" + +describe("/api/global/users/:userId/app/builder", () => { + const config = new TestConfiguration() + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + async function newUser() { + const base = structures.users.user() + return await config.createUser(base) + } + + async function getUser(userId: string) { + const response = await config.api.users.getUser(userId) + return response.body as User + } + + async function grantAppBuilder(): Promise { + const user = await newUser() + await config.api.users.grantAppBuilder(user._id!) + return await getUser(user._id!) + } + + describe("POST /api/global/users/:userId/app/builder", () => { + it("should be able to grant a user builder permissions", async () => { + const user = await grantAppBuilder() + expect(user.builder?.appBuilder).toBe(true) + }) + }) + + describe("PATCH /api/global/users/:userId/app/:appId/builder", () => { + it("shouldn't allow granting access to an app to a non-app builder", async () => { + const user = await newUser() + await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) + }) + + it("should be able to grant a user access to a particular app", async () => { + const user = await grantAppBuilder() + }) + }) + + describe("DELETE /api/global/users/:userId/app/:appId/builder", () => {}) +}) diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index 52d77cbae6..df9c19f8ca 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -66,7 +66,7 @@ describe("/api/global/users", () => { expect(res.body._id).toBeDefined() const user = await config.getUser(email) expect(user).toBeDefined() - expect(user._id).toEqual(res.body._id) + expect(user!._id).toEqual(res.body._id) expect(events.user.inviteAccepted).toBeCalledTimes(1) expect(events.user.inviteAccepted).toBeCalledWith(user) }) diff --git a/packages/worker/src/sdk/users/index.ts b/packages/worker/src/sdk/users/index.ts index 2eaa0e68a2..b37ea07d94 100644 --- a/packages/worker/src/sdk/users/index.ts +++ b/packages/worker/src/sdk/users/index.ts @@ -1,2 +1,4 @@ export * from "./users" +import { users } from "@budibase/backend-core" +export const db = users.db export { users as core } from "@budibase/backend-core" diff --git a/packages/worker/src/sdk/users/tests/users.spec.ts b/packages/worker/src/sdk/users/tests/users.spec.ts index a24f074512..5962e78e9a 100644 --- a/packages/worker/src/sdk/users/tests/users.spec.ts +++ b/packages/worker/src/sdk/users/tests/users.spec.ts @@ -1,9 +1,8 @@ import { structures } from "../../../tests" import { mocks } from "@budibase/backend-core/tests" -import { env, context } from "@budibase/backend-core" +import { env, context, users as usersCore } from "@budibase/backend-core" import * as users from "../users" import { CloudAccount } from "@budibase/types" -import { isPreventPasswordActions } from "../users" jest.mock("@budibase/pro") import * as _pro from "@budibase/pro" @@ -18,7 +17,7 @@ describe("users", () => { it("returns false for non sso user", async () => { await context.doInTenant(structures.tenant.id(), async () => { const user = structures.users.user() - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(false) }) }) @@ -29,7 +28,7 @@ describe("users", () => { const account = structures.accounts.ssoAccount() as CloudAccount account.email = user.email mocks.accounts.getAccountByTenantId.mockResolvedValueOnce(account) - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(true) }) }) @@ -39,7 +38,7 @@ describe("users", () => { const user = structures.users.user() const account = structures.accounts.ssoAccount() as CloudAccount mocks.accounts.getAccountByTenantId.mockResolvedValueOnce(account) - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(false) }) }) @@ -47,7 +46,7 @@ describe("users", () => { it("returns true for sso user", async () => { await context.doInTenant(structures.tenant.id(), async () => { const user = structures.users.ssoUser() - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(true) }) }) @@ -57,7 +56,7 @@ describe("users", () => { await context.doInTenant(structures.tenant.id(), async () => { const user = structures.users.user() pro.features.isSSOEnforced.mockResolvedValueOnce(true) - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(true) }) }) @@ -75,7 +74,7 @@ describe("users", () => { describe("non-admin user", () => { it("returns true", async () => { const user = structures.users.ssoUser() - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(true) }) }) @@ -85,7 +84,7 @@ describe("users", () => { const user = structures.users.ssoUser({ user: structures.users.adminUser(), }) - const result = await users.isPreventPasswordActions(user) + const result = await usersCore.isPreventPasswordActions(user) expect(result).toBe(false) }) }) diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index e0aa042995..f45c7dda20 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -1,590 +1,7 @@ -import env from "../../environment" -import * as eventHelpers from "./events" -import { - accounts, - cache, - constants, - db as dbUtils, - events, - HTTPError, - sessions, - tenancy, - platform, - users as usersCore, - utils, - ViewName, - env as coreEnv, - context, - EmailUnavailableError, -} from "@budibase/backend-core" -import { - AccountMetadata, - AllDocsResponse, - CloudAccount, - InviteUsersRequest, - InviteUsersResponse, - isSSOAccount, - isSSOUser, - PlatformUser, - PlatformUserByEmail, - RowResponse, - User, - SaveUserOpts, - BulkUserCreated, - BulkUserDeleted, - Account, -} from "@budibase/types" +import { events, tenancy, users as usersCore } from "@budibase/backend-core" +import { InviteUsersRequest, InviteUsersResponse } from "@budibase/types" import { sendEmail } from "../../utilities/email" import { EmailTemplatePurpose } from "../../constants" -import * as pro from "@budibase/pro" -import * as accountSdk from "../accounts" - -export const allUsers = async () => { - const db = tenancy.getGlobalDB() - const response = await db.allDocs( - dbUtils.getGlobalUserParams(null, { - include_docs: true, - }) - ) - return response.rows.map((row: any) => row.doc) -} - -export const countUsersByApp = async (appId: string) => { - let response: any = await usersCore.searchGlobalUsersByApp(appId, {}) - return { - userCount: response.length, - } -} - -export const getUsersByAppAccess = async (appId?: string) => { - const opts: any = { - include_docs: true, - limit: 50, - } - let response: User[] = await usersCore.searchGlobalUsersByAppAccess( - appId, - opts - ) - return response -} - -export async function getUserByEmail(email: string) { - return usersCore.getGlobalUserByEmail(email) -} - -/** - * Gets a user by ID from the global database, based on the current tenancy. - */ -export const getUser = async (userId: string) => { - const user = await usersCore.getById(userId) - if (user) { - delete user.password - } - return user -} - -const buildUser = async ( - user: User, - opts: SaveUserOpts = { - hashPassword: true, - requirePassword: true, - }, - tenantId: string, - dbUser?: any, - account?: Account -): Promise => { - let { password, _id } = user - - // don't require a password if the db user doesn't already have one - if (dbUser && !dbUser.password) { - opts.requirePassword = false - } - - let hashedPassword - if (password) { - if (await isPreventPasswordActions(user, account)) { - throw new HTTPError("Password change is disabled for this user", 400) - } - hashedPassword = opts.hashPassword ? await utils.hash(password) : password - } else if (dbUser) { - hashedPassword = dbUser.password - } - - // passwords are never required if sso is enforced - const requirePasswords = - opts.requirePassword && !(await pro.features.isSSOEnforced()) - if (!hashedPassword && requirePasswords) { - throw "Password must be specified." - } - - _id = _id || dbUtils.generateGlobalUserID() - - const fullUser = { - createdAt: Date.now(), - ...dbUser, - ...user, - _id, - password: hashedPassword, - tenantId, - } - // make sure the roles object is always present - if (!fullUser.roles) { - fullUser.roles = {} - } - // add the active status to a user if its not provided - if (fullUser.status == null) { - fullUser.status = constants.UserStatus.ACTIVE - } - - return fullUser -} - -// lookup, could be email or userId, either will return a doc -export const getPlatformUser = async ( - identifier: string -): Promise => { - // use the view here and allow to find anyone regardless of casing - // Use lowercase to ensure email login is case insensitive - const response = dbUtils.queryPlatformView( - ViewName.PLATFORM_USERS_LOWERCASE, - { - keys: [identifier.toLowerCase()], - include_docs: true, - } - ) as Promise - return response -} - -const validateUniqueUser = async (email: string, tenantId: string) => { - // check budibase users in other tenants - if (env.MULTI_TENANCY) { - const tenantUser = await getPlatformUser(email) - if (tenantUser != null && tenantUser.tenantId !== tenantId) { - throw new EmailUnavailableError(email) - } - } - - // check root account users in account portal - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - const account = await accounts.getAccount(email) - if (account && account.verified && account.tenantId !== tenantId) { - throw new EmailUnavailableError(email) - } - } -} - -export async function isPreventPasswordActions(user: User, account?: Account) { - // when in maintenance mode we allow sso users with the admin role - // to perform any password action - this prevents lockout - if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && usersCore.isAdmin(user)) { - return false - } - - // SSO is enforced for all users - if (await pro.features.isSSOEnforced()) { - return true - } - - // Check local sso - if (isSSOUser(user)) { - return true - } - - // Check account sso - if (!account) { - account = await accountSdk.api.getAccountByTenantId(tenancy.getTenantId()) - } - return !!(account && account.email === user.email && isSSOAccount(account)) -} - -// TODO: The single save should re-use the bulk insert with a single -// user so that we don't need to duplicate logic -export const save = async ( - user: User, - opts: SaveUserOpts = {} -): Promise => { - // default booleans to true - if (opts.hashPassword == null) { - opts.hashPassword = true - } - if (opts.requirePassword == null) { - opts.requirePassword = true - } - const tenantId = tenancy.getTenantId() - const db = tenancy.getGlobalDB() - - let { email, _id, userGroups = [], roles } = user - - if (!email && !_id) { - throw new Error("_id or email is required") - } - - let dbUser: User | undefined - if (_id) { - // try to get existing user from db - try { - dbUser = (await db.get(_id)) as User - if (email && dbUser.email !== email) { - throw "Email address cannot be changed" - } - email = dbUser.email - } catch (e: any) { - if (e.status === 404) { - // do nothing, save this new user with the id specified - required for SSO auth - } else { - throw e - } - } - } - - if (!dbUser && email) { - // no id was specified - load from email instead - dbUser = await usersCore.getGlobalUserByEmail(email) - if (dbUser && dbUser._id !== _id) { - throw new EmailUnavailableError(email) - } - } - - const change = dbUser ? 0 : 1 // no change if there is existing user - return pro.quotas.addUsers(change, async () => { - await validateUniqueUser(email, tenantId) - - let builtUser = await buildUser(user, opts, tenantId, dbUser) - // don't allow a user to update its own roles/perms - if (opts.currentUserId && opts.currentUserId === dbUser?._id) { - builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User - } - - if (!dbUser && roles?.length) { - builtUser.roles = { ...roles } - } - - // make sure we set the _id field for a new user - // Also if this is a new user, associate groups with them - let groupPromises = [] - if (!_id) { - _id = builtUser._id! - - if (userGroups.length > 0) { - for (let groupId of userGroups) { - groupPromises.push(pro.groups.addUsers(groupId, [_id])) - } - } - } - - try { - // save the user to db - let response = await db.put(builtUser) - builtUser._rev = response.rev - - await eventHelpers.handleSaveEvents(builtUser, dbUser) - await platform.users.addUser(tenantId, builtUser._id!, builtUser.email) - await cache.user.invalidateUser(response.id) - - await Promise.all(groupPromises) - - // finally returned the saved user from the db - return db.get(builtUser._id!) - } catch (err: any) { - if (err.status === 409) { - throw "User exists already" - } else { - throw err - } - } - }) -} - -const getExistingTenantUsers = async (emails: string[]): Promise => { - const lcEmails = emails.map(email => email.toLowerCase()) - const params = { - keys: lcEmails, - include_docs: true, - } - - const opts = { - arrayResponse: true, - } - - return dbUtils.queryGlobalView( - ViewName.USER_BY_EMAIL, - params, - undefined, - opts - ) as Promise -} - -const getExistingPlatformUsers = async ( - emails: string[] -): Promise => { - const lcEmails = emails.map(email => email.toLowerCase()) - const params = { - keys: lcEmails, - include_docs: true, - } - - const opts = { - arrayResponse: true, - } - return dbUtils.queryPlatformView( - ViewName.PLATFORM_USERS_LOWERCASE, - params, - opts - ) as Promise -} - -const getExistingAccounts = async ( - emails: string[] -): Promise => { - const lcEmails = emails.map(email => email.toLowerCase()) - const params = { - keys: lcEmails, - include_docs: true, - } - - const opts = { - arrayResponse: true, - } - - return dbUtils.queryPlatformView( - ViewName.ACCOUNT_BY_EMAIL, - params, - opts - ) as Promise -} - -/** - * Apply a system-wide search on emails: - * - in tenant - * - cross tenant - * - accounts - * return an array of emails that match the supplied emails. - */ -const searchExistingEmails = async (emails: string[]) => { - let matchedEmails: string[] = [] - - const existingTenantUsers = await getExistingTenantUsers(emails) - matchedEmails.push(...existingTenantUsers.map(user => user.email)) - - const existingPlatformUsers = await getExistingPlatformUsers(emails) - matchedEmails.push(...existingPlatformUsers.map(user => user._id!)) - - const existingAccounts = await getExistingAccounts(emails) - matchedEmails.push(...existingAccounts.map(account => account.email)) - - return [...new Set(matchedEmails.map(email => email.toLowerCase()))] -} - -export const bulkCreate = async ( - newUsersRequested: User[], - groups: string[] -): Promise => { - const tenantId = tenancy.getTenantId() - - let usersToSave: any[] = [] - let newUsers: any[] = [] - - const emails = newUsersRequested.map((user: User) => user.email) - const existingEmails = await searchExistingEmails(emails) - const unsuccessful: { email: string; reason: string }[] = [] - - for (const newUser of newUsersRequested) { - if ( - newUsers.find( - (x: User) => x.email.toLowerCase() === newUser.email.toLowerCase() - ) || - existingEmails.includes(newUser.email.toLowerCase()) - ) { - unsuccessful.push({ - email: newUser.email, - reason: `Unavailable`, - }) - continue - } - newUser.userGroups = groups - newUsers.push(newUser) - } - - const account = await accountSdk.api.getAccountByTenantId(tenantId) - return pro.quotas.addUsers(newUsers.length, async () => { - // create the promises array that will be called by bulkDocs - newUsers.forEach((user: any) => { - usersToSave.push( - buildUser( - user, - { - hashPassword: true, - requirePassword: user.requirePassword, - }, - tenantId, - undefined, // no dbUser - account - ) - ) - }) - - const usersToBulkSave = await Promise.all(usersToSave) - await usersCore.bulkUpdateGlobalUsers(usersToBulkSave) - - // Post-processing of bulk added users, e.g. events and cache operations - for (const user of usersToBulkSave) { - // TODO: Refactor to bulk insert users into the info db - // instead of relying on looping tenant creation - await platform.users.addUser(tenantId, user._id, user.email) - await eventHelpers.handleSaveEvents(user, undefined) - } - - const saved = usersToBulkSave.map(user => { - return { - _id: user._id, - email: user.email, - } - }) - - // now update the groups - if (Array.isArray(saved) && groups) { - const groupPromises = [] - const createdUserIds = saved.map(user => user._id) - for (let groupId of groups) { - groupPromises.push(pro.groups.addUsers(groupId, createdUserIds)) - } - await Promise.all(groupPromises) - } - - return { - successful: saved, - unsuccessful, - } - }) -} - -/** - * For the given user id's, return the account holder if it is in the ids. - */ -const getAccountHolderFromUserIds = async ( - userIds: string[] -): Promise => { - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - const tenantId = tenancy.getTenantId() - const account = await accounts.getAccountByTenantId(tenantId) - if (!account) { - throw new Error(`Account not found for tenantId=${tenantId}`) - } - - const budibaseUserId = account.budibaseUserId - if (userIds.includes(budibaseUserId)) { - return account - } - } -} - -export const bulkDelete = async ( - userIds: string[] -): Promise => { - const db = tenancy.getGlobalDB() - - const response: BulkUserDeleted = { - successful: [], - unsuccessful: [], - } - - // remove the account holder from the delete request if present - const account = await getAccountHolderFromUserIds(userIds) - if (account) { - userIds = userIds.filter(u => u !== account.budibaseUserId) - // mark user as unsuccessful - response.unsuccessful.push({ - _id: account.budibaseUserId, - email: account.email, - reason: "Account holder cannot be deleted", - }) - } - - // Get users and delete - const allDocsResponse: AllDocsResponse = await db.allDocs({ - include_docs: true, - keys: userIds, - }) - const usersToDelete: User[] = allDocsResponse.rows.map( - (user: RowResponse) => { - return user.doc - } - ) - - // Delete from DB - const toDelete = usersToDelete.map(user => ({ - ...user, - _deleted: true, - })) - const dbResponse = await usersCore.bulkUpdateGlobalUsers(toDelete) - - await pro.quotas.removeUsers(toDelete.length) - for (let user of usersToDelete) { - await bulkDeleteProcessing(user) - } - - // Build Response - // index users by id - const userIndex: { [key: string]: User } = {} - usersToDelete.reduce((prev, current) => { - prev[current._id!] = current - return prev - }, userIndex) - - // add the successful and unsuccessful users to response - dbResponse.forEach(item => { - const email = userIndex[item.id].email - if (item.ok) { - response.successful.push({ _id: item.id, email }) - } else { - response.unsuccessful.push({ - _id: item.id, - email, - reason: "Database error", - }) - } - }) - - return response -} - -// TODO: The single delete should re-use the bulk delete with a single -// user so that we don't need to duplicate logic -export const destroy = async (id: string) => { - const db = tenancy.getGlobalDB() - const dbUser = (await db.get(id)) as User - const userId = dbUser._id as string - - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - // root account holder can't be deleted from inside budibase - const email = dbUser.email - const account = await accounts.getAccount(email) - if (account) { - if (dbUser.userId === context.getIdentity()!._id) { - throw new HTTPError('Please visit "Account" to delete this user', 400) - } else { - throw new HTTPError("Account holder cannot be deleted", 400) - } - } - } - - await platform.users.removeUser(dbUser) - - await db.remove(userId, dbUser._rev) - - await pro.quotas.removeUsers(1) - await eventHelpers.handleDeleteEvents(dbUser) - await cache.user.invalidateUser(userId) - await sessions.invalidateSessions(userId, { reason: "deletion" }) -} - -const bulkDeleteProcessing = async (dbUser: User) => { - const userId = dbUser._id as string - await platform.users.removeUser(dbUser) - await eventHelpers.handleDeleteEvents(dbUser) - await cache.user.invalidateUser(userId) - await sessions.invalidateSessions(userId, { reason: "bulk-deletion" }) -} export const invite = async ( users: InviteUsersRequest @@ -594,7 +11,9 @@ export const invite = async ( unsuccessful: [], } - const matchedEmails = await searchExistingEmails(users.map(u => u.email)) + const matchedEmails = await usersCore.searchExistingEmails( + users.map(u => u.email) + ) const newUsers = [] // separate duplicates from new users diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index b41b76efda..8fd6c46284 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -263,7 +263,7 @@ class TestConfiguration { } const response = await this._req(user, null, controllers.users.save) const body = response as SaveUserResponse - return this.getUser(body.email) as Promise + return (await this.getUser(body.email)) as User } // CONFIGS diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index 16a43d70e0..39f7b64d59 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -140,4 +140,28 @@ export class UserAPI extends TestAPI { .expect("Content-Type", /json/) .expect(opts?.status ? opts.status : 200) } + + grantAppBuilder = (userId: string) => { + return this.request + .post(`/api/global/users/${userId}/app/builder`) + .set(this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + + grantBuilderToApp = (userId: string, appId: string) => { + return this.request + .patch(`/api/global/users/${userId}/app/${appId}/builder`) + .set(this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } + + revokeBuilderToApp = (userId: string, appId: string) => { + return this.request + .delete(`/api/global/users/${userId}/app/${appId}/builder`) + .set(this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(200) + } } From 66fbdfe4e89e42a7d350772a10afcf9ce9b26256 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 25 Jul 2023 18:39:40 +0100 Subject: [PATCH 009/117] Breaking out pro components back into the worker user SDK, and attempting to separate the pro components as much as possible from the user SDK itself, so that it can be easily re-created in other services. --- packages/backend-core/src/users/db.ts | 728 +++++++++--------- packages/backend-core/src/users/index.ts | 2 +- packages/backend-core/src/users/utils.ts | 33 +- .../routes/global/tests/appBuilder.spec.ts | 18 +- packages/worker/src/sdk/users/index.ts | 10 +- packages/worker/src/sdk/users/users.ts | 108 ++- packages/worker/src/tests/api/users.ts | 14 +- 7 files changed, 487 insertions(+), 426 deletions(-) diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index cb1129d079..5e0a2bf31d 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -2,409 +2,36 @@ import env from "../environment" import * as eventHelpers from "./events" import * as accounts from "../accounts" import * as cache from "../cache" -import { UserStatus, ViewName } from "../constants" import { getIdentity, getTenantId, getGlobalDB } from "../context" import * as dbUtils from "../db" import { EmailUnavailableError, HTTPError } from "../errors" import * as platform from "../platform" import * as sessions from "../security/sessions" -import * as utils from "../utils" import * as usersCore from "./users" import { - Account, AllDocsResponse, BulkUserCreated, BulkUserDeleted, - PlatformUser, RowResponse, SaveUserOpts, User, + Account, } from "@budibase/types" -import * as pro from "@budibase/pro" import * as accountSdk from "../accounts" -import { - isPreventPasswordActions, - validateUniqueUser, - getAccountHolderFromUserIds, -} from "./utils" +import { validateUniqueUser, getAccountHolderFromUserIds } from "./utils" import { searchExistingEmails } from "./lookup" -export async function allUsers() { - const db = getGlobalDB() - const response = await db.allDocs( - dbUtils.getGlobalUserParams(null, { - include_docs: true, - }) - ) - return response.rows.map((row: any) => row.doc) -} - -export async function countUsersByApp(appId: string) { - let response: any = await usersCore.searchGlobalUsersByApp(appId, {}) - return { - userCount: response.length, - } -} - -export async function getUsersByAppAccess(appId?: string) { - const opts: any = { - include_docs: true, - limit: 50, - } - let response: User[] = await usersCore.searchGlobalUsersByAppAccess( - appId, - opts - ) - return response -} - -export async function getUserByEmail(email: string) { - return usersCore.getGlobalUserByEmail(email) -} - -/** - * Gets a user by ID from the global database, based on the current tenancy. - */ -export async function getUser(userId: string) { - const user = await usersCore.getById(userId) - if (user) { - delete user.password - } - return user -} - -export async function buildUser( +type QuotaUpdateFn = (change: number, cb?: () => Promise) => Promise +type GroupUpdateFn = (groupId: string, userIds: string[]) => Promise +type QuotaFns = { addUsers: QuotaUpdateFn; removeUsers: QuotaUpdateFn } +type GroupFns = { addUsers: GroupUpdateFn } +type BuildUserFn = ( user: User, - opts: SaveUserOpts = { - hashPassword: true, - requirePassword: true, - }, + opts: SaveUserOpts, tenantId: string, - dbUser?: any, + dbUser?: User, account?: Account -): Promise { - let { password, _id } = user - - // don't require a password if the db user doesn't already have one - if (dbUser && !dbUser.password) { - opts.requirePassword = false - } - - let hashedPassword - if (password) { - if (await isPreventPasswordActions(user, account)) { - throw new HTTPError("Password change is disabled for this user", 400) - } - hashedPassword = opts.hashPassword ? await utils.hash(password) : password - } else if (dbUser) { - hashedPassword = dbUser.password - } - - // passwords are never required if sso is enforced - const requirePasswords = - opts.requirePassword && !(await pro.features.isSSOEnforced()) - if (!hashedPassword && requirePasswords) { - throw "Password must be specified." - } - - _id = _id || dbUtils.generateGlobalUserID() - - const fullUser = { - createdAt: Date.now(), - ...dbUser, - ...user, - _id, - password: hashedPassword, - tenantId, - } - // make sure the roles object is always present - if (!fullUser.roles) { - fullUser.roles = {} - } - // add the active status to a user if its not provided - if (fullUser.status == null) { - fullUser.status = UserStatus.ACTIVE - } - - return fullUser -} - -export const save = async ( - user: User, - opts: SaveUserOpts = {} -): Promise => { - // default booleans to true - if (opts.hashPassword == null) { - opts.hashPassword = true - } - if (opts.requirePassword == null) { - opts.requirePassword = true - } - const tenantId = getTenantId() - const db = getGlobalDB() - - let { email, _id, userGroups = [], roles } = user - - if (!email && !_id) { - throw new Error("_id or email is required") - } - - let dbUser: User | undefined - if (_id) { - // try to get existing user from db - try { - dbUser = (await db.get(_id)) as User - if (email && dbUser.email !== email) { - throw "Email address cannot be changed" - } - email = dbUser.email - } catch (e: any) { - if (e.status === 404) { - // do nothing, save this new user with the id specified - required for SSO auth - } else { - throw e - } - } - } - - if (!dbUser && email) { - // no id was specified - load from email instead - dbUser = await usersCore.getGlobalUserByEmail(email) - if (dbUser && dbUser._id !== _id) { - throw new EmailUnavailableError(email) - } - } - - const change = dbUser ? 0 : 1 // no change if there is existing user - return pro.quotas.addUsers(change, async () => { - await validateUniqueUser(email, tenantId) - - let builtUser = await buildUser(user, opts, tenantId, dbUser) - // don't allow a user to update its own roles/perms - if (opts.currentUserId && opts.currentUserId === dbUser?._id) { - builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User - } - - if (!dbUser && roles?.length) { - builtUser.roles = { ...roles } - } - - // make sure we set the _id field for a new user - // Also if this is a new user, associate groups with them - let groupPromises = [] - if (!_id) { - _id = builtUser._id! - - if (userGroups.length > 0) { - for (let groupId of userGroups) { - groupPromises.push(pro.groups.addUsers(groupId, [_id])) - } - } - } - - try { - // save the user to db - let response = await db.put(builtUser) - builtUser._rev = response.rev - - await eventHelpers.handleSaveEvents(builtUser, dbUser) - await platform.users.addUser(tenantId, builtUser._id!, builtUser.email) - await cache.user.invalidateUser(response.id) - - await Promise.all(groupPromises) - - // finally returned the saved user from the db - return db.get(builtUser._id!) - } catch (err: any) { - if (err.status === 409) { - throw "User exists already" - } else { - throw err - } - } - }) -} - -export const bulkCreate = async ( - newUsersRequested: User[], - groups: string[] -): Promise => { - const tenantId = getTenantId() - - let usersToSave: any[] = [] - let newUsers: any[] = [] - - const emails = newUsersRequested.map((user: User) => user.email) - const existingEmails = await searchExistingEmails(emails) - const unsuccessful: { email: string; reason: string }[] = [] - - for (const newUser of newUsersRequested) { - if ( - newUsers.find( - (x: User) => x.email.toLowerCase() === newUser.email.toLowerCase() - ) || - existingEmails.includes(newUser.email.toLowerCase()) - ) { - unsuccessful.push({ - email: newUser.email, - reason: `Unavailable`, - }) - continue - } - newUser.userGroups = groups - newUsers.push(newUser) - } - - const account = await accountSdk.getAccountByTenantId(tenantId) - return pro.quotas.addUsers(newUsers.length, async () => { - // create the promises array that will be called by bulkDocs - newUsers.forEach((user: any) => { - usersToSave.push( - buildUser( - user, - { - hashPassword: true, - requirePassword: user.requirePassword, - }, - tenantId, - undefined, // no dbUser - account - ) - ) - }) - - const usersToBulkSave = await Promise.all(usersToSave) - await usersCore.bulkUpdateGlobalUsers(usersToBulkSave) - - // Post-processing of bulk added users, e.g. events and cache operations - for (const user of usersToBulkSave) { - // TODO: Refactor to bulk insert users into the info db - // instead of relying on looping tenant creation - await platform.users.addUser(tenantId, user._id, user.email) - await eventHelpers.handleSaveEvents(user, undefined) - } - - const saved = usersToBulkSave.map(user => { - return { - _id: user._id, - email: user.email, - } - }) - - // now update the groups - if (Array.isArray(saved) && groups) { - const groupPromises = [] - const createdUserIds = saved.map(user => user._id) - for (let groupId of groups) { - groupPromises.push(pro.groups.addUsers(groupId, createdUserIds)) - } - await Promise.all(groupPromises) - } - - return { - successful: saved, - unsuccessful, - } - }) -} - -export const bulkDelete = async ( - userIds: string[] -): Promise => { - const db = getGlobalDB() - - const response: BulkUserDeleted = { - successful: [], - unsuccessful: [], - } - - // remove the account holder from the delete request if present - const account = await getAccountHolderFromUserIds(userIds) - if (account) { - userIds = userIds.filter(u => u !== account.budibaseUserId) - // mark user as unsuccessful - response.unsuccessful.push({ - _id: account.budibaseUserId, - email: account.email, - reason: "Account holder cannot be deleted", - }) - } - - // Get users and delete - const allDocsResponse: AllDocsResponse = await db.allDocs({ - include_docs: true, - keys: userIds, - }) - const usersToDelete: User[] = allDocsResponse.rows.map( - (user: RowResponse) => { - return user.doc - } - ) - - // Delete from DB - const toDelete = usersToDelete.map(user => ({ - ...user, - _deleted: true, - })) - const dbResponse = await usersCore.bulkUpdateGlobalUsers(toDelete) - - await pro.quotas.removeUsers(toDelete.length) - for (let user of usersToDelete) { - await bulkDeleteProcessing(user) - } - - // Build Response - // index users by id - const userIndex: { [key: string]: User } = {} - usersToDelete.reduce((prev, current) => { - prev[current._id!] = current - return prev - }, userIndex) - - // add the successful and unsuccessful users to response - dbResponse.forEach(item => { - const email = userIndex[item.id].email - if (item.ok) { - response.successful.push({ _id: item.id, email }) - } else { - response.unsuccessful.push({ - _id: item.id, - email, - reason: "Database error", - }) - } - }) - - return response -} - -export const destroy = async (id: string) => { - const db = getGlobalDB() - const dbUser = (await db.get(id)) as User - const userId = dbUser._id as string - - if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { - // root account holder can't be deleted from inside budibase - const email = dbUser.email - const account = await accounts.getAccount(email) - if (account) { - if (dbUser.userId === getIdentity()!._id) { - throw new HTTPError('Please visit "Account" to delete this user', 400) - } else { - throw new HTTPError("Account holder cannot be deleted", 400) - } - } - } - - await platform.users.removeUser(dbUser) - - await db.remove(userId, dbUser._rev) - - await pro.quotas.removeUsers(1) - await eventHelpers.handleDeleteEvents(dbUser) - await cache.user.invalidateUser(userId) - await sessions.invalidateSessions(userId, { reason: "deletion" }) -} +) => Promise const bulkDeleteProcessing = async (dbUser: User) => { const userId = dbUser._id as string @@ -413,3 +40,338 @@ const bulkDeleteProcessing = async (dbUser: User) => { await cache.user.invalidateUser(userId) await sessions.invalidateSessions(userId, { reason: "bulk-deletion" }) } + +export class UserDB { + quotas: QuotaFns + groups: GroupFns + ssoEnforcedFn: () => Promise + buildUserFn: BuildUserFn + + constructor( + quotaFns: QuotaFns, + groupFns: GroupFns, + ssoEnforcedFn: () => Promise, + buildUserFn: BuildUserFn + ) { + this.quotas = quotaFns + this.groups = groupFns + this.ssoEnforcedFn = ssoEnforcedFn + this.buildUserFn = buildUserFn + } + + async allUsers() { + const db = getGlobalDB() + const response = await db.allDocs( + dbUtils.getGlobalUserParams(null, { + include_docs: true, + }) + ) + return response.rows.map((row: any) => row.doc) + } + + async countUsersByApp(appId: string) { + let response: any = await usersCore.searchGlobalUsersByApp(appId, {}) + return { + userCount: response.length, + } + } + + async getUsersByAppAccess(appId?: string) { + const opts: any = { + include_docs: true, + limit: 50, + } + let response: User[] = await usersCore.searchGlobalUsersByAppAccess( + appId, + opts + ) + return response + } + + async getUserByEmail(email: string) { + return usersCore.getGlobalUserByEmail(email) + } + + /** + * Gets a user by ID from the global database, based on the current tenancy. + */ + async getUser(userId: string) { + const user = await usersCore.getById(userId) + if (user) { + delete user.password + } + return user + } + + async save(user: User, opts: SaveUserOpts = {}): Promise { + // default booleans to true + if (opts.hashPassword == null) { + opts.hashPassword = true + } + if (opts.requirePassword == null) { + opts.requirePassword = true + } + const tenantId = getTenantId() + const db = getGlobalDB() + + let { email, _id, userGroups = [], roles } = user + + if (!email && !_id) { + throw new Error("_id or email is required") + } + + let dbUser: User | undefined + if (_id) { + // try to get existing user from db + try { + dbUser = (await db.get(_id)) as User + if (email && dbUser.email !== email) { + throw "Email address cannot be changed" + } + email = dbUser.email + } catch (e: any) { + if (e.status === 404) { + // do nothing, save this new user with the id specified - required for SSO auth + } else { + throw e + } + } + } + + if (!dbUser && email) { + // no id was specified - load from email instead + dbUser = await usersCore.getGlobalUserByEmail(email) + if (dbUser && dbUser._id !== _id) { + throw new EmailUnavailableError(email) + } + } + + const change = dbUser ? 0 : 1 // no change if there is existing user + return this.quotas.addUsers(change, async () => { + await validateUniqueUser(email, tenantId) + + let builtUser = await this.buildUserFn(user, opts, tenantId, dbUser) + // don't allow a user to update its own roles/perms + if (opts.currentUserId && opts.currentUserId === dbUser?._id) { + builtUser = usersCore.cleanseUserObject(builtUser, dbUser) as User + } + + if (!dbUser && roles?.length) { + builtUser.roles = { ...roles } + } + + // make sure we set the _id field for a new user + // Also if this is a new user, associate groups with them + let groupPromises = [] + if (!_id) { + _id = builtUser._id! + + if (userGroups.length > 0) { + for (let groupId of userGroups) { + groupPromises.push(this.groups.addUsers(groupId, [_id!])) + } + } + } + + try { + // save the user to db + let response = await db.put(builtUser) + builtUser._rev = response.rev + + await eventHelpers.handleSaveEvents(builtUser, dbUser) + await platform.users.addUser(tenantId, builtUser._id!, builtUser.email) + await cache.user.invalidateUser(response.id) + + await Promise.all(groupPromises) + + // finally returned the saved user from the db + return db.get(builtUser._id!) + } catch (err: any) { + if (err.status === 409) { + throw "User exists already" + } else { + throw err + } + } + }) + } + + async bulkCreate( + newUsersRequested: User[], + groups: string[] + ): Promise { + const tenantId = getTenantId() + + let usersToSave: any[] = [] + let newUsers: any[] = [] + + const emails = newUsersRequested.map((user: User) => user.email) + const existingEmails = await searchExistingEmails(emails) + const unsuccessful: { email: string; reason: string }[] = [] + + for (const newUser of newUsersRequested) { + if ( + newUsers.find( + (x: User) => x.email.toLowerCase() === newUser.email.toLowerCase() + ) || + existingEmails.includes(newUser.email.toLowerCase()) + ) { + unsuccessful.push({ + email: newUser.email, + reason: `Unavailable`, + }) + continue + } + newUser.userGroups = groups + newUsers.push(newUser) + } + + const account = await accountSdk.getAccountByTenantId(tenantId) + return this.quotas.addUsers(newUsers.length, async () => { + // create the promises array that will be called by bulkDocs + newUsers.forEach((user: any) => { + usersToSave.push( + this.buildUserFn( + user, + { + hashPassword: true, + requirePassword: user.requirePassword, + }, + tenantId, + undefined, // no dbUser + account + ) + ) + }) + + const usersToBulkSave = await Promise.all(usersToSave) + await usersCore.bulkUpdateGlobalUsers(usersToBulkSave) + + // Post-processing of bulk added users, e.g. events and cache operations + for (const user of usersToBulkSave) { + // TODO: Refactor to bulk insert users into the info db + // instead of relying on looping tenant creation + await platform.users.addUser(tenantId, user._id, user.email) + await eventHelpers.handleSaveEvents(user, undefined) + } + + const saved = usersToBulkSave.map(user => { + return { + _id: user._id, + email: user.email, + } + }) + + // now update the groups + if (Array.isArray(saved) && groups) { + const groupPromises = [] + const createdUserIds = saved.map(user => user._id) + for (let groupId of groups) { + groupPromises.push(this.groups.addUsers(groupId, createdUserIds)) + } + await Promise.all(groupPromises) + } + + return { + successful: saved, + unsuccessful, + } + }) + } + + async bulkDelete(userIds: string[]): Promise { + const db = getGlobalDB() + + const response: BulkUserDeleted = { + successful: [], + unsuccessful: [], + } + + // remove the account holder from the delete request if present + const account = await getAccountHolderFromUserIds(userIds) + if (account) { + userIds = userIds.filter(u => u !== account.budibaseUserId) + // mark user as unsuccessful + response.unsuccessful.push({ + _id: account.budibaseUserId, + email: account.email, + reason: "Account holder cannot be deleted", + }) + } + + // Get users and delete + const allDocsResponse: AllDocsResponse = await db.allDocs({ + include_docs: true, + keys: userIds, + }) + const usersToDelete: User[] = allDocsResponse.rows.map( + (user: RowResponse) => { + return user.doc + } + ) + + // Delete from DB + const toDelete = usersToDelete.map(user => ({ + ...user, + _deleted: true, + })) + const dbResponse = await usersCore.bulkUpdateGlobalUsers(toDelete) + + await this.quotas.removeUsers(toDelete.length) + for (let user of usersToDelete) { + await bulkDeleteProcessing(user) + } + + // Build Response + // index users by id + const userIndex: { [key: string]: User } = {} + usersToDelete.reduce((prev, current) => { + prev[current._id!] = current + return prev + }, userIndex) + + // add the successful and unsuccessful users to response + dbResponse.forEach(item => { + const email = userIndex[item.id].email + if (item.ok) { + response.successful.push({ _id: item.id, email }) + } else { + response.unsuccessful.push({ + _id: item.id, + email, + reason: "Database error", + }) + } + }) + + return response + } + + async destroy(id: string) { + const db = getGlobalDB() + const dbUser = (await db.get(id)) as User + const userId = dbUser._id as string + + if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { + // root account holder can't be deleted from inside budibase + const email = dbUser.email + const account = await accounts.getAccount(email) + if (account) { + if (dbUser.userId === getIdentity()!._id) { + throw new HTTPError('Please visit "Account" to delete this user', 400) + } else { + throw new HTTPError("Account holder cannot be deleted", 400) + } + } + } + + await platform.users.removeUser(dbUser) + + await db.remove(userId, dbUser._rev) + + await this.quotas.removeUsers(1) + await eventHelpers.handleDeleteEvents(dbUser) + await cache.user.invalidateUser(userId) + await sessions.invalidateSessions(userId, { reason: "deletion" }) + } +} diff --git a/packages/backend-core/src/users/index.ts b/packages/backend-core/src/users/index.ts index 2e5e2f948c..c11d2a2c62 100644 --- a/packages/backend-core/src/users/index.ts +++ b/packages/backend-core/src/users/index.ts @@ -1,4 +1,4 @@ export * from "./users" export * from "./utils" export * from "./lookup" -export * as db from "./db" +export { UserDB } from "./db" diff --git a/packages/backend-core/src/users/utils.ts b/packages/backend-core/src/users/utils.ts index 0a9abd50bc..b6bf3f0abb 100644 --- a/packages/backend-core/src/users/utils.ts +++ b/packages/backend-core/src/users/utils.ts @@ -1,11 +1,4 @@ -import { - Account, - CloudAccount, - isSSOAccount, - isSSOUser, - User, -} from "@budibase/types" -import * as pro from "@budibase/pro" +import { CloudAccount } from "@budibase/types" import * as accountSdk from "../accounts" import env from "../environment" import { getPlatformUser } from "./lookup" @@ -40,30 +33,6 @@ export async function validateUniqueUser(email: string, tenantId: string) { } } -export async function isPreventPasswordActions(user: User, account?: Account) { - // when in maintenance mode we allow sso users with the admin role - // to perform any password action - this prevents lockout - if (env.ENABLE_SSO_MAINTENANCE_MODE && isAdmin(user)) { - return false - } - - // SSO is enforced for all users - if (await pro.features.isSSOEnforced()) { - return true - } - - // Check local sso - if (isSSOUser(user)) { - return true - } - - // Check account sso - if (!account) { - account = await accountSdk.getAccountByTenantId(getTenantId()) - } - return !!(account && account.email === user.email && isSSOAccount(account)) -} - /** * For the given user id's, return the account holder if it is in the ids. */ diff --git a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts index 2974588cae..138ebbc595 100644 --- a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts +++ b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts @@ -40,13 +40,27 @@ describe("/api/global/users/:userId/app/builder", () => { describe("PATCH /api/global/users/:userId/app/:appId/builder", () => { it("shouldn't allow granting access to an app to a non-app builder", async () => { const user = await newUser() - await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) + await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID, 400) }) it("should be able to grant a user access to a particular app", async () => { const user = await grantAppBuilder() + await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) + const updated = await getUser(user._id!) + expect(updated.builder?.appBuilder).toBe(true) + expect(updated.builder?.apps).toBe([MOCK_APP_ID]) }) }) - describe("DELETE /api/global/users/:userId/app/:appId/builder", () => {}) + describe("DELETE /api/global/users/:userId/app/:appId/builder", () => { + it("should allow revoking access", async () => { + const user = await grantAppBuilder() + await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) + let updated = await getUser(user._id!) + expect(updated.builder?.apps).toBe([MOCK_APP_ID]) + await config.api.users.revokeBuilderToApp(user._id!, MOCK_APP_ID) + updated = await getUser(user._id!) + expect(updated.builder?.apps).toBe([]) + }) + }) }) diff --git a/packages/worker/src/sdk/users/index.ts b/packages/worker/src/sdk/users/index.ts index b37ea07d94..d40ed1b045 100644 --- a/packages/worker/src/sdk/users/index.ts +++ b/packages/worker/src/sdk/users/index.ts @@ -1,4 +1,12 @@ export * from "./users" +import { buildUser } from "./users" import { users } from "@budibase/backend-core" -export const db = users.db +import * as pro from "@budibase/pro" +// pass in the components which are specific to the worker/the parts of pro which backend-core cannot access +export const db = new users.UserDB( + pro.quotas, + pro.groups, + pro.features.isSSOEnforced, + buildUser +) export { users as core } from "@budibase/backend-core" diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index f45c7dda20..45a69f5b32 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -1,11 +1,111 @@ -import { events, tenancy, users as usersCore } from "@budibase/backend-core" -import { InviteUsersRequest, InviteUsersResponse } from "@budibase/types" +import { + events, + HTTPError, + tenancy, + users as usersCore, + UserStatus, + db as dbUtils, + utils, + accounts as accountSdk, + context, + env as coreEnv, +} from "@budibase/backend-core" +import { + Account, + InviteUsersRequest, + InviteUsersResponse, + isSSOAccount, + isSSOUser, + SaveUserOpts, + User, +} from "@budibase/types" import { sendEmail } from "../../utilities/email" import { EmailTemplatePurpose } from "../../constants" +import * as pro from "@budibase/pro" -export const invite = async ( +export async function isPreventPasswordActions(user: User, account?: Account) { + // when in maintenance mode we allow sso users with the admin role + // to perform any password action - this prevents lockout + if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && usersCore.isAdmin(user)) { + return false + } + + // SSO is enforced for all users + if (await pro.features.isSSOEnforced()) { + return true + } + + // Check local sso + if (isSSOUser(user)) { + return true + } + + // Check account sso + if (!account) { + account = await accountSdk.getAccountByTenantId(context.getTenantId()) + } + return !!(account && account.email === user.email && isSSOAccount(account)) +} + +export async function buildUser( + user: User, + opts: SaveUserOpts = { + hashPassword: true, + requirePassword: true, + }, + tenantId: string, + dbUser?: any, + account?: Account +): Promise { + let { password, _id } = user + + // don't require a password if the db user doesn't already have one + if (dbUser && !dbUser.password) { + opts.requirePassword = false + } + + let hashedPassword + if (password) { + if (await isPreventPasswordActions(user, account)) { + throw new HTTPError("Password change is disabled for this user", 400) + } + hashedPassword = opts.hashPassword ? await utils.hash(password) : password + } else if (dbUser) { + hashedPassword = dbUser.password + } + + // passwords are never required if sso is enforced + const requirePasswords = + opts.requirePassword && !(await pro.features.isSSOEnforced()) + if (!hashedPassword && requirePasswords) { + throw "Password must be specified." + } + + _id = _id || dbUtils.generateGlobalUserID() + + const fullUser = { + createdAt: Date.now(), + ...dbUser, + ...user, + _id, + password: hashedPassword, + tenantId, + } + // make sure the roles object is always present + if (!fullUser.roles) { + fullUser.roles = {} + } + // add the active status to a user if its not provided + if (fullUser.status == null) { + fullUser.status = UserStatus.ACTIVE + } + + return fullUser +} + +export async function invite( users: InviteUsersRequest -): Promise => { +): Promise { const response: InviteUsersResponse = { successful: [], unsuccessful: [], diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index 39f7b64d59..605ac79416 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -149,15 +149,23 @@ export class UserAPI extends TestAPI { .expect(200) } - grantBuilderToApp = (userId: string, appId: string) => { + grantBuilderToApp = ( + userId: string, + appId: string, + statusCode: number = 200 + ) => { return this.request .patch(`/api/global/users/${userId}/app/${appId}/builder`) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(200) + .expect(statusCode) } - revokeBuilderToApp = (userId: string, appId: string) => { + revokeBuilderToApp = ( + userId: string, + appId: string, + statusCode: number = 200 + ) => { return this.request .delete(`/api/global/users/${userId}/app/${appId}/builder`) .set(this.config.defaultHeaders()) From 43bfb943a3238d1d6bcd68c50194a95df95fde11 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 25 Jul 2023 18:52:59 +0100 Subject: [PATCH 010/117] Some fixes post testing. --- packages/backend-core/src/users/utils.ts | 1 + .../shared-core/src/sdk/documents/users.ts | 4 +++ .../src/api/controllers/global/users.ts | 25 +++++++++++-------- .../routes/global/tests/appBuilder.spec.ts | 6 ++--- packages/worker/src/tests/api/users.ts | 3 +-- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/backend-core/src/users/utils.ts b/packages/backend-core/src/users/utils.ts index b6bf3f0abb..af0e8e10c7 100644 --- a/packages/backend-core/src/users/utils.ts +++ b/packages/backend-core/src/users/utils.ts @@ -10,6 +10,7 @@ import { getAccountByTenantId } from "../accounts" // extract from shared-core to make easily accessible from backend-core export const isBuilder = sdk.users.isBuilder export const isAdmin = sdk.users.isAdmin +export const isGlobalBuilder = sdk.users.isGlobalBuilder export const isAdminOrBuilder = sdk.users.isAdminOrBuilder export const hasAdminPermissions = sdk.users.hasAdminPermissions export const hasBuilderPermissions = sdk.users.hasBuilderPermissions diff --git a/packages/shared-core/src/sdk/documents/users.ts b/packages/shared-core/src/sdk/documents/users.ts index 1a9314f731..92379a03ba 100644 --- a/packages/shared-core/src/sdk/documents/users.ts +++ b/packages/shared-core/src/sdk/documents/users.ts @@ -14,6 +14,10 @@ export function isBuilder(user: User | ContextUser, appId?: string) { return false } +export function isGlobalBuilder(user: User | ContextUser) { + return (isBuilder(user) && !hasAppBuilderPermissions(user)) || isAdmin(user) +} + // alias for hasAdminPermission, currently do the same thing // in future whether someone has admin permissions and whether they are // an admin for a specific resource could be separated diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index 5984f39ef8..d1e66b4ac1 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -447,17 +447,20 @@ export const grantAppBuilder = async (ctx: Ctx) => { export const addAppBuilder = async (ctx: Ctx) => { const { userId, appId } = ctx.params const user = await userSdk.db.getUser(userId) - if (!user.builder?.global || user.admin?.global) { - ctx.body = { message: "User already admin - no permissions updated." } - return - } - if (!user.builder?.appBuilder) { + if (!user.builder?.appBuilder && !userSdk.core.isGlobalBuilder(user)) { ctx.throw( 400, "Unable to update access, user must be granted app builder permissions." ) } + if (userSdk.core.isGlobalBuilder(user)) { + ctx.body = { message: "User already admin - no permissions updated." } + return + } const prodAppId = dbCore.getProdAppID(appId) + if (!user.builder) { + user.builder = {} + } if (!user.builder.apps) { user.builder.apps = [] } @@ -469,19 +472,19 @@ export const addAppBuilder = async (ctx: Ctx) => { export const removeAppBuilder = async (ctx: Ctx) => { const { userId, appId } = ctx.params const user = await userSdk.db.getUser(userId) - if (!user.builder?.global || user.admin?.global) { - ctx.body = { message: "User already admin - no permissions removed." } - return - } - if (!user.builder?.appBuilder) { + if (!user.builder?.appBuilder && !userSdk.core.isGlobalBuilder(user)) { ctx.throw( 400, "Unable to update access, user must be granted app builder permissions." ) } + if (userSdk.core.isGlobalBuilder(user)) { + ctx.body = { message: "User already admin - no permissions removed." } + return + } const prodAppId = dbCore.getProdAppID(appId) const indexOf = user.builder?.apps?.indexOf(prodAppId) - if (indexOf && indexOf !== -1) { + if (user.builder && indexOf != undefined && indexOf !== -1) { user.builder.apps = user.builder.apps!.splice(indexOf, 1) } await userSdk.db.save(user, { hashPassword: false }) diff --git a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts index 138ebbc595..83bc401759 100644 --- a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts +++ b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts @@ -48,7 +48,7 @@ describe("/api/global/users/:userId/app/builder", () => { await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) const updated = await getUser(user._id!) expect(updated.builder?.appBuilder).toBe(true) - expect(updated.builder?.apps).toBe([MOCK_APP_ID]) + expect(updated.builder?.apps![0]).toBe(MOCK_APP_ID) }) }) @@ -57,10 +57,10 @@ describe("/api/global/users/:userId/app/builder", () => { const user = await grantAppBuilder() await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) let updated = await getUser(user._id!) - expect(updated.builder?.apps).toBe([MOCK_APP_ID]) + expect(updated.builder?.apps![0]).toBe(MOCK_APP_ID) await config.api.users.revokeBuilderToApp(user._id!, MOCK_APP_ID) updated = await getUser(user._id!) - expect(updated.builder?.apps).toBe([]) + expect(updated.builder?.apps!.length).toBe(0) }) }) }) diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index 605ac79416..bafc157e69 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -163,8 +163,7 @@ export class UserAPI extends TestAPI { revokeBuilderToApp = ( userId: string, - appId: string, - statusCode: number = 200 + appId: string ) => { return this.request .delete(`/api/global/users/${userId}/app/${appId}/builder`) From 64a5426d360c5ab29801c6f777d6c33bb57e9165 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 26 Jul 2023 17:32:21 +0100 Subject: [PATCH 011/117] Updates to remove app builder concept, denying access to app creation for app builders. --- .../backend-core/src/security/permissions.ts | 26 ++++--------------- packages/server/src/api/routes/application.ts | 2 +- packages/server/src/middleware/authorized.ts | 17 +++++++----- packages/server/src/middleware/builder.ts | 2 +- packages/types/src/documents/global/user.ts | 1 - packages/types/src/sdk/index.ts | 1 + packages/types/src/sdk/permissions.ts | 19 ++++++++++++++ .../src/api/controllers/global/users.ts | 23 ---------------- .../routes/global/tests/appBuilder.spec.ts | 22 ++-------------- .../worker/src/api/routes/global/users.ts | 3 +-- packages/worker/src/tests/api/users.ts | 15 ++--------- 11 files changed, 42 insertions(+), 89 deletions(-) create mode 100644 packages/types/src/sdk/permissions.ts diff --git a/packages/backend-core/src/security/permissions.ts b/packages/backend-core/src/security/permissions.ts index 6cacc12dd6..0edab188d9 100644 --- a/packages/backend-core/src/security/permissions.ts +++ b/packages/backend-core/src/security/permissions.ts @@ -1,29 +1,12 @@ -const { flatten } = require("lodash") -const { cloneDeep } = require("lodash/fp") +import { flatten } from "lodash" +import { cloneDeep } from "lodash/fp" +import { PermissionType, PermissionLevel } from "@budibase/types" +export { PermissionType, PermissionLevel } from "@budibase/types" export type RoleHierarchy = { permissionId: string }[] -export enum PermissionLevel { - READ = "read", - WRITE = "write", - EXECUTE = "execute", - ADMIN = "admin", -} - -// these are the global types, that govern the underlying default behaviour -export enum PermissionType { - APP = "app", - TABLE = "table", - USER = "user", - AUTOMATION = "automation", - WEBHOOK = "webhook", - BUILDER = "builder", - VIEW = "view", - QUERY = "query", -} - export class Permission { type: PermissionType level: PermissionLevel @@ -173,3 +156,4 @@ export function isPermissionLevelHigherThanRead(level: PermissionLevel) { // utility as a lot of things need simply the builder permission export const BUILDER = PermissionType.BUILDER +export const GLOBAL_BUILDER = PermissionType.GLOBAL_BUILDER diff --git a/packages/server/src/api/routes/application.ts b/packages/server/src/api/routes/application.ts index 0c1fa364ff..04d5f67b96 100644 --- a/packages/server/src/api/routes/application.ts +++ b/packages/server/src/api/routes/application.ts @@ -15,7 +15,7 @@ router ) .post( "/api/applications", - authorized(permissions.BUILDER), + authorized(permissions.GLOBAL_BUILDER), applicationValidator(), controller.create ) diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index dba5d47cb9..767c3d95ef 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -5,7 +5,7 @@ import { context, users, } from "@budibase/backend-core" -import { Role, UserCtx } from "@budibase/types" +import { Role, UserCtx, PermissionType, PermissionLevel } from "@budibase/types" import builderMiddleware from "./builder" import { isWebhookEndpoint } from "./utils" @@ -24,8 +24,8 @@ const csrf = auth.buildCsrfMiddleware() const checkAuthorized = async ( ctx: UserCtx, resourceRoles: any, - permType: any, - permLevel: any + permType: PermissionType, + permLevel: PermissionLevel ) => { const appId = context.getAppId() // check if this is a builder api and the user is not a builder @@ -47,10 +47,10 @@ const checkAuthorized = async ( } const checkAuthorizedResource = async ( - ctx: any, + ctx: UserCtx, resourceRoles: any, - permType: any, - permLevel: any + permType: PermissionType, + permLevel: PermissionLevel ) => { // get the user's roles const roleId = ctx.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC @@ -122,7 +122,10 @@ export default ( // check general builder stuff, this middleware is a good way // to find API endpoints which are builder focused - if (permType === permissions.PermissionType.BUILDER) { + if ( + permType === permissions.PermissionType.BUILDER || + permType === permissions.PermissionType.GLOBAL_BUILDER + ) { await builderMiddleware(ctx) } diff --git a/packages/server/src/middleware/builder.ts b/packages/server/src/middleware/builder.ts index 881ec843a4..7df135c86a 100644 --- a/packages/server/src/middleware/builder.ts +++ b/packages/server/src/middleware/builder.ts @@ -10,7 +10,7 @@ import { setDebounce, } from "../utilities/redis" import { db as dbCore, cache } from "@budibase/backend-core" -import { UserCtx, Database, App } from "@budibase/types" +import { UserCtx, Database } from "@budibase/types" const DEBOUNCE_TIME_SEC = 30 diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index 3249660624..2ce714801d 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -43,7 +43,6 @@ export interface User extends Document { roles: UserRoles builder?: { global?: boolean - appBuilder?: boolean apps?: string[] } admin?: { diff --git a/packages/types/src/sdk/index.ts b/packages/types/src/sdk/index.ts index 49d0387a82..e4b5778ed9 100644 --- a/packages/types/src/sdk/index.ts +++ b/packages/types/src/sdk/index.ts @@ -18,3 +18,4 @@ export * from "./sso" export * from "./user" export * from "./cli" export * from "./websocket" +export * from "./permissions" diff --git a/packages/types/src/sdk/permissions.ts b/packages/types/src/sdk/permissions.ts new file mode 100644 index 0000000000..9e51e4c7b4 --- /dev/null +++ b/packages/types/src/sdk/permissions.ts @@ -0,0 +1,19 @@ +export enum PermissionLevel { + READ = "read", + WRITE = "write", + EXECUTE = "execute", + ADMIN = "admin", +} + +// these are the global types, that govern the underlying default behaviour +export enum PermissionType { + APP = "app", + TABLE = "table", + USER = "user", + AUTOMATION = "automation", + WEBHOOK = "webhook", + BUILDER = "builder", + GLOBAL_BUILDER = "globalBuilder", + VIEW = "view", + QUERY = "query", +} diff --git a/packages/worker/src/api/controllers/global/users.ts b/packages/worker/src/api/controllers/global/users.ts index d1e66b4ac1..99da08a5b8 100644 --- a/packages/worker/src/api/controllers/global/users.ts +++ b/packages/worker/src/api/controllers/global/users.ts @@ -433,26 +433,9 @@ export const inviteAccept = async ( } } -export const grantAppBuilder = async (ctx: Ctx) => { - const { userId } = ctx.params - const user = await userSdk.db.getUser(userId) - if (!user.builder) { - user.builder = {} - } - user.builder.appBuilder = true - await userSdk.db.save(user, { hashPassword: false }) - ctx.body = { message: `User "${user.email}" granted app builder permissions` } -} - export const addAppBuilder = async (ctx: Ctx) => { const { userId, appId } = ctx.params const user = await userSdk.db.getUser(userId) - if (!user.builder?.appBuilder && !userSdk.core.isGlobalBuilder(user)) { - ctx.throw( - 400, - "Unable to update access, user must be granted app builder permissions." - ) - } if (userSdk.core.isGlobalBuilder(user)) { ctx.body = { message: "User already admin - no permissions updated." } return @@ -472,12 +455,6 @@ export const addAppBuilder = async (ctx: Ctx) => { export const removeAppBuilder = async (ctx: Ctx) => { const { userId, appId } = ctx.params const user = await userSdk.db.getUser(userId) - if (!user.builder?.appBuilder && !userSdk.core.isGlobalBuilder(user)) { - ctx.throw( - 400, - "Unable to update access, user must be granted app builder permissions." - ) - } if (userSdk.core.isGlobalBuilder(user)) { ctx.body = { message: "User already admin - no permissions removed." } return diff --git a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts index 83bc401759..22039471b4 100644 --- a/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts +++ b/packages/worker/src/api/routes/global/tests/appBuilder.spec.ts @@ -24,27 +24,9 @@ describe("/api/global/users/:userId/app/builder", () => { return response.body as User } - async function grantAppBuilder(): Promise { - const user = await newUser() - await config.api.users.grantAppBuilder(user._id!) - return await getUser(user._id!) - } - - describe("POST /api/global/users/:userId/app/builder", () => { - it("should be able to grant a user builder permissions", async () => { - const user = await grantAppBuilder() - expect(user.builder?.appBuilder).toBe(true) - }) - }) - describe("PATCH /api/global/users/:userId/app/:appId/builder", () => { - it("shouldn't allow granting access to an app to a non-app builder", async () => { - const user = await newUser() - await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID, 400) - }) - it("should be able to grant a user access to a particular app", async () => { - const user = await grantAppBuilder() + const user = await newUser() await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) const updated = await getUser(user._id!) expect(updated.builder?.appBuilder).toBe(true) @@ -54,7 +36,7 @@ describe("/api/global/users/:userId/app/builder", () => { describe("DELETE /api/global/users/:userId/app/:appId/builder", () => { it("should allow revoking access", async () => { - const user = await grantAppBuilder() + const user = await newUser() await config.api.users.grantBuilderToApp(user._id!, MOCK_APP_ID) let updated = await getUser(user._id!) expect(updated.builder?.apps![0]).toBe(MOCK_APP_ID) diff --git a/packages/worker/src/api/routes/global/users.ts b/packages/worker/src/api/routes/global/users.ts index 9c1b5d9acb..348939a2e7 100644 --- a/packages/worker/src/api/routes/global/users.ts +++ b/packages/worker/src/api/routes/global/users.ts @@ -122,8 +122,7 @@ router buildAdminInitValidation(), controller.adminUser ) - .post("/api/global/users/:userId/app/builder", controller.grantAppBuilder) - .patch( + .post( "/api/global/users/:userId/app/:appId/builder", controller.addAppBuilder ) diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index bafc157e69..d9f6595f1d 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -141,30 +141,19 @@ export class UserAPI extends TestAPI { .expect(opts?.status ? opts.status : 200) } - grantAppBuilder = (userId: string) => { - return this.request - .post(`/api/global/users/${userId}/app/builder`) - .set(this.config.defaultHeaders()) - .expect("Content-Type", /json/) - .expect(200) - } - grantBuilderToApp = ( userId: string, appId: string, statusCode: number = 200 ) => { return this.request - .patch(`/api/global/users/${userId}/app/${appId}/builder`) + .post(`/api/global/users/${userId}/app/${appId}/builder`) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) .expect(statusCode) } - revokeBuilderToApp = ( - userId: string, - appId: string - ) => { + revokeBuilderToApp = (userId: string, appId: string) => { return this.request .delete(`/api/global/users/${userId}/app/${appId}/builder`) .set(this.config.defaultHeaders()) From c2793ede4c6c9d5970c06a6c8f35e3f75acf2f05 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 26 Jul 2023 17:48:35 +0100 Subject: [PATCH 012/117] Properly supporting the new global builder permission type to deny access to app creation. --- packages/server/src/middleware/authorized.ts | 39 ++++++++++---------- packages/types/src/sdk/koa.ts | 1 + 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index 767c3d95ef..915344f747 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -1,11 +1,11 @@ import { - roles, - permissions, auth, context, + permissions, + roles, users, } from "@budibase/backend-core" -import { Role, UserCtx, PermissionType, PermissionLevel } from "@budibase/types" +import { PermissionLevel, PermissionType, Role, UserCtx } from "@budibase/types" import builderMiddleware from "./builder" import { isWebhookEndpoint } from "./utils" @@ -28,15 +28,14 @@ const checkAuthorized = async ( permLevel: PermissionLevel ) => { const appId = context.getAppId() + const isGlobalBuilderApi = permType === PermissionType.GLOBAL_BUILDER + const isBuilderApi = permType === PermissionType.BUILDER + const globalBuilder = users.isGlobalBuilder(ctx.user) + let isBuilder = appId + ? users.isBuilder(ctx.user, appId) + : users.hasBuilderPermissions(ctx.user) // check if this is a builder api and the user is not a builder - let isBuilder - if (!appId) { - isBuilder = users.hasBuilderPermissions(ctx.user) - } else { - isBuilder = users.isBuilder(ctx.user, appId) - } - const isBuilderApi = permType === permissions.PermissionType.BUILDER - if (isBuilderApi && !isBuilder) { + if ((isGlobalBuilderApi && !globalBuilder) || (isBuilderApi && !isBuilder)) { return ctx.throw(403, "Not Authorized") } @@ -76,8 +75,8 @@ const checkAuthorizedResource = async ( } export default ( - permType: any, - permLevel: any = null, + permType: PermissionType, + permLevel?: PermissionLevel, opts = { schema: false } ) => async (ctx: any, next: any) => { @@ -95,12 +94,12 @@ export default ( let resourceRoles: any = [] let otherLevelRoles: any = [] const otherLevel = - permLevel === permissions.PermissionLevel.READ - ? permissions.PermissionLevel.WRITE - : permissions.PermissionLevel.READ + permLevel === PermissionLevel.READ + ? PermissionLevel.WRITE + : PermissionLevel.READ const appId = context.getAppId() if (appId && hasResource(ctx)) { - resourceRoles = await roles.getRequiredResourceRole(permLevel, ctx) + resourceRoles = await roles.getRequiredResourceRole(permLevel!, ctx) if (opts && opts.schema) { otherLevelRoles = await roles.getRequiredResourceRole(otherLevel, ctx) } @@ -123,15 +122,15 @@ export default ( // check general builder stuff, this middleware is a good way // to find API endpoints which are builder focused if ( - permType === permissions.PermissionType.BUILDER || - permType === permissions.PermissionType.GLOBAL_BUILDER + permType === PermissionType.BUILDER || + permType === PermissionType.GLOBAL_BUILDER ) { await builderMiddleware(ctx) } try { // check authorized - await checkAuthorized(ctx, resourceRoles, permType, permLevel) + await checkAuthorized(ctx, resourceRoles, permType, permLevel!) } catch (err) { // this is a schema, check if if (opts && opts.schema && permLevel) { diff --git a/packages/types/src/sdk/koa.ts b/packages/types/src/sdk/koa.ts index f824b73458..861f5e9329 100644 --- a/packages/types/src/sdk/koa.ts +++ b/packages/types/src/sdk/koa.ts @@ -38,6 +38,7 @@ export interface Ctx extends Context { export interface UserCtx extends Ctx { user: ContextUser + roleId?: string } /** From eefac0fe088a7532c5213f3a6acf136bd8b434e7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 26 Jul 2023 18:13:18 +0100 Subject: [PATCH 013/117] Quick updates based on latest lerna version, as well as some fixes after running through flows (post refactoring). --- package.json | 4 +- .../pages/builder/portal/apps/index.svelte | 53 ++++++++++--------- .../worker/src/api/controllers/global/auth.ts | 8 +-- .../worker/src/api/controllers/global/self.ts | 6 +-- .../src/api/routes/global/tests/auth.spec.ts | 6 +-- .../src/api/routes/global/tests/users.spec.ts | 2 +- packages/worker/src/initPro.ts | 4 +- packages/worker/src/sdk/auth/auth.ts | 4 +- 8 files changed, 45 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 6a678f1bf3..2173e91055 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "kill-builder": "kill-port 3000", "kill-server": "kill-port 4001 4002", "kill-all": "yarn run kill-builder && yarn run kill-server", - "dev": "yarn run kill-all && yarn nx run-many --target=dev:builder", - "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && yarn nx run-many --target=dev:builder --exclude=@budibase/backend-core,@budibase/server,@budibase/worker", + "dev": "yarn run kill-all && yarn nx run-many --target=dev:builder --parallel=10", + "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && yarn nx run-many --target=dev:builder --parallel=10 --exclude=@budibase/backend-core,@budibase/server,@budibase/worker", "dev:server": "yarn run kill-server && yarn nx run-many --target=dev:builder --projects=@budibase/worker,@budibase/server", "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built", "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index dc5ee25688..00991dd39a 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -15,6 +15,7 @@ import CreateAppModal from "components/start/CreateAppModal.svelte" import AppLimitModal from "components/portal/licensing/AppLimitModal.svelte" import AccountLockedModal from "components/portal/licensing/AccountLockedModal.svelte" + import { sdk } from "@budibase/shared-core" import { store, automationStore } from "builderStore" import { API } from "api" @@ -237,35 +238,37 @@ {#if enrichedApps.length}
-
- - {#if $apps?.length > 0 && !$admin.offlineMode} + {#if sdk.isGlobalBuilder($auth.user)} +
- {/if} - {#if !$apps?.length} - - {/if} -
+ {#if $apps?.length > 0 && !$admin.offlineMode} + + {/if} + {#if !$apps?.length} + + {/if} +
+ {/if} {#if enrichedApps.length > 1}
- -
- {/if} -
- -
- {#each filteredApps as app (app.appId)} - - {/each} -
-
- {/if} - - {#if creatingFromTemplate} -
- -

Creating your Budibase app from your selected template...

- + {/if} + {#if !$apps?.length} + + {/if} +
+ {/if} + {#if enrichedApps.length > 1} +
+ - + Remove diff --git a/packages/builder/src/pages/builder/portal/users/users/index.svelte b/packages/builder/src/pages/builder/portal/users/users/index.svelte index fa1c5fc8ee..e1fc0ca7eb 100644 --- a/packages/builder/src/pages/builder/portal/users/users/index.svelte +++ b/packages/builder/src/pages/builder/portal/users/users/index.svelte @@ -39,6 +39,7 @@ import { API } from "api" import { OnboardingType } from "../../../../../constants" import ScimBanner from "../_components/SCIMBanner.svelte" + import { sdk } from "@budibase/shared-core" const fetch = fetchData({ API, @@ -66,7 +67,7 @@ let userData = [] $: isOwner = $auth.accountPortalAccess && $admin.cloud - $: readonly = !$auth.isAdmin || $features.isScimEnabled + $: readonly = !sdk.users.isAdmin($auth.user) || $features.isScimEnabled $: debouncedUpdateFetch(searchEmail) $: schema = { diff --git a/packages/builder/src/stores/portal/auth.js b/packages/builder/src/stores/portal/auth.js index 40113e2f76..8caf541f98 100644 --- a/packages/builder/src/stores/portal/auth.js +++ b/packages/builder/src/stores/portal/auth.js @@ -14,13 +14,6 @@ export function createAuthStore() { postLogout: false, }) const store = derived(auth, $store => { - let isAdmin = false - let isBuilder = false - if ($store.user) { - const user = $store.user - isAdmin = sdk.users.isAdmin(user) - isBuilder = sdk.users.isBuilder(user) - } return { user: $store.user, accountPortalAccess: $store.accountPortalAccess, @@ -28,8 +21,6 @@ export function createAuthStore() { tenantSet: $store.tenantSet, loaded: $store.loaded, postLogout: $store.postLogout, - isAdmin, - isBuilder, isSSO: !!$store.user?.provider, } }) From 202963c864abfc09d0991a7420d306580f6d8eaa Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 12:34:50 +0200 Subject: [PATCH 038/117] Fix type tests --- .../worker/src/api/routes/global/tests/auth.spec.ts | 2 +- .../worker/src/api/routes/global/tests/self.spec.ts | 4 ++-- .../worker/src/api/routes/global/tests/users.spec.ts | 2 +- .../worker/src/api/routes/system/tests/status.spec.ts | 11 +++++++---- packages/worker/src/tests/mocks/index.ts | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/worker/src/api/routes/global/tests/auth.spec.ts b/packages/worker/src/api/routes/global/tests/auth.spec.ts index 5e62b2123f..47533a13d0 100644 --- a/packages/worker/src/api/routes/global/tests/auth.spec.ts +++ b/packages/worker/src/api/routes/global/tests/auth.spec.ts @@ -206,7 +206,7 @@ describe("/api/global/auth", () => { const newPassword = "newpassword" const res = await config.api.auth.updatePassword(code!, newPassword) - user = await config.getUser(user.email) + user = (await config.getUser(user.email))! delete user.password expect(res.body).toEqual({ message: "password reset successfully." }) diff --git a/packages/worker/src/api/routes/global/tests/self.spec.ts b/packages/worker/src/api/routes/global/tests/self.spec.ts index f3959c7521..274efb0aff 100644 --- a/packages/worker/src/api/routes/global/tests/self.spec.ts +++ b/packages/worker/src/api/routes/global/tests/self.spec.ts @@ -36,7 +36,7 @@ describe("/api/global/self", () => { }) .expect(200) - const dbUser = await config.getUser(user.email) + const dbUser = (await config.getUser(user.email))! user._rev = dbUser._rev user.dayPassRecordedAt = mocks.date.MOCK_DATE.toISOString() @@ -58,7 +58,7 @@ describe("/api/global/self", () => { }) .expect(200) - const dbUser = await config.getUser(user.email) + const dbUser = (await config.getUser(user.email))! user._rev = dbUser._rev user.dayPassRecordedAt = mocks.date.MOCK_DATE.toISOString() diff --git a/packages/worker/src/api/routes/global/tests/users.spec.ts b/packages/worker/src/api/routes/global/tests/users.spec.ts index 52d77cbae6..df9c19f8ca 100644 --- a/packages/worker/src/api/routes/global/tests/users.spec.ts +++ b/packages/worker/src/api/routes/global/tests/users.spec.ts @@ -66,7 +66,7 @@ describe("/api/global/users", () => { expect(res.body._id).toBeDefined() const user = await config.getUser(email) expect(user).toBeDefined() - expect(user._id).toEqual(res.body._id) + expect(user!._id).toEqual(res.body._id) expect(events.user.inviteAccepted).toBeCalledTimes(1) expect(events.user.inviteAccepted).toBeCalledWith(user) }) diff --git a/packages/worker/src/api/routes/system/tests/status.spec.ts b/packages/worker/src/api/routes/system/tests/status.spec.ts index fe0ff13551..48a0fc005d 100644 --- a/packages/worker/src/api/routes/system/tests/status.spec.ts +++ b/packages/worker/src/api/routes/system/tests/status.spec.ts @@ -1,3 +1,4 @@ +import { HealthStatusResponse } from "@budibase/types" import { TestConfiguration } from "../../../../tests" import { accounts as _accounts } from "@budibase/backend-core" const accounts = jest.mocked(_accounts) @@ -31,13 +32,15 @@ describe("/api/system/status", () => { }) it("returns status in cloud", async () => { - const value = { - health: { - passing: false, + const value: HealthStatusResponse = { + passing: false, + checks: { + login: false, + search: false, }, } - accounts.getStatus.mockReturnValueOnce(Promise.resolve(value)) + accounts.getStatus.mockResolvedValue(value) const res = await config.api.status.getStatus() diff --git a/packages/worker/src/tests/mocks/index.ts b/packages/worker/src/tests/mocks/index.ts index cab019bb46..917e5f9ffc 100644 --- a/packages/worker/src/tests/mocks/index.ts +++ b/packages/worker/src/tests/mocks/index.ts @@ -2,7 +2,7 @@ import * as email from "./email" import { mocks } from "@budibase/backend-core/tests" import * as _pro from "@budibase/pro" -const pro = jest.mocked(_pro, true) +const pro = jest.mocked(_pro, { shallow: false }) export default { email, From 96c598688f73a327635e9f51d969ee48b5f9a75c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 12:35:17 +0200 Subject: [PATCH 039/117] Run tests checks for worker --- packages/worker/package.json | 2 +- packages/worker/tsconfig.json | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/worker/package.json b/packages/worker/package.json index 3767100925..a1b9e68878 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -14,7 +14,7 @@ "scripts": { "prebuild": "rimraf dist/", "build": "node ../../scripts/build.js", - "check:types": "tsc -p tsconfig.build.json --noEmit", + "check:types": "tsc -p tsconfig.json --noEmit --paths null", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", "run:docker": "node dist/index.js", "debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js", diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 147ef1f700..4f3d5cbac8 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -2,14 +2,12 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": true, - "declaration": true, - "sourceMap": true, "baseUrl": "." }, "ts-node": { "require": ["tsconfig-paths/register"], "swc": true }, - "include": ["src/**/*"], - "exclude": ["dist"] + "include": ["src/**/*", "__mocks__/**/*"], + "exclude": ["node_modules", "dist"] } From b3a110e609cbf54c33b6f74ccc59fcb19aad3420 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 12:36:32 +0200 Subject: [PATCH 040/117] Run tests checks for server --- packages/server/package.json | 2 +- packages/server/tsconfig.json | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 4145caf0ca..ba663df9c3 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -11,7 +11,7 @@ "scripts": { "prebuild": "rimraf dist/", "build": "node ./scripts/build.js", - "check:types": "tsc -p tsconfig.build.json --noEmit", + "check:types": "tsc -p tsconfig.json --noEmit --paths null", "postbuild": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", "debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js", diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index 6e88b99c34..d9f84f6a5e 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -2,15 +2,12 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": true, - "declaration": true, - "sourceMap": true, - "baseUrl": ".", - "outDir": "dist" + "baseUrl": "." }, "ts-node": { "require": ["tsconfig-paths/register"], "swc": true }, - "include": ["src/**/*", "specs"], + "include": ["src/**/*", "specs", "__mocks__"], "exclude": ["node_modules", "dist"] } From f4675aab6e8d812e7beb3373de1911c17e76e4ef Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 12:49:50 +0200 Subject: [PATCH 041/117] Fix import --- packages/server/src/integrations/tests/googlesheets.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index fcb24c152a..748baddc39 100644 --- a/packages/server/src/integrations/tests/googlesheets.spec.ts +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -30,7 +30,7 @@ GoogleSpreadsheet.mockImplementation(() => mockGoogleIntegration) import { structures } from "@budibase/backend-core/tests" import TestConfiguration from "../../tests/utilities/TestConfiguration" import GoogleSheetsIntegration from "../googlesheets" -import { FieldType, Table, TableSchema } from "../../../../types/src/documents" +import { FieldType, Table, TableSchema } from "@budibase/types" describe("Google Sheets Integration", () => { let integration: any, From c6c1450d06c7378171dfdca0d86b4146396d749e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 12:52:00 +0200 Subject: [PATCH 042/117] Fixe types --- .../server/src/utilities/rowProcessor/tests/utils.spec.ts | 7 ++++--- packages/server/src/utilities/rowProcessor/utils.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/server/src/utilities/rowProcessor/tests/utils.spec.ts b/packages/server/src/utilities/rowProcessor/tests/utils.spec.ts index a9ab59c15a..68b62a3291 100644 --- a/packages/server/src/utilities/rowProcessor/tests/utils.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/utils.spec.ts @@ -1,18 +1,19 @@ import { fixAutoColumnSubType } from "../utils" import { AutoFieldDefaultNames, AutoFieldSubTypes } from "../../../constants" +import { FieldSchema, FieldType, RelationshipType } from "@budibase/types" describe("rowProcessor utility", () => { describe("fixAutoColumnSubType", () => { - let schema = { + let schema: FieldSchema = { name: "", - type: "link", + type: FieldType.LINK, subtype: "", // missing subtype icon: "ri-magic-line", autocolumn: true, constraints: { type: "array", presence: false }, tableId: "ta_users", fieldName: "test-Updated By", - relationshipType: "many-to-many", + relationshipType: RelationshipType.MANY_TO_MANY, sortable: false, } diff --git a/packages/server/src/utilities/rowProcessor/utils.ts b/packages/server/src/utilities/rowProcessor/utils.ts index 66ca969663..0d7eace369 100644 --- a/packages/server/src/utilities/rowProcessor/utils.ts +++ b/packages/server/src/utilities/rowProcessor/utils.ts @@ -5,7 +5,7 @@ import { FormulaTypes, } from "../../constants" import { processStringSync } from "@budibase/string-templates" -import { FieldSchema, FieldType, Row, Table } from "@budibase/types" +import { FieldSchema, Row, Table } from "@budibase/types" /** * If the subtype has been lost for any reason this works out what From ff7120cf7aebde8419269f4edbe2a0b04605a41b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 12:57:44 +0200 Subject: [PATCH 043/117] Fix types on tests --- packages/server/src/automations/tests/openai.spec.ts | 2 +- packages/server/src/migrations/tests/index.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/automations/tests/openai.spec.ts b/packages/server/src/automations/tests/openai.spec.ts index 31f7e48305..07ac73a7c8 100644 --- a/packages/server/src/automations/tests/openai.spec.ts +++ b/packages/server/src/automations/tests/openai.spec.ts @@ -68,7 +68,7 @@ describe("test the openai action", () => { }) it("should present the correct error message when an error is thrown from the createChatCompletion call", async () => { - openai.OpenAIApi.mockImplementation(() => ({ + ;(openai.OpenAIApi as any).mockImplementation(() => ({ createChatCompletion: jest.fn(() => { throw new Error("An error occurred while calling createChatCompletion") }), diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts index b0fd971f42..2465c930b4 100644 --- a/packages/server/src/migrations/tests/index.spec.ts +++ b/packages/server/src/migrations/tests/index.spec.ts @@ -17,7 +17,7 @@ tk.freeze(timestamp) const clearMigrations = async () => { const dbs = [context.getDevAppDB(), context.getProdAppDB()] for (const db of dbs) { - const doc = await db.get(DocumentType.MIGRATIONS) + const doc = await db.get(DocumentType.MIGRATIONS) const newDoc = { _id: doc._id, _rev: doc._rev } await db.put(newDoc) } @@ -52,7 +52,7 @@ describe("migrations", () => { await config.createTable() await config.createView() await config.createTable() - await config.createView(structures.view(config.table._id)) + await config.createView(structures.view(config.table!._id!)) await config.createScreen() await config.createScreen() From 8b693a6fa34fbf54f64daf1506ed333a0e64f2c2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 13:08:09 +0200 Subject: [PATCH 044/117] Check types on backend-core --- packages/backend-core/package.json | 1 + .../src/cache/tests/writethrough.spec.ts | 14 +++++++------- packages/backend-core/tsconfig.json | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 345762348c..5883a53387 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -16,6 +16,7 @@ "prepack": "cp package.json dist", "build": "tsc -p tsconfig.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", + "check:types": "tsc -p tsconfig.json --noEmit --paths null", "test": "bash scripts/test.sh", "test:watch": "jest --watchAll" }, diff --git a/packages/backend-core/src/cache/tests/writethrough.spec.ts b/packages/backend-core/src/cache/tests/writethrough.spec.ts index 92b073ed64..97d3ece7a6 100644 --- a/packages/backend-core/src/cache/tests/writethrough.spec.ts +++ b/packages/backend-core/src/cache/tests/writethrough.spec.ts @@ -36,7 +36,7 @@ describe("writethrough", () => { _id: docId, value: 1, }) - const output = await db.get(response.id) + const output = await db.get(response.id) current = output expect(output.value).toBe(1) }) @@ -45,7 +45,7 @@ describe("writethrough", () => { it("second put shouldn't update DB", async () => { await config.doInTenant(async () => { const response = await writethrough.put({ ...current, value: 2 }) - const output = await db.get(response.id) + const output = await db.get(response.id) expect(current._rev).toBe(output._rev) expect(output.value).toBe(1) }) @@ -55,7 +55,7 @@ describe("writethrough", () => { await config.doInTenant(async () => { tk.freeze(Date.now() + DELAY + 1) const response = await writethrough.put({ ...current, value: 3 }) - const output = await db.get(response.id) + const output = await db.get(response.id) expect(response.rev).not.toBe(current._rev) expect(output.value).toBe(3) @@ -79,7 +79,7 @@ describe("writethrough", () => { expect.arrayContaining([current._rev, current._rev, newRev]) ) - const output = await db.get(current._id) + const output = await db.get(current._id) expect(output.value).toBe(4) expect(output._rev).toBe(newRev) @@ -107,7 +107,7 @@ describe("writethrough", () => { }) expect(res.ok).toBe(true) - const output = await db.get(id) + const output = await db.get(id) expect(output.value).toBe(3) expect(output._rev).toBe(res.rev) }) @@ -130,8 +130,8 @@ describe("writethrough", () => { const resp2 = await writethrough2.put({ _id: "db1", value: "second" }) expect(resp1.rev).toBeDefined() expect(resp2.rev).toBeDefined() - expect((await db.get("db1")).value).toBe("first") - expect((await db2.get("db1")).value).toBe("second") + expect((await db.get("db1")).value).toBe("first") + expect((await db2.get("db1")).value).toBe("second") }) }) }) diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 2b1419b051..2993ff84ed 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -7,6 +7,5 @@ "@budibase/types": ["../types/src"] } }, - "exclude": ["node_modules", "dist"] } From 9b9081d43715a7348da6e658bfb62f98de059db3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 13:11:04 +0200 Subject: [PATCH 045/117] Check types on cli --- packages/backend-core/tests/core/utilities/mocks/events.ts | 2 -- packages/cli/package.json | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend-core/tests/core/utilities/mocks/events.ts b/packages/backend-core/tests/core/utilities/mocks/events.ts index 81de1f8175..fef730768a 100644 --- a/packages/backend-core/tests/core/utilities/mocks/events.ts +++ b/packages/backend-core/tests/core/utilities/mocks/events.ts @@ -1,5 +1,3 @@ -import * as events from "../../../../src/events" - beforeAll(async () => { const processors = await import("../../../../src/events/processors") const events = await import("../../../../src/events") diff --git a/packages/cli/package.json b/packages/cli/package.json index 8e6c612478..8e00d2cd91 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -14,6 +14,7 @@ "tsc": "tsc -p tsconfig.build.json", "pkg": "pkg . --out-path build --no-bytecode --public --public-packages \"*\" -C GZip", "build": "yarn prebuild && yarn rename && yarn tsc && yarn pkg && yarn postbuild", + "check:types": "tsc -p tsconfig.json --noEmit --paths null", "postbuild": "rm -rf prebuilds 2> /dev/null" }, "pkg": { From 27517fc6c8cdcc7af25f2f6c59d058fd90a113ad Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 13:22:58 +0200 Subject: [PATCH 046/117] Update pro --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 63fa1b15f6..7a1a21c8d7 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 63fa1b15f6e2afa8a264d597157fd798c9ce031c +Subproject commit 7a1a21c8d794be3f174eaa186ca33b9505467cda From 5bea5bf97433813ea106b6ed76eaff3424bfc49a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 13:24:55 +0200 Subject: [PATCH 047/117] Check types --- packages/shared-core/package.json | 3 ++- packages/shared-core/tsconfig-base.build.json | 5 ++++- packages/shared-core/tsconfig.json | 9 ++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index b51f9732a0..f79075eb2e 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -17,7 +17,8 @@ "prebuild": "rimraf dist/", "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", - "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" + "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"", + "check:types": "tsc -p tsconfig.json --noEmit --paths null" }, "dependencies": { "@budibase/types": "0.0.0" diff --git a/packages/shared-core/tsconfig-base.build.json b/packages/shared-core/tsconfig-base.build.json index 6930e3cb99..31dc1afc10 100644 --- a/packages/shared-core/tsconfig-base.build.json +++ b/packages/shared-core/tsconfig-base.build.json @@ -12,7 +12,10 @@ "declaration": true, "types": ["node"], "outDir": "dist", - "skipLibCheck": true + "skipLibCheck": true, + "paths": { + "@budibase/types": ["../types/src"] + } }, "include": ["**/*.js", "**/*.ts"], "exclude": [ diff --git a/packages/shared-core/tsconfig.json b/packages/shared-core/tsconfig.json index ebfec9c3a5..52e07ddb5c 100644 --- a/packages/shared-core/tsconfig.json +++ b/packages/shared-core/tsconfig.json @@ -2,9 +2,8 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": true, - "baseUrl": ".", - "paths": { - "@budibase/types": ["../types/src"] - } - } + "baseUrl": "." + }, + "include": ["**/*.js", "**/*.ts"], + "exclude": ["node_modules", "dist"] } From 672666d96b4c8eca42ee79128b8f496e43a6b5b1 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 13:26:04 +0200 Subject: [PATCH 048/117] Check types --- packages/types/package.json | 3 ++- packages/types/tsconfig.json | 7 ++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/types/package.json b/packages/types/package.json index 69eaa8b6b5..c419a4d527 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -16,7 +16,8 @@ "prebuild": "rimraf dist/", "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", - "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" + "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"", + "check:types": "tsc -p tsconfig.json --noEmit --paths null" }, "jest": {}, "devDependencies": { diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index 8342b99304..ad5356c2dc 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -3,8 +3,5 @@ "compilerOptions": { "composite": true }, - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "exclude": ["node_modules", "dist"] +} From 4e1aa4fbbdde802d913ebbcd678238c614d35e64 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 1 Aug 2023 12:33:59 +0100 Subject: [PATCH 049/117] Final PR comment. --- packages/server/src/sdk/app/applications/applications.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/server/src/sdk/app/applications/applications.ts b/packages/server/src/sdk/app/applications/applications.ts index e89aba5f48..865b277504 100644 --- a/packages/server/src/sdk/app/applications/applications.ts +++ b/packages/server/src/sdk/app/applications/applications.ts @@ -16,13 +16,7 @@ export function filterAppList(user: ContextUser, apps: App[]) { } else { return apps } - const finalApps: App[] = [] - for (let app of apps) { - if (appList.includes(dbCore.getProdAppID(app.appId))) { - finalApps.push(app) - } - } - return finalApps + return apps.filter(app => appList.includes(dbCore.getProdAppID(app.appId))) } export async function fetch(status: AppStatus, user: ContextUser) { From 559f52d9041fe818c769e5dc53ed1aad07596acf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 13:36:38 +0200 Subject: [PATCH 050/117] Lint --- .../src/automations/tests/openai.spec.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/server/src/automations/tests/openai.spec.ts b/packages/server/src/automations/tests/openai.spec.ts index 07ac73a7c8..88cb63b95b 100644 --- a/packages/server/src/automations/tests/openai.spec.ts +++ b/packages/server/src/automations/tests/openai.spec.ts @@ -22,6 +22,10 @@ jest.mock( })) ) +const mockedOpenAIApi = openai.OpenAIApi as jest.MockedClass< + typeof openai.OpenAIApi +> + const OPENAI_PROMPT = "What is the meaning of life?" describe("test the openai action", () => { @@ -68,11 +72,16 @@ describe("test the openai action", () => { }) it("should present the correct error message when an error is thrown from the createChatCompletion call", async () => { - ;(openai.OpenAIApi as any).mockImplementation(() => ({ - createChatCompletion: jest.fn(() => { - throw new Error("An error occurred while calling createChatCompletion") - }), - })) + mockedOpenAIApi.mockImplementation( + () => + ({ + createChatCompletion: jest.fn(() => { + throw new Error( + "An error occurred while calling createChatCompletion" + ) + }), + } as any) + ) const res = await setup.runStep("OPENAI", { prompt: OPENAI_PROMPT, From a71d2804c073818bceb2269ca4b35d918b42bff0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 1 Aug 2023 13:07:37 +0100 Subject: [PATCH 051/117] Update pro to develop. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index b10f97aa63..faeaee6e80 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit b10f97aa6302cfbbf61c44edbc51af5d31ff84dd +Subproject commit faeaee6e8007260c68b40d453f307f6b6413aeb0 From 0449ba7a71779b1e532123bf54e109cbfd442379 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:12:28 +0200 Subject: [PATCH 052/117] Change refs --- packages/backend-core/tsconfig.build.json | 5 ++++- packages/backend-core/tsconfig.json | 5 +---- packages/pro | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json index bfbed31e23..ba9058b131 100644 --- a/packages/backend-core/tsconfig.build.json +++ b/packages/backend-core/tsconfig.build.json @@ -12,7 +12,10 @@ "declaration": true, "types": ["node", "jest"], "outDir": "dist", - "skipLibCheck": true + "skipLibCheck": true, + "paths": { + "@budibase/types": ["../types/src"] + } }, "include": ["**/*.js", "**/*.ts"], "exclude": [ diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 2993ff84ed..4fd2295642 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -2,10 +2,7 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": true, - "baseUrl": ".", - "paths": { - "@budibase/types": ["../types/src"] - } + "baseUrl": "." }, "exclude": ["node_modules", "dist"] } diff --git a/packages/pro b/packages/pro index 7a1a21c8d7..4ef37991e5 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 7a1a21c8d794be3f174eaa186ca33b9505467cda +Subproject commit 4ef37991e585ea63e36a481f8251dcaf3e2da8b4 From bf2f0b5c3a287cf9699bb786a226309959b14f0a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:16:37 +0200 Subject: [PATCH 053/117] Sync posthog-node package --- packages/cli/package.json | 2 +- yarn.lock | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 8e00d2cd91..4437469be2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -46,7 +46,7 @@ "lookpath": "1.1.0", "node-fetch": "2.6.7", "pkg": "5.8.0", - "posthog-node": "1.0.7", + "posthog-node": "1.3.0", "pouchdb": "7.3.0", "pouchdb-replication-stream": "1.2.9", "randomstring": "1.1.5", diff --git a/yarn.lock b/yarn.lock index d1f22e7699..827c94a176 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20279,20 +20279,6 @@ posthog-js@^1.36.0: fflate "^0.4.1" rrweb-snapshot "^1.1.14" -posthog-node@1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.0.7.tgz#a7a9525eebff23312117e57cff3ddac82afb2262" - integrity sha512-KTCwyU+PU1eAQtjy5ZSJ47mrxv2d/mMkxo+vvV5c+YqfE4mBAY1UPEPMv1nElb5Vq0UnxvyQXaUnOn8d8Xr6Eg== - dependencies: - axios "^0.21.1" - axios-retry "^3.1.9" - component-type "^1.2.1" - join-component "^1.1.0" - md5 "^2.3.0" - ms "^2.1.3" - remove-trailing-slash "^0.1.1" - uuid "^8.3.2" - posthog-node@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" From 0f9ae3fa664e7e053dc14727dca36d6813060fc8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:17:44 +0200 Subject: [PATCH 054/117] Revert paths on tsconfig.build --- packages/backend-core/tsconfig.build.json | 5 +---- packages/backend-core/tsconfig.json | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend-core/tsconfig.build.json b/packages/backend-core/tsconfig.build.json index ba9058b131..bfbed31e23 100644 --- a/packages/backend-core/tsconfig.build.json +++ b/packages/backend-core/tsconfig.build.json @@ -12,10 +12,7 @@ "declaration": true, "types": ["node", "jest"], "outDir": "dist", - "skipLibCheck": true, - "paths": { - "@budibase/types": ["../types/src"] - } + "skipLibCheck": true }, "include": ["**/*.js", "**/*.ts"], "exclude": [ diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 4fd2295642..2993ff84ed 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -2,7 +2,10 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": true, - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@budibase/types": ["../types/src"] + } }, "exclude": ["node_modules", "dist"] } From 953aead95cf8973bca0f450840a3b949edcd392b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:18:54 +0200 Subject: [PATCH 055/117] Revert paths on tsconfig.build --- packages/shared-core/tsconfig-base.build.json | 5 +---- packages/shared-core/tsconfig.json | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/shared-core/tsconfig-base.build.json b/packages/shared-core/tsconfig-base.build.json index 31dc1afc10..6930e3cb99 100644 --- a/packages/shared-core/tsconfig-base.build.json +++ b/packages/shared-core/tsconfig-base.build.json @@ -12,10 +12,7 @@ "declaration": true, "types": ["node"], "outDir": "dist", - "skipLibCheck": true, - "paths": { - "@budibase/types": ["../types/src"] - } + "skipLibCheck": true }, "include": ["**/*.js", "**/*.ts"], "exclude": [ diff --git a/packages/shared-core/tsconfig.json b/packages/shared-core/tsconfig.json index 52e07ddb5c..3f32e2adb8 100644 --- a/packages/shared-core/tsconfig.json +++ b/packages/shared-core/tsconfig.json @@ -2,7 +2,10 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "composite": true, - "baseUrl": "." + "baseUrl": ".", + "paths": { + "@budibase/types": ["../types/src"] + } }, "include": ["**/*.js", "**/*.ts"], "exclude": ["node_modules", "dist"] From 068710922412c846842e499213712e7910010e79 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:20:39 +0200 Subject: [PATCH 056/117] Update refs --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 4ef37991e5..879c62c25f 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 4ef37991e585ea63e36a481f8251dcaf3e2da8b4 +Subproject commit 879c62c25f2c9772b624733901534b9adbf80a6b From 6ed80386b0db5ec4ec03df0ffbf26d9e0690f7c1 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 1 Aug 2023 13:20:48 +0100 Subject: [PATCH 057/117] Update pro. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index faeaee6e80..66b2626712 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit faeaee6e8007260c68b40d453f307f6b6413aeb0 +Subproject commit 66b2626712584d5e570db1492cffee7796d0bc3e From dc36829f7a1b50ddccce7dddf5df8e6d193ab2b2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:25:52 +0200 Subject: [PATCH 058/117] Cache check:types --- nx.json | 2 +- package.json | 2 +- packages/pro | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nx.json b/nx.json index c2f44ef70d..9680f96dc1 100644 --- a/nx.json +++ b/nx.json @@ -3,7 +3,7 @@ "default": { "runner": "nx-cloud", "options": { - "cacheableOperations": ["build", "test"], + "cacheableOperations": ["build", "test", "check:types"], "accessToken": "MmM4OGYxNzItMDBlYy00ZmE3LTk4MTYtNmJhYWMyZjBjZTUyfHJlYWQ=" } } diff --git a/package.json b/package.json index a392aeee78..ab62955124 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "bootstrap": "./scripts/link-dependencies.sh && echo '***BOOTSTRAP ONLY REQUIRED FOR USE WITH ACCOUNT PORTAL***'", "build": "yarn nx run-many -t=build", "build:dev": "lerna run --stream prebuild && yarn nx run-many --target=build --output-style=dynamic --watch --preserveWatchOutput", - "check:types": "lerna run check:types --skip-nx-cache", + "check:types": "lerna run check:types", "backend:bootstrap": "./scripts/scopeBackend.sh && yarn run bootstrap", "backend:build": "./scripts/scopeBackend.sh 'lerna run --stream build'", "build:sdk": "lerna run --stream build:sdk", diff --git a/packages/pro b/packages/pro index 879c62c25f..06789ccbb2 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 879c62c25f2c9772b624733901534b9adbf80a6b +Subproject commit 06789ccbb280e7f6aef522d974f6013124de9215 From e7c6724620cdac2151fd5c102ca2fe439c7ee317 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:39:17 +0200 Subject: [PATCH 059/117] Ad nx dependency --- packages/builder/package.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/builder/package.json b/packages/builder/package.json index 2f6d329877..6b5399c942 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -140,6 +140,17 @@ } ] }, + "dev:builder": { + "dependsOn": [ + { + "projects": [ + "@budibase/string-templates", + "@budibase/shared-core" + ], + "target": "build" + } + ] + }, "test": { "dependsOn": [ { From 49e65c693e9f5d8674429c0c3fcac1b60373a46c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 1 Aug 2023 13:46:36 +0100 Subject: [PATCH 060/117] Update pro again. --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 66b2626712..fae546fb06 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 66b2626712584d5e570db1492cffee7796d0bc3e +Subproject commit fae546fb0678dfdad443199eb1e67497e3004370 From 56f49150f7dba44c886a2d6a55daa3a0f2cf98b4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 14:39:54 +0200 Subject: [PATCH 061/117] Add nx dependency --- packages/builder/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index 6b5399c942..b6adfc674c 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -145,7 +145,8 @@ { "projects": [ "@budibase/string-templates", - "@budibase/shared-core" + "@budibase/shared-core", + "@budibase/types" ], "target": "build" } From 7e8b3cdc30327e8de2cfb52af109087179f27731 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 16:28:40 +0200 Subject: [PATCH 062/117] Update pro ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 06789ccbb2..cf3bef2aad 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 06789ccbb280e7f6aef522d974f6013124de9215 +Subproject commit cf3bef2aad9c739111b306fd0712397adc363f81 From ad42cfc6fbf8fcfb138711624e1f305b6be84398 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 1 Aug 2023 16:25:47 +0100 Subject: [PATCH 063/117] Linting. --- packages/pro | 2 +- packages/types/src/index.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/pro b/packages/pro index fae546fb06..cf3bef2aad 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit fae546fb0678dfdad443199eb1e67497e3004370 +Subproject commit cf3bef2aad9c739111b306fd0712397adc363f81 diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index fe1b4859ed..a0932829cf 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -3,4 +3,3 @@ export * from "./sdk" export * from "./api" export * from "./core" export * from "./shared" - From 496fe0df8f36230519019ed5e70e4d4d55a031e3 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 1 Aug 2023 15:49:05 +0000 Subject: [PATCH 064/117] Bump version to 2.8.29-alpha.10 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 2b5b60e7a9..3e1cd55d75 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.29-alpha.9", + "version": "2.8.29-alpha.10", "npmClient": "yarn", "packages": [ "packages/*" From 590c3a8dfc23f6da804184cc19ff559f43a71874 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 1 Aug 2023 16:10:49 +0000 Subject: [PATCH 065/117] Bump version to 2.8.29-alpha.11 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 3e1cd55d75..74a1392bf9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.29-alpha.10", + "version": "2.8.29-alpha.11", "npmClient": "yarn", "packages": [ "packages/*" From 8c2d0f594c99d7c1ccfcee2614678c824b541fa1 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 1 Aug 2023 18:18:53 +0100 Subject: [PATCH 066/117] Quick fix for utilising a hyphen in a Postgres schema - this needed to be escaped correctly. --- packages/server/scripts/integrations/postgres/init.sql | 6 +++--- packages/server/src/integrations/postgres.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/scripts/integrations/postgres/init.sql b/packages/server/scripts/integrations/postgres/init.sql index 5e385c12d7..6cb3f51269 100644 --- a/packages/server/scripts/integrations/postgres/init.sql +++ b/packages/server/scripts/integrations/postgres/init.sql @@ -1,6 +1,6 @@ SELECT 'CREATE DATABASE main' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'main')\gexec -CREATE SCHEMA test; +CREATE SCHEMA "test-1"; CREATE TYPE person_job AS ENUM ('qa', 'programmer', 'designer'); CREATE TABLE Persons ( PersonID SERIAL PRIMARY KEY, @@ -39,7 +39,7 @@ CREATE TABLE Products_Tasks ( REFERENCES Tasks(TaskID), PRIMARY KEY (ProductID, TaskID) ); -CREATE TABLE test.table1 ( +CREATE TABLE "test-1".table1 ( id SERIAL PRIMARY KEY, Name varchar(255) ); @@ -60,7 +60,7 @@ INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (1, 1); INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (2, 1); INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (3, 1); INSERT INTO Products_Tasks (ProductID, TaskID) VALUES (1, 2); -INSERT INTO test.table1 (Name) VALUES ('Test'); +INSERT INTO "test-1".table1 (Name) VALUES ('Test'); INSERT INTO CompositeTable (KeyPartOne, KeyPartTwo, Name) VALUES ('aaa', 'bbb', 'Michael'); INSERT INTO CompositeTable (KeyPartOne, KeyPartTwo, Name) VALUES ('bbb', 'ccc', 'Andrew'); INSERT INTO CompositeTable (KeyPartOne, KeyPartTwo, Name) VALUES ('ddd', '', 'OneKey'); diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index b16f5b858b..c4b7c2bb65 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -210,7 +210,7 @@ class PostgresIntegration extends Sql implements DatasourcePlus { if (!this.config.schema) { this.config.schema = "public" } - await this.client.query(`SET search_path TO ${this.config.schema}`) + await this.client.query(`SET search_path TO "${this.config.schema}"`) this.COLUMNS_SQL = `select * from information_schema.columns where table_schema = '${this.config.schema}'` this.open = true } From e36a04d6839a6fea99d99395ca82737648b82c21 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 15:45:56 +0200 Subject: [PATCH 067/117] Remove --parallel flag --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ab62955124..d27af2e27d 100644 --- a/package.json +++ b/package.json @@ -51,9 +51,9 @@ "kill-builder": "kill-port 3000", "kill-server": "kill-port 4001 4002", "kill-all": "yarn run kill-builder && yarn run kill-server", - "dev": "yarn run kill-all && lerna run --stream --parallel dev:builder", - "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", - "dev:server": "yarn run kill-server && lerna run --stream --parallel dev:builder --scope @budibase/worker --scope @budibase/server", + "dev": "yarn run kill-all && lerna run --stream dev:builder", + "dev:noserver": "yarn run kill-builder && lerna run --stream dev:stack:up && lerna run --stream dev:builder --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker", + "dev:server": "yarn run kill-server && lerna run --stream dev:builder --scope @budibase/worker --scope @budibase/server", "dev:built": "yarn run kill-all && cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream dev:built", "dev:docker": "yarn build:docker:pre && docker-compose -f hosting/docker-compose.build.yaml -f hosting/docker-compose.dev.yaml --env-file hosting/.env up --build --scale proxy-service=0", "test": "lerna run --stream test --stream", From f5131f673c81c3d251fd306b38c7dc72fcd053ac Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 15:55:38 +0200 Subject: [PATCH 068/117] Fix dev build --- packages/shared-core/package.json | 12 +++++++++++- packages/types/package.json | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index f79075eb2e..d1d5160fc6 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -17,7 +17,7 @@ "prebuild": "rimraf dist/", "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", - "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"", + "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tsc -p tsconfig-cjs.build.json --watch --preserveWatchOutput\"", "check:types": "tsc -p tsconfig.json --noEmit --paths null" }, "dependencies": { @@ -39,6 +39,16 @@ "target": "build" } ] + }, + "dev:builder": { + "dependsOn": [ + { + "projects": [ + "@budibase/types" + ], + "target": "build" + } + ] } } } diff --git a/packages/types/package.json b/packages/types/package.json index c419a4d527..41064b8581 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -16,7 +16,7 @@ "prebuild": "rimraf dist/", "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", - "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"", + "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tsc -p tsconfig-cjs.build.json --watch --preserveWatchOutput\"", "check:types": "tsc -p tsconfig.json --noEmit --paths null" }, "jest": {}, From 7e30047b3fb95695d10396d831cc28a89b58b61c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 17:46:09 +0200 Subject: [PATCH 069/117] Fix dev:builder --- packages/shared-core/package.json | 12 +++++------- packages/types/package.json | 9 ++++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index d1d5160fc6..0896273e68 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -2,14 +2,12 @@ "name": "@budibase/shared-core", "version": "0.0.0", "description": "Shared data utils", - "main": "dist/cjs/src/index.js", - "types": "dist/mjs/src/index.d.ts", + "main": "src/index.ts", + "types": "src/index.ts", "exports": { ".": { - "import": "./dist/mjs/src/index.js", - "require": "./dist/cjs/src/index.js" - }, - "./package.json": "./dist/mjs/package.json" + "import": "./dist/mjs/src/index.js" + } }, "author": "Budibase", "license": "GPL-3.0", @@ -17,7 +15,7 @@ "prebuild": "rimraf dist/", "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", - "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tsc -p tsconfig-cjs.build.json --watch --preserveWatchOutput\"", + "dev:builder": "yarn prebuild && tsc -p tsconfig.json --watch --preserveWatchOutput", "check:types": "tsc -p tsconfig.json --noEmit --paths null" }, "dependencies": { diff --git a/packages/types/package.json b/packages/types/package.json index 41064b8581..520d8fa5b0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -2,12 +2,11 @@ "name": "@budibase/types", "version": "0.0.0", "description": "Budibase types", - "main": "dist/cjs/index.js", - "types": "dist/mjs/index.d.ts", + "main": "src/index.ts", + "types": "src/index.ts", "exports": { ".": { - "import": "./dist/mjs/index.js", - "require": "./dist/cjs/index.js" + "import": "./dist/mjs/index.js" } }, "author": "Budibase", @@ -16,7 +15,7 @@ "prebuild": "rimraf dist/", "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", - "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tsc -p tsconfig-cjs.build.json --watch --preserveWatchOutput\"", + "dev:builder": "yarn prebuild && tsc -p tsconfig.json --watch --preserveWatchOutput", "check:types": "tsc -p tsconfig.json --noEmit --paths null" }, "jest": {}, From 250a4e890fb5e1c2297dc0aad37bdd9ae5ec9bc8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 17:46:27 +0200 Subject: [PATCH 070/117] Remove composite --- packages/backend-core/tsconfig.json | 1 - packages/builder/tsconfig.json | 1 - packages/cli/tsconfig.json | 1 - packages/server/tsconfig.json | 1 - packages/shared-core/tsconfig.json | 1 - packages/types/tsconfig.json | 3 --- packages/worker/tsconfig.json | 1 - 7 files changed, 9 deletions(-) diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 2993ff84ed..54523cf3bf 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "baseUrl": ".", "paths": { "@budibase/types": ["../types/src"] diff --git a/packages/builder/tsconfig.json b/packages/builder/tsconfig.json index 0913aea33d..bce358896f 100644 --- a/packages/builder/tsconfig.json +++ b/packages/builder/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "declaration": true, "sourceMap": true, "baseUrl": "." diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index ab306debca..e28797f20a 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "declaration": true, "sourceMap": true, "baseUrl": ".", diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index d9f84f6a5e..cfab6c8ef8 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "baseUrl": "." }, "ts-node": { diff --git a/packages/shared-core/tsconfig.json b/packages/shared-core/tsconfig.json index 3f32e2adb8..f1541a0f8a 100644 --- a/packages/shared-core/tsconfig.json +++ b/packages/shared-core/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "baseUrl": ".", "paths": { "@budibase/types": ["../types/src"] diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index ad5356c2dc..33e37179d7 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -1,7 +1,4 @@ { "extends": "./tsconfig.build.json", - "compilerOptions": { - "composite": true - }, "exclude": ["node_modules", "dist"] } diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 4f3d5cbac8..2b8759ebd9 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "baseUrl": "." }, "ts-node": { From 8ec251aefbc6e7c434fbb2e9212bf479c0180cb2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 17:50:11 +0200 Subject: [PATCH 071/117] Remove unnecessary references --- packages/cli/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index e28797f20a..15c04c6cab 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -15,7 +15,6 @@ "require": ["tsconfig-paths/register"], "swc": true }, - "references": [{ "path": "../types" }, { "path": "../backend-core" }], "include": ["src/**/*", "package.json"], "exclude": ["node_modules", "dist"] } From 50ca4de0f767dcf72ceaf9db9e6e52e665d8e606 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 17:56:27 +0200 Subject: [PATCH 072/117] Remove cjs refs --- packages/shared-core/package.json | 2 +- packages/shared-core/tsconfig-cjs.build.json | 8 -------- packages/types/package.json | 2 +- packages/types/tsconfig-cjs.build.json | 8 -------- 4 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 packages/shared-core/tsconfig-cjs.build.json delete mode 100644 packages/types/tsconfig-cjs.build.json diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index 0896273e68..f5a855ae7e 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -13,7 +13,7 @@ "license": "GPL-3.0", "scripts": { "prebuild": "rimraf dist/", - "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", + "build": "tsc -p tsconfig.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", "dev:builder": "yarn prebuild && tsc -p tsconfig.json --watch --preserveWatchOutput", "check:types": "tsc -p tsconfig.json --noEmit --paths null" diff --git a/packages/shared-core/tsconfig-cjs.build.json b/packages/shared-core/tsconfig-cjs.build.json deleted file mode 100644 index 45e81e575c..0000000000 --- a/packages/shared-core/tsconfig-cjs.build.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig-base.build.json", - "compilerOptions": { - "module": "commonjs", - "outDir": "dist/cjs/src", - "target": "es2015" - } -} diff --git a/packages/types/package.json b/packages/types/package.json index 520d8fa5b0..6429bbbf21 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -13,7 +13,7 @@ "license": "GPL-3.0", "scripts": { "prebuild": "rimraf dist/", - "build": "tsc -p tsconfig.build.json && tsc -p tsconfig-cjs.build.json", + "build": "tsc -p tsconfig.build.json", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", "dev:builder": "yarn prebuild && tsc -p tsconfig.json --watch --preserveWatchOutput", "check:types": "tsc -p tsconfig.json --noEmit --paths null" diff --git a/packages/types/tsconfig-cjs.build.json b/packages/types/tsconfig-cjs.build.json deleted file mode 100644 index 9b479b7b34..0000000000 --- a/packages/types/tsconfig-cjs.build.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig-base.build.json", - "compilerOptions": { - "module": "commonjs", - "outDir": "dist/cjs", - "target": "es2015" - } -} From 5347504c1226a57a9af354eec57d96414ef195e7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 18:06:36 +0200 Subject: [PATCH 073/117] Fix nodemon watch --- packages/server/nodemon.json | 15 +++++++++------ packages/worker/nodemon.json | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/server/nodemon.json b/packages/server/nodemon.json index 7956ad161b..33d277dd64 100644 --- a/packages/server/nodemon.json +++ b/packages/server/nodemon.json @@ -1,10 +1,13 @@ { - "watch": ["src", "../backend-core", "../pro"], - "ext": "js,ts,json", - "ignore": [ - "src/**/*.spec.ts", - "src/**/*.spec.js", - "../backend-core/dist/**/*" + "watch": [ + "src", + "../backend-core", + "../pro", + "../types", + "../shared-core", + "../string-templates" ], + "ext": "js,ts,json", + "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js", "../*/dist/**/*"], "exec": "yarn build && node ./dist/index.js" } diff --git a/packages/worker/nodemon.json b/packages/worker/nodemon.json index 9585d85af5..a8e9d45b47 100644 --- a/packages/worker/nodemon.json +++ b/packages/worker/nodemon.json @@ -1,10 +1,13 @@ { - "watch": ["src", "../backend-core", "../pro"], - "ext": "js,ts,json", - "ignore": [ - "src/**/*.spec.ts", - "src/**/*.spec.js", - "../backend-core/dist/**/*" + "watch": [ + "src", + "../backend-core", + "../pro", + "../types", + "../shared-core", + "../string-templates" ], + "ext": "js,ts,json", + "ignore": ["src/**/*.spec.ts", "src/**/*.spec.js", "../*/dist/**/*"], "exec": "yarn build && node dist/index.js" } From da6f7938e55f593b4d8cc2f18ff0667ba9995404 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 18:22:38 +0200 Subject: [PATCH 074/117] Remove --- packages/types/tsconfig-base.build.json | 15 --------------- packages/types/tsconfig.build.json | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 packages/types/tsconfig-base.build.json diff --git a/packages/types/tsconfig-base.build.json b/packages/types/tsconfig-base.build.json deleted file mode 100644 index d2403d75fc..0000000000 --- a/packages/types/tsconfig-base.build.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "lib": ["es2020"], - "strict": true, - "noImplicitAny": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "incremental": true, - "sourceMap": true, - "declaration": true, - "skipLibCheck": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.spec.js"] -} diff --git a/packages/types/tsconfig.build.json b/packages/types/tsconfig.build.json index 9643676b52..33409409a8 100644 --- a/packages/types/tsconfig.build.json +++ b/packages/types/tsconfig.build.json @@ -1,8 +1,18 @@ { - "extends": "./tsconfig-base.build.json", "compilerOptions": { "target": "es6", "moduleResolution": "node", + "lib": ["es2020"], + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "incremental": true, + "sourceMap": true, + "declaration": true, + "skipLibCheck": true, "outDir": "dist/mjs" - } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.spec.js"] } From 012ab8017644d74b4c1911bd22bcbf59a6ac8ad9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 18:22:44 +0200 Subject: [PATCH 075/117] Remove base --- packages/shared-core/tsconfig-base.build.json | 25 ------------------- packages/shared-core/tsconfig.build.json | 25 ++++++++++++++++--- 2 files changed, 21 insertions(+), 29 deletions(-) delete mode 100644 packages/shared-core/tsconfig-base.build.json diff --git a/packages/shared-core/tsconfig-base.build.json b/packages/shared-core/tsconfig-base.build.json deleted file mode 100644 index 6930e3cb99..0000000000 --- a/packages/shared-core/tsconfig-base.build.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "moduleResolution": "node", - "lib": ["es2020"], - "strict": true, - "noImplicitAny": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "incremental": true, - "sourceMap": true, - "declaration": true, - "types": ["node"], - "outDir": "dist", - "skipLibCheck": true - }, - "include": ["**/*.js", "**/*.ts"], - "exclude": [ - "node_modules", - "dist", - "**/*.spec.ts", - "**/*.spec.js", - "__mocks__" - ] -} diff --git a/packages/shared-core/tsconfig.build.json b/packages/shared-core/tsconfig.build.json index 2a00a44dfe..d76840d3de 100644 --- a/packages/shared-core/tsconfig.build.json +++ b/packages/shared-core/tsconfig.build.json @@ -1,8 +1,25 @@ { - "extends": "./tsconfig-base.build.json", "compilerOptions": { - "module": "esnext", + "target": "es6", + "module": "commonjs", + "lib": ["es2020"], + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "incremental": true, + "sourceMap": true, + "declaration": true, + "types": ["node"], "outDir": "dist/mjs/src", - "target": "esnext" - } + "skipLibCheck": true + }, + "include": ["**/*.js", "**/*.ts"], + "exclude": [ + "node_modules", + "dist", + "**/*.spec.ts", + "**/*.spec.js", + "__mocks__" + ] } From 4b5751003f9d5e5c3e5bd7bba384d4ec526fe946 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 18:31:08 +0200 Subject: [PATCH 076/117] Revert "Remove composite" This reverts commit f9115f1554bbadadc3a0ef91d00d170fa05e45b6. --- packages/backend-core/tsconfig.json | 1 + packages/builder/tsconfig.json | 1 + packages/cli/tsconfig.json | 1 + packages/server/tsconfig.json | 1 + packages/shared-core/tsconfig.json | 1 + packages/types/tsconfig.json | 3 +++ packages/worker/tsconfig.json | 1 + 7 files changed, 9 insertions(+) diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 54523cf3bf..2993ff84ed 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, "baseUrl": ".", "paths": { "@budibase/types": ["../types/src"] diff --git a/packages/builder/tsconfig.json b/packages/builder/tsconfig.json index bce358896f..0913aea33d 100644 --- a/packages/builder/tsconfig.json +++ b/packages/builder/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, "declaration": true, "sourceMap": true, "baseUrl": "." diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 15c04c6cab..c2935129a8 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, "declaration": true, "sourceMap": true, "baseUrl": ".", diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index cfab6c8ef8..d9f84f6a5e 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, "baseUrl": "." }, "ts-node": { diff --git a/packages/shared-core/tsconfig.json b/packages/shared-core/tsconfig.json index f1541a0f8a..3f32e2adb8 100644 --- a/packages/shared-core/tsconfig.json +++ b/packages/shared-core/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, "baseUrl": ".", "paths": { "@budibase/types": ["../types/src"] diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index 33e37179d7..ad5356c2dc 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "./tsconfig.build.json", + "compilerOptions": { + "composite": true + }, "exclude": ["node_modules", "dist"] } diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 2b8759ebd9..4f3d5cbac8 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "composite": true, "baseUrl": "." }, "ts-node": { From c59bfdb9208657f42337c96a96503ca1b363f85e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 18:44:49 +0200 Subject: [PATCH 077/117] Remove unnecessary out folder --- packages/client/rollup.config.js | 4 ++-- packages/shared-core/package.json | 2 +- packages/shared-core/tsconfig.build.json | 2 +- packages/types/package.json | 2 +- packages/types/tsconfig.build.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/rollup.config.js b/packages/client/rollup.config.js index c22b928f10..35a5b7a6b9 100644 --- a/packages/client/rollup.config.js +++ b/packages/client/rollup.config.js @@ -25,11 +25,11 @@ const devPaths = production : [ { find: "@budibase/shared-core", - replacement: path.resolve("../shared-core/dist/mjs/src/index"), + replacement: path.resolve("../shared-core/dist/src/index"), }, { find: "@budibase/types", - replacement: path.resolve("../types/dist/mjs/index"), + replacement: path.resolve("../types/dist/index"), }, ] diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index f5a855ae7e..aaf59023df 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -6,7 +6,7 @@ "types": "src/index.ts", "exports": { ".": { - "import": "./dist/mjs/src/index.js" + "import": "./dist/src/index.js" } }, "author": "Budibase", diff --git a/packages/shared-core/tsconfig.build.json b/packages/shared-core/tsconfig.build.json index d76840d3de..9d3bffe235 100644 --- a/packages/shared-core/tsconfig.build.json +++ b/packages/shared-core/tsconfig.build.json @@ -11,7 +11,7 @@ "sourceMap": true, "declaration": true, "types": ["node"], - "outDir": "dist/mjs/src", + "outDir": "dist/src", "skipLibCheck": true }, "include": ["**/*.js", "**/*.ts"], diff --git a/packages/types/package.json b/packages/types/package.json index 6429bbbf21..04bd3bc1d1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,7 @@ "types": "src/index.ts", "exports": { ".": { - "import": "./dist/mjs/index.js" + "import": "./dist/index.js" } }, "author": "Budibase", diff --git a/packages/types/tsconfig.build.json b/packages/types/tsconfig.build.json index 33409409a8..f2c4c7667b 100644 --- a/packages/types/tsconfig.build.json +++ b/packages/types/tsconfig.build.json @@ -11,7 +11,7 @@ "sourceMap": true, "declaration": true, "skipLibCheck": true, - "outDir": "dist/mjs" + "outDir": "dist" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.spec.js"] From c715e7aa3fa79229e2098de468dd9bba3a216a94 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 18:53:19 +0200 Subject: [PATCH 078/117] Unify rollup/plugin-commonjs --- packages/bbui/package.json | 2 +- packages/string-templates/package.json | 2 +- yarn.lock | 26 -------------------------- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/packages/bbui/package.json b/packages/bbui/package.json index e5d0ad6b57..97a4ecb042 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -17,7 +17,7 @@ "build": "rollup -c" }, "devDependencies": { - "@rollup/plugin-commonjs": "^16.0.0", + "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.2.1", "cross-env": "^7.0.2", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index d9416bad4c..2ef5477f1e 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -33,7 +33,7 @@ "vm2": "^3.9.15" }, "devDependencies": { - "@rollup/plugin-commonjs": "^17.1.0", + "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-json": "^4.1.0", "doctrine": "^3.0.0", "jest": "29.6.2", diff --git a/yarn.lock b/yarn.lock index 827c94a176..3b21e89c25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4818,32 +4818,6 @@ dependencies: slash "^3.0.0" -"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" - integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== - dependencies: - "@rollup/pluginutils" "^3.1.0" - commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" - -"@rollup/plugin-commonjs@^17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz#757ec88737dffa8aa913eb392fade2e45aef2a2d" - integrity sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew== - dependencies: - "@rollup/pluginutils" "^3.1.0" - commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" - "@rollup/plugin-commonjs@^18.0.0": version "18.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-18.1.0.tgz#5a760d757af168a50727c0ae080251fbfcc5eb02" From d310751fd3fe5d8205865080eb69082ca3708fe5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:10:18 +0200 Subject: [PATCH 079/117] Update rollup/plugin-commonjs --- packages/bbui/package.json | 4 +- packages/bbui/rollup.config.js | 2 +- packages/client/package.json | 4 +- packages/sdk/package.json | 4 +- packages/string-templates/package.json | 2 +- yarn.lock | 56 ++++++++++++++------------ 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 97a4ecb042..dbabe5c29b 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -17,9 +17,9 @@ "build": "rollup -c" }, "devDependencies": { - "@rollup/plugin-commonjs": "^18.0.0", + "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-node-resolve": "15.1.0", "cross-env": "^7.0.2", "nollup": "^0.14.1", "postcss": "^8.2.9", diff --git a/packages/bbui/rollup.config.js b/packages/bbui/rollup.config.js index e285d548d6..0abb9f667b 100644 --- a/packages/bbui/rollup.config.js +++ b/packages/bbui/rollup.config.js @@ -13,8 +13,8 @@ export default { file: "dist/bbui.es.js", }, plugins: [ - resolve(), commonjs(), + resolve(), svelte({ emitCss: true, }), diff --git a/packages/client/package.json b/packages/client/package.json index 689ec332e5..f0f0d6e816 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -48,9 +48,9 @@ }, "devDependencies": { "@rollup/plugin-alias": "^3.1.5", - "@rollup/plugin-commonjs": "^18.0.0", + "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-image": "^3.0.2", - "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-node-resolve": "15.1.0", "postcss": "^8.2.10", "rollup": "^2.44.0", "rollup-plugin-json": "^4.0.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 4d3c811b3d..873d60155f 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -14,8 +14,8 @@ "superagent": "^5.3.0" }, "devDependencies": { - "@rollup/plugin-commonjs": "^18.0.0", - "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-commonjs": "25.0.3", + "@rollup/plugin-node-resolve": "15.1.0", "rollup": "^2.44.0", "rollup-plugin-polyfill-node": "^0.8.0", "rollup-plugin-terser": "^7.0.2" diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 2ef5477f1e..2b5d31dabd 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -33,7 +33,7 @@ "vm2": "^3.9.15" }, "devDependencies": { - "@rollup/plugin-commonjs": "^18.0.0", + "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-json": "^4.1.0", "doctrine": "^3.0.0", "jest": "29.6.2", diff --git a/yarn.lock b/yarn.lock index 3b21e89c25..d0072d0dcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4818,18 +4818,17 @@ dependencies: slash "^3.0.0" -"@rollup/plugin-commonjs@^18.0.0": - version "18.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-18.1.0.tgz#5a760d757af168a50727c0ae080251fbfcc5eb02" - integrity sha512-h3e6T9rUxVMAQswpDIobfUHn/doMzM9sgkMrsMWCFLmB84PSoC8mV8tOloAJjSRwdqhXBqstlX2BwBpHJvbhxg== +"@rollup/plugin-commonjs@25.0.3": + version "25.0.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.3.tgz#eb5217ebae43d63a172b516655be270ed258bdcc" + integrity sha512-uBdtWr/H3BVcgm97MUdq2oJmqBR23ny1hOrWe2PKo9FTbjsGqg32jfasJUKYAI5ouqacjRnj65mBB/S79F+GQA== dependencies: - "@rollup/pluginutils" "^3.1.0" + "@rollup/pluginutils" "^5.0.1" commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.27.0" "@rollup/plugin-image@^3.0.2": version "3.0.2" @@ -4855,17 +4854,17 @@ dependencies: "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-node-resolve@^11.2.1": - version "11.2.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz#82aa59397a29cd4e13248b106e6a4a1880362a60" - integrity sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg== +"@rollup/plugin-node-resolve@15.1.0": + version "15.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz#9ffcd8e8c457080dba89bb9fcb583a6778dc757e" + integrity sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA== dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" - builtin-modules "^3.1.0" + "@rollup/pluginutils" "^5.0.1" + "@types/resolve" "1.20.2" deepmerge "^4.2.2" + is-builtin-module "^3.2.1" is-module "^1.0.0" - resolve "^1.19.0" + resolve "^1.22.1" "@rollup/plugin-replace@^2.4.2": version "2.4.2" @@ -6543,12 +6542,10 @@ dependencies: "@types/node" "*" -"@types/resolve@1.17.1": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" - integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== - dependencies: - "@types/node" "*" +"@types/resolve@1.20.2": + version "1.20.2" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" + integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== "@types/responselike@^1.0.0": version "1.0.0" @@ -8435,7 +8432,7 @@ buildcheck@0.0.3: resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.3.tgz#70451897a95d80f7807e68fc412eb2e7e35ff4d5" integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA== -builtin-modules@^3.1.0: +builtin-modules@^3.1.0, builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== @@ -14476,6 +14473,13 @@ is-buffer@^1.1.5, is-buffer@~1.1.6: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + is-callable@^1.1.3, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -14770,7 +14774,7 @@ is-property@^1.0.2: resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== -is-reference@^1.2.1: +is-reference@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== From ddec3e9a9f46c15c54f7cd3e1ff52f52f01e7a77 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:11:29 +0200 Subject: [PATCH 080/117] Update plugins --- packages/string-templates/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 2b5d31dabd..fc0cab5ed3 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -35,6 +35,7 @@ "devDependencies": { "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "15.1.0", "doctrine": "^3.0.0", "jest": "29.6.2", "jest-environment-node": "29.6.2", From 9ab78809eb9c1b9d5897db3142495e063f7399f0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:14:26 +0200 Subject: [PATCH 081/117] Clean package --- packages/string-templates/package.json | 1 - yarn.lock | 24 +++--------------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index fc0cab5ed3..7cdd4ab502 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -44,7 +44,6 @@ "rollup-plugin-inject-process-env": "^1.3.1", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-node-globals": "^1.4.0", - "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^7.0.2", "typescript": "4.7.3" }, diff --git a/yarn.lock b/yarn.lock index d0072d0dcd..10411f0f55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6535,13 +6535,6 @@ "@types/tough-cookie" "*" form-data "^2.5.0" -"@types/resolve@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== - dependencies: - "@types/node" "*" - "@types/resolve@1.20.2": version "1.20.2" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" @@ -8432,7 +8425,7 @@ buildcheck@0.0.3: resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.3.tgz#70451897a95d80f7807e68fc412eb2e7e35ff4d5" integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA== -builtin-modules@^3.1.0, builtin-modules@^3.3.0: +builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== @@ -21640,7 +21633,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== -resolve@^1.10.0, resolve@^1.11.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.9.0: +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.9.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -21801,17 +21794,6 @@ rollup-plugin-node-globals@^1.4.0: process-es6 "^0.11.6" rollup-pluginutils "^2.3.1" -rollup-plugin-node-resolve@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" - integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== - dependencies: - "@types/resolve" "0.0.8" - builtin-modules "^3.1.0" - is-module "^1.0.0" - resolve "^1.11.1" - rollup-pluginutils "^2.8.1" - rollup-plugin-polyfill-node@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.8.0.tgz#859c070822f5e38d221e5b4238cb34aa894c2b19" @@ -21889,7 +21871,7 @@ rollup-pluginutils@^1.3.1: estree-walker "^0.2.1" minimatch "^3.0.2" -rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: +rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.2: version "2.8.2" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== From 367f5c2963539a22d9bca4792f27bd2d4a952d40 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:15:14 +0200 Subject: [PATCH 082/117] Update usage --- packages/string-templates/rollup.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/string-templates/rollup.config.js b/packages/string-templates/rollup.config.js index d7f45489c9..a85162116e 100644 --- a/packages/string-templates/rollup.config.js +++ b/packages/string-templates/rollup.config.js @@ -1,5 +1,5 @@ import commonjs from "@rollup/plugin-commonjs" -import resolve from "rollup-plugin-node-resolve" +import resolve from "@rollup/plugin-node-resolve" import json from "@rollup/plugin-json" import { terser } from "rollup-plugin-terser" import builtins from "rollup-plugin-node-builtins" From 4794cfce99a4e128a60f3d222103df0017d4f745 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:18:22 +0200 Subject: [PATCH 083/117] Unify rollup --- packages/builder/package.json | 2 +- packages/client/package.json | 2 +- packages/sdk/package.json | 2 +- packages/string-templates/package.json | 2 +- yarn.lock | 11 ++--------- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index b6adfc674c..ad6037d91a 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -115,7 +115,7 @@ "mochawesome-report-generator": "^6.2.0", "ncp": "^2.0.0", "rimraf": "^3.0.2", - "rollup": "^2.44.0", + "rollup": "^2.45.2", "rollup-plugin-copy": "^3.4.0", "start-server-and-test": "^1.12.1", "svelte": "^3.48.0", diff --git a/packages/client/package.json b/packages/client/package.json index f0f0d6e816..f1f452eb40 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -52,7 +52,7 @@ "@rollup/plugin-image": "^3.0.2", "@rollup/plugin-node-resolve": "15.1.0", "postcss": "^8.2.10", - "rollup": "^2.44.0", + "rollup": "^2.45.2", "rollup-plugin-json": "^4.0.0", "rollup-plugin-polyfill-node": "^0.8.0", "rollup-plugin-postcss": "^4.0.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 873d60155f..ed877a9fce 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -16,7 +16,7 @@ "devDependencies": { "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-node-resolve": "15.1.0", - "rollup": "^2.44.0", + "rollup": "^2.45.2", "rollup-plugin-polyfill-node": "^0.8.0", "rollup-plugin-terser": "^7.0.2" } diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 7cdd4ab502..489ed9ef78 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -40,7 +40,7 @@ "jest": "29.6.2", "jest-environment-node": "29.6.2", "marked": "^4.0.10", - "rollup": "^2.36.2", + "rollup": "^2.45.2", "rollup-plugin-inject-process-env": "^1.3.1", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-node-globals": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index 10411f0f55..af662b4c25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12819,7 +12819,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -21878,14 +21878,7 @@ rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, dependencies: estree-walker "^0.6.1" -rollup@2.45.2: - version "2.45.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48" - integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ== - optionalDependencies: - fsevents "~2.3.1" - -rollup@^2.36.2, rollup@^2.44.0, rollup@^2.45.2, rollup@^2.79.1: +rollup@^2.45.2, rollup@^2.79.1: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== From 064b15d8234b94fb2a6383e72b06ae4b696b00fb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:22:04 +0200 Subject: [PATCH 084/117] Set plugin-commonjs in builder --- packages/builder/package.json | 1 + packages/builder/vite.config.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/builder/package.json b/packages/builder/package.json index ad6037d91a..98bac96859 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -98,6 +98,7 @@ "@babel/plugin-transform-runtime": "^7.13.10", "@babel/preset-env": "^7.13.12", "@babel/runtime": "^7.13.10", + "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-replace": "^2.4.2", "@roxi/routify": "2.18.5", "@sveltejs/vite-plugin-svelte": "1.0.1", diff --git a/packages/builder/vite.config.js b/packages/builder/vite.config.js index 4a0ffca8d4..549a3971b4 100644 --- a/packages/builder/vite.config.js +++ b/packages/builder/vite.config.js @@ -1,4 +1,5 @@ import { svelte } from "@sveltejs/vite-plugin-svelte" +import commonjs from "@rollup/plugin-commonjs" import replace from "@rollup/plugin-replace" import { defineConfig, loadEnv } from "vite" import { viteStaticCopy } from "vite-plugin-static-copy" @@ -61,6 +62,7 @@ export default defineConfig(({ mode }) => { sourcemap: !isProduction, }, plugins: [ + commonjs(), svelte({ hot: !isProduction, emitCss: true, From d104cc3a31706698d06f26f7d6e7186bf85482ca Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 1 Aug 2023 19:24:25 +0200 Subject: [PATCH 085/117] Update pro ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index cf3bef2aad..d37a88a5ac 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit cf3bef2aad9c739111b306fd0712397adc363f81 +Subproject commit d37a88a5acae873a7635edbd9c5bae3c4e30f4c0 From 4281d2ca53080d21c1a61d2041b299ac19e0ff57 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 00:21:59 +0200 Subject: [PATCH 086/117] Build types and shared-core --- packages/shared-core/package.json | 2 +- packages/shared-core/tsconfig.build.json | 2 +- packages/shared-core/tsconfig.json | 6 +++--- packages/types/tsconfig.build.json | 2 +- packages/types/tsconfig.json | 2 ++ 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index aaf59023df..838b8c0bea 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -6,7 +6,7 @@ "types": "src/index.ts", "exports": { ".": { - "import": "./dist/src/index.js" + "import": "./dist/index.js" } }, "author": "Budibase", diff --git a/packages/shared-core/tsconfig.build.json b/packages/shared-core/tsconfig.build.json index 9d3bffe235..8b73754a43 100644 --- a/packages/shared-core/tsconfig.build.json +++ b/packages/shared-core/tsconfig.build.json @@ -11,7 +11,7 @@ "sourceMap": true, "declaration": true, "types": ["node"], - "outDir": "dist/src", + "outDir": "dist", "skipLibCheck": true }, "include": ["**/*.js", "**/*.ts"], diff --git a/packages/shared-core/tsconfig.json b/packages/shared-core/tsconfig.json index 3f32e2adb8..29825689c8 100644 --- a/packages/shared-core/tsconfig.json +++ b/packages/shared-core/tsconfig.json @@ -1,12 +1,12 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { - "composite": true, "baseUrl": ".", + "rootDir": "./src", + "composite": true, "paths": { - "@budibase/types": ["../types/src"] + "@budibase/types": ["../../types/src"] } }, - "include": ["**/*.js", "**/*.ts"], "exclude": ["node_modules", "dist"] } diff --git a/packages/types/tsconfig.build.json b/packages/types/tsconfig.build.json index f2c4c7667b..8df63fac57 100644 --- a/packages/types/tsconfig.build.json +++ b/packages/types/tsconfig.build.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es6", - "moduleResolution": "node", + "module": "commonjs", "lib": ["es2020"], "strict": true, "noImplicitAny": true, diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index ad5356c2dc..a2fd5207b4 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "./tsconfig.build.json", "compilerOptions": { + "baseUrl": ".", + "rootDir": "./src", "composite": true }, "exclude": ["node_modules", "dist"] From 89c89e648d38738505d9a7fd5337fd99fbb07fbc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 00:25:24 +0200 Subject: [PATCH 087/117] Fix build --- packages/client/rollup.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/rollup.config.js b/packages/client/rollup.config.js index 35a5b7a6b9..ee839424ee 100644 --- a/packages/client/rollup.config.js +++ b/packages/client/rollup.config.js @@ -25,7 +25,7 @@ const devPaths = production : [ { find: "@budibase/shared-core", - replacement: path.resolve("../shared-core/dist/src/index"), + replacement: path.resolve("../shared-core/dist/index"), }, { find: "@budibase/types", From 3952cbc80c175c007f4551aa786b5cefafaa5a4e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 00:38:30 +0200 Subject: [PATCH 088/117] Undo adding plugin --- packages/builder/package.json | 1 - packages/builder/vite.config.js | 2 -- 2 files changed, 3 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index 98bac96859..ad6037d91a 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -98,7 +98,6 @@ "@babel/plugin-transform-runtime": "^7.13.10", "@babel/preset-env": "^7.13.12", "@babel/runtime": "^7.13.10", - "@rollup/plugin-commonjs": "25.0.3", "@rollup/plugin-replace": "^2.4.2", "@roxi/routify": "2.18.5", "@sveltejs/vite-plugin-svelte": "1.0.1", diff --git a/packages/builder/vite.config.js b/packages/builder/vite.config.js index 549a3971b4..4a0ffca8d4 100644 --- a/packages/builder/vite.config.js +++ b/packages/builder/vite.config.js @@ -1,5 +1,4 @@ import { svelte } from "@sveltejs/vite-plugin-svelte" -import commonjs from "@rollup/plugin-commonjs" import replace from "@rollup/plugin-replace" import { defineConfig, loadEnv } from "vite" import { viteStaticCopy } from "vite-plugin-static-copy" @@ -62,7 +61,6 @@ export default defineConfig(({ mode }) => { sourcemap: !isProduction, }, plugins: [ - commonjs(), svelte({ hot: !isProduction, emitCss: true, From 4f723720fe6728698b95df9b6af672b1ab35132a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 00:40:09 +0200 Subject: [PATCH 089/117] Fix tsBuildInfoFile --- packages/shared-core/tsconfig.json | 1 + packages/types/tsconfig.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/shared-core/tsconfig.json b/packages/shared-core/tsconfig.json index 29825689c8..f72933ff9b 100644 --- a/packages/shared-core/tsconfig.json +++ b/packages/shared-core/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": ".", "rootDir": "./src", "composite": true, + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", "paths": { "@budibase/types": ["../../types/src"] } diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index a2fd5207b4..66357a9d71 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "baseUrl": ".", "rootDir": "./src", - "composite": true + "composite": true, + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo" }, "exclude": ["node_modules", "dist"] } From 194e413026a5595dc84e4857e547f9609a90aea2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 00:56:43 +0200 Subject: [PATCH 090/117] Undo changes --- packages/bbui/package.json | 4 +- packages/bbui/rollup.config.js | 2 +- packages/builder/package.json | 14 +-- packages/client/package.json | 6 +- packages/sdk/package.json | 6 +- packages/string-templates/package.json | 6 +- packages/string-templates/rollup.config.js | 2 +- yarn.lock | 129 +++++++++++++++------ 8 files changed, 109 insertions(+), 60 deletions(-) diff --git a/packages/bbui/package.json b/packages/bbui/package.json index dbabe5c29b..e5d0ad6b57 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -17,9 +17,9 @@ "build": "rollup -c" }, "devDependencies": { - "@rollup/plugin-commonjs": "25.0.3", + "@rollup/plugin-commonjs": "^16.0.0", "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "15.1.0", + "@rollup/plugin-node-resolve": "^11.2.1", "cross-env": "^7.0.2", "nollup": "^0.14.1", "postcss": "^8.2.9", diff --git a/packages/bbui/rollup.config.js b/packages/bbui/rollup.config.js index 0abb9f667b..e285d548d6 100644 --- a/packages/bbui/rollup.config.js +++ b/packages/bbui/rollup.config.js @@ -13,8 +13,8 @@ export default { file: "dist/bbui.es.js", }, plugins: [ - commonjs(), resolve(), + commonjs(), svelte({ emitCss: true, }), diff --git a/packages/builder/package.json b/packages/builder/package.json index ad6037d91a..2f6d329877 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -115,7 +115,7 @@ "mochawesome-report-generator": "^6.2.0", "ncp": "^2.0.0", "rimraf": "^3.0.2", - "rollup": "^2.45.2", + "rollup": "^2.44.0", "rollup-plugin-copy": "^3.4.0", "start-server-and-test": "^1.12.1", "svelte": "^3.48.0", @@ -140,18 +140,6 @@ } ] }, - "dev:builder": { - "dependsOn": [ - { - "projects": [ - "@budibase/string-templates", - "@budibase/shared-core", - "@budibase/types" - ], - "target": "build" - } - ] - }, "test": { "dependsOn": [ { diff --git a/packages/client/package.json b/packages/client/package.json index f1f452eb40..689ec332e5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -48,11 +48,11 @@ }, "devDependencies": { "@rollup/plugin-alias": "^3.1.5", - "@rollup/plugin-commonjs": "25.0.3", + "@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-image": "^3.0.2", - "@rollup/plugin-node-resolve": "15.1.0", + "@rollup/plugin-node-resolve": "^11.2.1", "postcss": "^8.2.10", - "rollup": "^2.45.2", + "rollup": "^2.44.0", "rollup-plugin-json": "^4.0.0", "rollup-plugin-polyfill-node": "^0.8.0", "rollup-plugin-postcss": "^4.0.0", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index ed877a9fce..4d3c811b3d 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -14,9 +14,9 @@ "superagent": "^5.3.0" }, "devDependencies": { - "@rollup/plugin-commonjs": "25.0.3", - "@rollup/plugin-node-resolve": "15.1.0", - "rollup": "^2.45.2", + "@rollup/plugin-commonjs": "^18.0.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "rollup": "^2.44.0", "rollup-plugin-polyfill-node": "^0.8.0", "rollup-plugin-terser": "^7.0.2" } diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 489ed9ef78..d9416bad4c 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -33,17 +33,17 @@ "vm2": "^3.9.15" }, "devDependencies": { - "@rollup/plugin-commonjs": "25.0.3", + "@rollup/plugin-commonjs": "^17.1.0", "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "15.1.0", "doctrine": "^3.0.0", "jest": "29.6.2", "jest-environment-node": "29.6.2", "marked": "^4.0.10", - "rollup": "^2.45.2", + "rollup": "^2.36.2", "rollup-plugin-inject-process-env": "^1.3.1", "rollup-plugin-node-builtins": "^2.1.2", "rollup-plugin-node-globals": "^1.4.0", + "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^7.0.2", "typescript": "4.7.3" }, diff --git a/packages/string-templates/rollup.config.js b/packages/string-templates/rollup.config.js index a85162116e..d7f45489c9 100644 --- a/packages/string-templates/rollup.config.js +++ b/packages/string-templates/rollup.config.js @@ -1,5 +1,5 @@ import commonjs from "@rollup/plugin-commonjs" -import resolve from "@rollup/plugin-node-resolve" +import resolve from "rollup-plugin-node-resolve" import json from "@rollup/plugin-json" import { terser } from "rollup-plugin-terser" import builtins from "rollup-plugin-node-builtins" diff --git a/yarn.lock b/yarn.lock index af662b4c25..d1f22e7699 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4818,17 +4818,44 @@ dependencies: slash "^3.0.0" -"@rollup/plugin-commonjs@25.0.3": - version "25.0.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.3.tgz#eb5217ebae43d63a172b516655be270ed258bdcc" - integrity sha512-uBdtWr/H3BVcgm97MUdq2oJmqBR23ny1hOrWe2PKo9FTbjsGqg32jfasJUKYAI5ouqacjRnj65mBB/S79F+GQA== +"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" + integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== dependencies: - "@rollup/pluginutils" "^5.0.1" + "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" - estree-walker "^2.0.2" - glob "^8.0.3" - is-reference "1.2.1" - magic-string "^0.27.0" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-commonjs@^17.1.0": + version "17.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz#757ec88737dffa8aa913eb392fade2e45aef2a2d" + integrity sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-commonjs@^18.0.0": + version "18.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-18.1.0.tgz#5a760d757af168a50727c0ae080251fbfcc5eb02" + integrity sha512-h3e6T9rUxVMAQswpDIobfUHn/doMzM9sgkMrsMWCFLmB84PSoC8mV8tOloAJjSRwdqhXBqstlX2BwBpHJvbhxg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" "@rollup/plugin-image@^3.0.2": version "3.0.2" @@ -4854,17 +4881,17 @@ dependencies: "@rollup/pluginutils" "^3.0.8" -"@rollup/plugin-node-resolve@15.1.0": - version "15.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz#9ffcd8e8c457080dba89bb9fcb583a6778dc757e" - integrity sha512-xeZHCgsiZ9pzYVgAo9580eCGqwh/XCEUM9q6iQfGNocjgkufHAqC3exA+45URvhiYV8sBF9RlBai650eNs7AsA== +"@rollup/plugin-node-resolve@^11.2.1": + version "11.2.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz#82aa59397a29cd4e13248b106e6a4a1880362a60" + integrity sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg== dependencies: - "@rollup/pluginutils" "^5.0.1" - "@types/resolve" "1.20.2" + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" deepmerge "^4.2.2" - is-builtin-module "^3.2.1" is-module "^1.0.0" - resolve "^1.22.1" + resolve "^1.19.0" "@rollup/plugin-replace@^2.4.2": version "2.4.2" @@ -6535,10 +6562,19 @@ "@types/tough-cookie" "*" form-data "^2.5.0" -"@types/resolve@1.20.2": - version "1.20.2" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" - integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" "@types/responselike@^1.0.0": version "1.0.0" @@ -8425,7 +8461,7 @@ buildcheck@0.0.3: resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.3.tgz#70451897a95d80f7807e68fc412eb2e7e35ff4d5" integrity sha512-pziaA+p/wdVImfcbsZLNF32EiWyujlQLwolMqUQE8xpKNOH7KmZQaY8sXN7DGOEzPAElo9QTaeNRfGnf3iOJbA== -builtin-modules@^3.3.0: +builtin-modules@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== @@ -12819,7 +12855,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -14466,13 +14502,6 @@ is-buffer@^1.1.5, is-buffer@~1.1.6: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-builtin-module@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" - integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== - dependencies: - builtin-modules "^3.3.0" - is-callable@^1.1.3, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -14767,7 +14796,7 @@ is-property@^1.0.2: resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== -is-reference@1.2.1: +is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== @@ -20250,6 +20279,20 @@ posthog-js@^1.36.0: fflate "^0.4.1" rrweb-snapshot "^1.1.14" +posthog-node@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.0.7.tgz#a7a9525eebff23312117e57cff3ddac82afb2262" + integrity sha512-KTCwyU+PU1eAQtjy5ZSJ47mrxv2d/mMkxo+vvV5c+YqfE4mBAY1UPEPMv1nElb5Vq0UnxvyQXaUnOn8d8Xr6Eg== + dependencies: + axios "^0.21.1" + axios-retry "^3.1.9" + component-type "^1.2.1" + join-component "^1.1.0" + md5 "^2.3.0" + ms "^2.1.3" + remove-trailing-slash "^0.1.1" + uuid "^8.3.2" + posthog-node@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" @@ -21633,7 +21676,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== -resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.9.0: +resolve@^1.10.0, resolve@^1.11.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.21.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.9.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -21794,6 +21837,17 @@ rollup-plugin-node-globals@^1.4.0: process-es6 "^0.11.6" rollup-pluginutils "^2.3.1" +rollup-plugin-node-resolve@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" + integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== + dependencies: + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" + is-module "^1.0.0" + resolve "^1.11.1" + rollup-pluginutils "^2.8.1" + rollup-plugin-polyfill-node@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.8.0.tgz#859c070822f5e38d221e5b4238cb34aa894c2b19" @@ -21871,14 +21925,21 @@ rollup-pluginutils@^1.3.1: estree-walker "^0.2.1" minimatch "^3.0.2" -rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.2: +rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: version "2.8.2" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== dependencies: estree-walker "^0.6.1" -rollup@^2.45.2, rollup@^2.79.1: +rollup@2.45.2: + version "2.45.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48" + integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ== + optionalDependencies: + fsevents "~2.3.1" + +rollup@^2.36.2, rollup@^2.44.0, rollup@^2.45.2, rollup@^2.79.1: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== From d5e614222d0b34aae7c2a7b1585116386438c494 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 01:02:22 +0200 Subject: [PATCH 091/117] Undo changes --- packages/pro | 2 +- yarn.lock | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/pro b/packages/pro index d37a88a5ac..cf14c6a1bb 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit d37a88a5acae873a7635edbd9c5bae3c4e30f4c0 +Subproject commit cf14c6a1bbb51b4c047db991370d6c0dd1566a44 diff --git a/yarn.lock b/yarn.lock index d1f22e7699..827c94a176 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20279,20 +20279,6 @@ posthog-js@^1.36.0: fflate "^0.4.1" rrweb-snapshot "^1.1.14" -posthog-node@1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.0.7.tgz#a7a9525eebff23312117e57cff3ddac82afb2262" - integrity sha512-KTCwyU+PU1eAQtjy5ZSJ47mrxv2d/mMkxo+vvV5c+YqfE4mBAY1UPEPMv1nElb5Vq0UnxvyQXaUnOn8d8Xr6Eg== - dependencies: - axios "^0.21.1" - axios-retry "^3.1.9" - component-type "^1.2.1" - join-component "^1.1.0" - md5 "^2.3.0" - ms "^2.1.3" - remove-trailing-slash "^0.1.1" - uuid "^8.3.2" - posthog-node@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.3.0.tgz#804ed2f213a2f05253f798bf9569d55a9cad94f7" From 1df57261cd7dbbad1ecaa73c432975428f127664 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 01:03:26 +0200 Subject: [PATCH 092/117] Update ref --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index cf14c6a1bb..cf3bef2aad 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit cf14c6a1bbb51b4c047db991370d6c0dd1566a44 +Subproject commit cf3bef2aad9c739111b306fd0712397adc363f81 From 803c79fa55db58c002f5b0d674f210b0b282aece Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 01:57:13 +0200 Subject: [PATCH 093/117] Fix build --- packages/shared-core/tsconfig.build.json | 2 +- packages/types/tsconfig.build.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared-core/tsconfig.build.json b/packages/shared-core/tsconfig.build.json index 8b73754a43..6930e3cb99 100644 --- a/packages/shared-core/tsconfig.build.json +++ b/packages/shared-core/tsconfig.build.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es6", - "module": "commonjs", + "moduleResolution": "node", "lib": ["es2020"], "strict": true, "noImplicitAny": true, diff --git a/packages/types/tsconfig.build.json b/packages/types/tsconfig.build.json index 8df63fac57..f2c4c7667b 100644 --- a/packages/types/tsconfig.build.json +++ b/packages/types/tsconfig.build.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es6", - "module": "commonjs", + "moduleResolution": "node", "lib": ["es2020"], "strict": true, "noImplicitAny": true, From ec79f612bc23065f2b39a0409eed71646c26ef42 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 02:16:27 +0200 Subject: [PATCH 094/117] Fix execution order --- packages/builder/package.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/builder/package.json b/packages/builder/package.json index 2f6d329877..c24d031518 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -140,6 +140,16 @@ } ] }, + "dev:builder": { + "dependsOn": [ + { + "projects": [ + "@budibase/shared-core" + ], + "target": "dev:builder" + } + ] + }, "test": { "dependsOn": [ { From 6c60ea79dd8d65dfaa8e62f769bd7f72ee51bedd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 02:17:51 +0200 Subject: [PATCH 095/117] Fix --- packages/builder/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index c24d031518..75c845a4a4 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -146,7 +146,7 @@ "projects": [ "@budibase/shared-core" ], - "target": "dev:builder" + "target": "build" } ] }, From 839f4f9545bad2a652a4cca91b08252c780479eb Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 02:20:07 +0200 Subject: [PATCH 096/117] Fix more ordering --- packages/builder/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/package.json b/packages/builder/package.json index 75c845a4a4..6b5399c942 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -144,6 +144,7 @@ "dependsOn": [ { "projects": [ + "@budibase/string-templates", "@budibase/shared-core" ], "target": "build" From 77a22470cb86cdf9e867a97d6adc08f84efe7932 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 09:59:14 +0200 Subject: [PATCH 097/117] Use path for shared-core in backend-core --- packages/backend-core/jest.config.ts | 1 + packages/backend-core/tsconfig.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/jest.config.ts b/packages/backend-core/jest.config.ts index 52d042dbac..3f1065ead2 100644 --- a/packages/backend-core/jest.config.ts +++ b/packages/backend-core/jest.config.ts @@ -9,6 +9,7 @@ const baseConfig: Config.InitialProjectOptions = { }, moduleNameMapper: { "@budibase/types": "/../types/src", + "@budibase/shared-core": ["/../shared-core/src"], }, } diff --git a/packages/backend-core/tsconfig.json b/packages/backend-core/tsconfig.json index 2993ff84ed..128814b955 100644 --- a/packages/backend-core/tsconfig.json +++ b/packages/backend-core/tsconfig.json @@ -4,7 +4,8 @@ "composite": true, "baseUrl": ".", "paths": { - "@budibase/types": ["../types/src"] + "@budibase/types": ["../types/src"], + "@budibase/shared-core": ["../shared-core/src"] } }, "exclude": ["node_modules", "dist"] From bb861e45a863acc546ce1693ec7b663ca5f3fd19 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:02:39 +0200 Subject: [PATCH 098/117] Remove unnecessary nx dependencies for build --- nx.json | 11 +---------- packages/backend-core/package.json | 14 -------------- packages/shared-core/package.json | 24 ------------------------ 3 files changed, 1 insertion(+), 48 deletions(-) diff --git a/nx.json b/nx.json index 9680f96dc1..8176bae82c 100644 --- a/nx.json +++ b/nx.json @@ -8,14 +8,5 @@ } } }, - "targetDefaults": { - "dev:builder": { - "dependsOn": [ - { - "projects": ["@budibase/string-templates"], - "target": "build" - } - ] - } - } + "targetDefaults": {} } diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index b842abb389..20ea53b420 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -89,19 +89,5 @@ "tsconfig-paths": "4.0.0", "typescript": "4.7.3" }, - "nx": { - "targets": { - "build": { - "dependsOn": [ - { - "projects": [ - "@budibase/types" - ], - "target": "build" - } - ] - } - } - }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index 838b8c0bea..9c8861c9d8 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -25,29 +25,5 @@ "concurrently": "^7.6.0", "rimraf": "3.0.2", "typescript": "4.7.3" - }, - "nx": { - "targets": { - "build": { - "dependsOn": [ - { - "projects": [ - "@budibase/types" - ], - "target": "build" - } - ] - }, - "dev:builder": { - "dependsOn": [ - { - "projects": [ - "@budibase/types" - ], - "target": "build" - } - ] - } - } } } From 52940b87fb2752ebc024a6a646b989fa6184be68 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:07:03 +0200 Subject: [PATCH 099/117] Fix build order --- packages/bbui/package.json | 6 +++--- packages/builder/package.json | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/bbui/package.json b/packages/bbui/package.json index e5d0ad6b57..8a9318ba94 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -98,13 +98,13 @@ { "projects": [ "@budibase/string-templates", - "@budibase/shared-core" + "@budibase/shared-core", + "@budibase/types" ], "target": "build" } ] } } - }, - "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" + } } diff --git a/packages/builder/package.json b/packages/builder/package.json index 6b5399c942..27a1b2f6c2 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -134,7 +134,8 @@ { "projects": [ "@budibase/string-templates", - "@budibase/shared-core" + "@budibase/shared-core", + "@budibase/types" ], "target": "build" } From 24d4ac2b5f8747fa7c304dd89071c6e83f95b125 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:12:41 +0200 Subject: [PATCH 100/117] Fix cli order --- packages/cli/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 4437469be2..cb59bf7c69 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -71,7 +71,8 @@ "dependsOn": [ { "projects": [ - "@budibase/backend-core" + "@budibase/backend-core", + "@budibase/string-templates" ], "target": "build" } From c2798493401ce358c96c0fab8780d2bb06a836e2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:17:58 +0200 Subject: [PATCH 101/117] Fix orders on server/worker --- packages/server/package.json | 10 ---------- packages/worker/package.json | 14 -------------- 2 files changed, 24 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index ba663df9c3..c0ab3a4f3b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -182,16 +182,6 @@ }, "nx": { "targets": { - "dev:builder": { - "dependsOn": [ - { - "projects": [ - "@budibase/backend-core" - ], - "target": "build" - } - ] - }, "test": { "dependsOn": [ { diff --git a/packages/worker/package.json b/packages/worker/package.json index a1b9e68878..6377bed6fc 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -103,19 +103,5 @@ "typescript": "4.7.3", "update-dotenv": "1.1.1" }, - "nx": { - "targets": { - "dev:builder": { - "dependsOn": [ - { - "projects": [ - "@budibase/backend-core" - ], - "target": "build" - } - ] - } - } - }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } From acd9d8ae545b274fb060b957c9d351bea153e5f7 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:22:42 +0200 Subject: [PATCH 102/117] Fix test issues --- packages/builder/package.json | 6 ++++-- packages/server/package.json | 15 --------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index 27a1b2f6c2..c4b49f2f64 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -146,7 +146,8 @@ { "projects": [ "@budibase/string-templates", - "@budibase/shared-core" + "@budibase/shared-core", + "@budibase/types" ], "target": "build" } @@ -157,7 +158,8 @@ { "projects": [ "@budibase/shared-core", - "@budibase/string-templates" + "@budibase/string-templates", + "@budibase/types" ], "target": "build" } diff --git a/packages/server/package.json b/packages/server/package.json index c0ab3a4f3b..8d4f096ac1 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -180,20 +180,5 @@ "optionalDependencies": { "oracledb": "5.3.0" }, - "nx": { - "targets": { - "test": { - "dependsOn": [ - { - "projects": [ - "@budibase/string-templates", - "@budibase/shared-core" - ], - "target": "build" - } - ] - } - } - }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" } From b4eccc4163f1d1bd898de54bf91aa224fab17ac5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:30:54 +0200 Subject: [PATCH 103/117] Fix dev nx order --- packages/builder/package.json | 4 ++-- packages/client/package.json | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index c4b49f2f64..235fce97ec 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -133,8 +133,8 @@ "dependsOn": [ { "projects": [ - "@budibase/string-templates", "@budibase/shared-core", + "@budibase/string-templates", "@budibase/types" ], "target": "build" @@ -145,8 +145,8 @@ "dependsOn": [ { "projects": [ - "@budibase/string-templates", "@budibase/shared-core", + "@budibase/string-templates", "@budibase/types" ], "target": "build" diff --git a/packages/client/package.json b/packages/client/package.json index 689ec332e5..67fd4ee3f9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -76,6 +76,17 @@ "target": "build" } ] + }, + "dev:builder": { + "dependsOn": [ + { + "projects": [ + "@budibase/string-templates", + "@budibase/shared-core" + ], + "target": "build" + } + ] } } }, From a2d7b0f7773c93f66b1a04a7f3a9c6855ba3d15a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:41:46 +0200 Subject: [PATCH 104/117] Fix client order --- packages/client/package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/package.json b/packages/client/package.json index 67fd4ee3f9..4ff8b9c67a 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -70,8 +70,9 @@ "dependsOn": [ { "projects": [ + "@budibase/shared-core", "@budibase/string-templates", - "@budibase/shared-core" + "@budibase/types" ], "target": "build" } @@ -81,8 +82,9 @@ "dependsOn": [ { "projects": [ + "@budibase/shared-core", "@budibase/string-templates", - "@budibase/shared-core" + "@budibase/types" ], "target": "build" } From 7944f72820d8b6febbba9bb409bf253cbede8f4c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 10:59:15 +0200 Subject: [PATCH 105/117] Fix integration tests --- .github/workflows/budibase_ci.yml | 2 +- packages/shared-core/package.json | 3 ++- packages/types/package.json | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 9509a22e99..ba8d9f53ea 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -154,7 +154,7 @@ jobs: node-version: 14.x cache: "yarn" - run: yarn - - run: yarn build + - run: yarn build --projects=@budibase/server,@budibase/worker,@budibase/shared-core - name: Run tests run: | cd qa-core diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index 9c8861c9d8..98ee89999b 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -6,7 +6,8 @@ "types": "src/index.ts", "exports": { ".": { - "import": "./dist/index.js" + "import": "./dist/index.js", + "require": "./src/index.ts" } }, "author": "Budibase", diff --git a/packages/types/package.json b/packages/types/package.json index 04bd3bc1d1..96ab8cb095 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -6,7 +6,8 @@ "types": "src/index.ts", "exports": { ".": { - "import": "./dist/index.js" + "import": "./dist/index.js", + "require": "./src/index.ts" } }, "author": "Budibase", From b6c4bd7698786c36faa55a1e194708cff3f0ad3d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 11:01:47 +0200 Subject: [PATCH 106/117] Remove unnecessary build --- .github/workflows/budibase_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index ba8d9f53ea..c56894335c 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -154,7 +154,7 @@ jobs: node-version: 14.x cache: "yarn" - run: yarn - - run: yarn build --projects=@budibase/server,@budibase/worker,@budibase/shared-core + - run: yarn build --projects=@budibase/server,@budibase/worker - name: Run tests run: | cd qa-core From 416899b3b86e2cd4be124cb2b2080431e92b7183 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 11:09:59 +0200 Subject: [PATCH 107/117] Build client for integration test --- .github/workflows/budibase_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index c56894335c..b84fd54fae 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -154,7 +154,7 @@ jobs: node-version: 14.x cache: "yarn" - run: yarn - - run: yarn build --projects=@budibase/server,@budibase/worker + - run: yarn build --projects=@budibase/server,@budibase/worker,@budibase/client - name: Run tests run: | cd qa-core From fcba916f0cf49e8e1b20dbed544cb47dcbff6295 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 11:25:52 +0200 Subject: [PATCH 108/117] Add bail in jest --- packages/server/scripts/test.sh | 4 ++-- packages/worker/scripts/test.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/server/scripts/test.sh b/packages/server/scripts/test.sh index f3f679c4da..bc21ec53ed 100644 --- a/packages/server/scripts/test.sh +++ b/packages/server/scripts/test.sh @@ -5,8 +5,8 @@ if [[ -n $CI ]] then # --runInBand performs better in ci where resources are limited export NODE_OPTIONS="--max-old-space-size=4096" - echo "jest --coverage --runInBand --forceExit" - jest --coverage --runInBand --forceExit + echo "jest --coverage --runInBand --forceExit --bail" + jest --coverage --runInBand --forceExit --bail else # --maxWorkers performs better in development echo "jest --coverage --maxWorkers=2 --forceExit" diff --git a/packages/worker/scripts/test.sh b/packages/worker/scripts/test.sh index d7659a9318..e9755f6afe 100644 --- a/packages/worker/scripts/test.sh +++ b/packages/worker/scripts/test.sh @@ -4,8 +4,8 @@ set -e if [[ -n $CI ]] then # --runInBand performs better in ci where resources are limited - echo "jest --coverage --runInBand --forceExit" - jest --coverage --runInBand --forceExit + echo "jest --coverage --runInBand --forceExit --bail" + jest --coverage --runInBand --forceExit --bail else # --maxWorkers performs better in development echo "jest --coverage --maxWorkers=2 --forceExit" From 1ef8d118cbf9d2e97686bea1dd036c88e22ad036 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 12:04:54 +0200 Subject: [PATCH 109/117] Remove unnecessary package.json/gitHead --- packages/backend-core/package.json | 3 +-- packages/builder/package.json | 3 +-- packages/client/package.json | 3 +-- packages/server/package.json | 3 +-- packages/string-templates/package.json | 3 +-- packages/worker/package.json | 3 +-- 6 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 20ea53b420..4631b090fe 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -88,6 +88,5 @@ "ts-node": "10.8.1", "tsconfig-paths": "4.0.0", "typescript": "4.7.3" - }, - "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" + } } diff --git a/packages/builder/package.json b/packages/builder/package.json index 235fce97ec..56834f79dd 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -166,6 +166,5 @@ ] } } - }, - "gitHead": "115189f72a850bfb52b65ec61d932531bf327072" + } } diff --git a/packages/client/package.json b/packages/client/package.json index 4ff8b9c67a..a5ee304610 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -91,6 +91,5 @@ ] } } - }, - "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" + } } diff --git a/packages/server/package.json b/packages/server/package.json index 8d4f096ac1..7d0d8f5feb 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -179,6 +179,5 @@ }, "optionalDependencies": { "oracledb": "5.3.0" - }, - "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" + } } diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index d9416bad4c..4dc4c17ad8 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -46,6 +46,5 @@ "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^7.0.2", "typescript": "4.7.3" - }, - "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" + } } diff --git a/packages/worker/package.json b/packages/worker/package.json index 6377bed6fc..a71e9519d9 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -102,6 +102,5 @@ "tsconfig-paths": "4.0.0", "typescript": "4.7.3", "update-dotenv": "1.1.1" - }, - "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" + } } From bcae0371d37362525fddeaf0312b07513088e8d0 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 12:24:43 +0200 Subject: [PATCH 110/117] Do not ignore *.tsbuildinfo as they should never be created outside the dist folder --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index b3dc8af0d4..22a7313e66 100644 --- a/.gitignore +++ b/.gitignore @@ -101,8 +101,6 @@ packages/builder/cypress.env.json packages/builder/cypress/reports stats.html -# TypeScript cache -*.tsbuildinfo # plugins budibase-component From 3d2954e1d508fb625f4711c429c6880a8705857a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 13:09:37 +0200 Subject: [PATCH 111/117] Add primary display --- packages/types/src/documents/app/view.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts index 10fcac2805..3fe8b4a500 100644 --- a/packages/types/src/documents/app/view.ts +++ b/packages/types/src/documents/app/view.ts @@ -18,6 +18,7 @@ export interface ViewV2 { version: 2 id: string name: string + primaryDisplay?: string tableId: string query?: SearchFilters sort?: { From 398cf99b4ff0feeccac421f87b5c5fba311bcb37 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 13:17:20 +0200 Subject: [PATCH 112/117] Handle primaryDisplay on controller --- packages/server/src/api/controllers/view/viewsV2.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index cd28a1b55f..82e536f62d 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -66,13 +66,14 @@ export async function create(ctx: Ctx) { const schemaUI = await parseSchemaUI(ctx, view) - const parsedView: Omit = { + const parsedView: Omit, "id" | "version"> = { name: view.name, tableId: view.tableId, query: view.query, sort: view.sort, columns: view.schema && Object.keys(view.schema), schemaUI, + primaryDisplay: view.primaryDisplay, } const result = await sdk.views.create(tableId, parsedView) ctx.status = 201 @@ -95,7 +96,7 @@ export async function update(ctx: Ctx) { const { tableId } = view const schemaUI = await parseSchemaUI(ctx, view) - const parsedView: ViewV2 = { + const parsedView: RequiredKeys = { id: view.id, name: view.name, version: view.version, @@ -104,6 +105,7 @@ export async function update(ctx: Ctx) { sort: view.sort, columns: view.schema && Object.keys(view.schema), schemaUI, + primaryDisplay: view.primaryDisplay, } const result = await sdk.views.update(tableId, parsedView) From a3a1e29350edc801e0989557cc258d45dc15e120 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 13:37:36 +0200 Subject: [PATCH 113/117] Handle undefineds --- packages/server/src/api/controllers/view/viewsV2.ts | 8 +++++++- packages/server/src/sdk/app/views/index.ts | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index 82e536f62d..80a115e365 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -49,12 +49,18 @@ async function parseSchemaUI(ctx: Ctx, view: CreateViewRequest) { const schemaUI = view.schema && Object.entries(view.schema).reduce((p, [fieldName, schemaValue]) => { - p[fieldName] = { + const fieldSchema: RequiredKeys = { order: schemaValue.order, width: schemaValue.width, visible: schemaValue.visible, icon: schemaValue.icon, } + Object.entries(fieldSchema) + .filter(([_, val]) => val === undefined) + .forEach(([key]) => { + delete fieldSchema[key as keyof UIFieldMetadata] + }) + p[fieldName] = fieldSchema return p }, {} as Record>) return schemaUI diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 3dfa82df0d..7e75f22060 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -86,6 +86,7 @@ export function enrichSchema(view: View | ViewV2, tableSchema: TableSchema) { : schema[fieldName].order, } } + delete view.schemaUI } if (view?.columns?.length) { @@ -97,6 +98,7 @@ export function enrichSchema(view: View | ViewV2, tableSchema: TableSchema) { pickedSchema[fieldName] = { ...schema[fieldName] } } schema = pickedSchema + delete view.columns } return { From e11279311dbc75424492c695d10661260417fc18 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 2 Aug 2023 13:37:58 +0200 Subject: [PATCH 114/117] Add tests --- .../src/api/routes/tests/viewV2.spec.ts | 79 ++++++++++++++----- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 452d15bde0..fc97e657c5 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -6,6 +6,7 @@ import { SortOrder, SortType, Table, + UpdateViewRequest, ViewV2, } from "@budibase/types" import { generator } from "@budibase/backend-core/tests" @@ -34,20 +35,6 @@ function priceTable(): Table { describe("/v2/views", () => { const config = setup.getConfig() - const viewFilters: Omit = { - query: { allOr: false, equal: { field: "value" } }, - sort: { - field: "fieldToSort", - order: SortOrder.DESCENDING, - type: SortType.STRING, - }, - schema: { - name: { - visible: true, - }, - }, - } - afterAll(setup.afterAll) beforeAll(async () => { @@ -70,20 +57,30 @@ describe("/v2/views", () => { }) }) - it("can persist views with queries", async () => { - const newView: CreateViewRequest = { + it("can persist views with all fields", async () => { + const newView: Required = { name: generator.name(), tableId: config.table!._id!, - query: viewFilters.query, - sort: viewFilters.sort, + primaryDisplay: generator.word(), + query: { allOr: false, equal: { field: "value" } }, + sort: { + field: "fieldToSort", + order: SortOrder.DESCENDING, + type: SortType.STRING, + }, + schema: { + name: { + visible: true, + }, + }, } - delete newView.schema const res = await config.api.viewV2.create(newView) expect(res).toEqual({ ...newView, - query: viewFilters.query, - sort: viewFilters.sort, + schema: undefined, + columns: ["name"], + schemaUI: newView.schema, id: expect.any(String), version: 2, }) @@ -210,6 +207,46 @@ describe("/v2/views", () => { }) }) + it("can update all fields", async () => { + const tableId = config.table!._id! + + const updatedData: Required = { + version: view.version, + id: view.id, + tableId, + name: view.name, + primaryDisplay: generator.word(), + query: { equal: { [generator.word()]: generator.word() } }, + sort: { + field: generator.word(), + order: SortOrder.DESCENDING, + type: SortType.STRING, + }, + schema: { + Category: { + visible: false, + }, + }, + } + await config.api.viewV2.update(updatedData) + + expect(await config.api.table.get(tableId)).toEqual({ + ...config.table, + views: { + [view.name]: { + ...updatedData, + schema: { + Category: expect.objectContaining({ + visible: false, + }), + }, + }, + }, + _rev: expect.any(String), + updatedAt: expect.any(String), + }) + }) + it("can update an existing view name", async () => { const tableId = config.table!._id! await config.api.viewV2.update({ ...view, name: "View B" }) From 063942d21cc79791981518ac2907c0969931d677 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 2 Aug 2023 12:22:47 +0000 Subject: [PATCH 115/117] Bump version to 2.8.29-alpha.12 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 74a1392bf9..13fc86bb18 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.29-alpha.11", + "version": "2.8.29-alpha.12", "npmClient": "yarn", "packages": [ "packages/*" From f730b155d49e0ba664af2ec7013da1ce9198d1c6 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 2 Aug 2023 12:49:20 +0000 Subject: [PATCH 116/117] Bump version to 2.8.29-alpha.13 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 13fc86bb18..bb259193c7 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.29-alpha.12", + "version": "2.8.29-alpha.13", "npmClient": "yarn", "packages": [ "packages/*" From de09dc86f7d2043b7b7b90f8f3a4826e454e97f8 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 2 Aug 2023 13:08:45 +0000 Subject: [PATCH 117/117] Bump version to 2.8.29-alpha.14 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index bb259193c7..399c4e8b42 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.29-alpha.13", + "version": "2.8.29-alpha.14", "npmClient": "yarn", "packages": [ "packages/*"