Merge branch 'master' of github.com:Budibase/budibase into labday/sqs

This commit is contained in:
mike12345567 2023-11-22 12:52:27 +00:00
commit 90a624884e
224 changed files with 691 additions and 420 deletions

View File

@ -57,7 +57,10 @@
"destructuredArrayIgnorePattern": "^_" "destructuredArrayIgnorePattern": "^_"
} }
], ],
"import/no-relative-packages": "error" "import/no-relative-packages": "error",
"import/export": "error",
"import/no-duplicates": "error",
"import/newline-after-import": "error"
}, },
"globals": { "globals": {
"GeolocationPositionError": true "GeolocationPositionError": true

View File

@ -1,24 +0,0 @@
#!/bin/bash
echo ${TARGETBUILD} > /buildtarget.txt
if [[ "${TARGETBUILD}" = "aas" ]]; then
# Azure AppService uses /home for persistent data & SSH on port 2222
DATA_DIR="${DATA_DIR:-/home}"
WEBSITES_ENABLE_APP_SERVICE_STORAGE=true
mkdir -p $DATA_DIR/{search,minio,couch}
mkdir -p $DATA_DIR/couch/{dbs,views}
chown -R couchdb:couchdb $DATA_DIR/couch/
apt update
apt-get install -y openssh-server
echo "root:Docker!" | chpasswd
mkdir -p /tmp
chmod +x /tmp/ssh_setup.sh \
&& (sleep 1;/tmp/ssh_setup.sh 2>&1 > /dev/null)
cp /etc/sshd_config /etc/ssh/sshd_config
/etc/init.d/ssh restart
sed -i "s#DATA_DIR#/home#g" /opt/clouseau/clouseau.ini
sed -i "s#DATA_DIR#/home#g" /opt/couchdb/etc/local.ini
else
sed -i "s#DATA_DIR#/data#g" /opt/clouseau/clouseau.ini
sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini
fi

View File

@ -25,7 +25,7 @@ if [[ $(curl -s -w "%{http_code}\n" http://localhost:4002/health -o /dev/null) -
healthy=false healthy=false
fi fi
if [[ $(curl -s -w "%{http_code}\n" http://localhost:5984/ -o /dev/null) -ne 200 ]]; then if [[ $(curl -s -w "%{http_code}\n" http://localhost:5984/_up -o /dev/null) -ne 200 ]]; then
echo 'ERROR: CouchDB is not running'; echo 'ERROR: CouchDB is not running';
healthy=false healthy=false
fi fi

View File

@ -1,5 +1,6 @@
const _passport = require("koa-passport") const _passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy const LocalStrategy = require("passport-local").Strategy
import { getGlobalDB } from "../context" import { getGlobalDB } from "../context"
import { Cookie } from "../constants" import { Cookie } from "../constants"
import { getSessionsForUser, invalidateSessions } from "../security/sessions" import { getSessionsForUser, invalidateSessions } from "../security/sessions"
@ -26,6 +27,7 @@ import { clearCookie, getCookie } from "../utils"
import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso" import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso"
const refresh = require("passport-oauth2-refresh") const refresh = require("passport-oauth2-refresh")
export { export {
auditLog, auditLog,
authError, authError,

View File

@ -17,7 +17,6 @@ import { DocumentType, SEPARATOR } from "../constants"
import { CacheKey, TTL, withCache } from "../cache" import { CacheKey, TTL, withCache } from "../cache"
import * as context from "../context" import * as context from "../context"
import env from "../environment" import env from "../environment"
import environment from "../environment"
// UTILS // UTILS
@ -181,10 +180,10 @@ export async function getGoogleDatasourceConfig(): Promise<
} }
export function getDefaultGoogleConfig(): GoogleInnerConfig | undefined { export function getDefaultGoogleConfig(): GoogleInnerConfig | undefined {
if (environment.GOOGLE_CLIENT_ID && environment.GOOGLE_CLIENT_SECRET) { if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
return { return {
clientID: environment.GOOGLE_CLIENT_ID!, clientID: env.GOOGLE_CLIENT_ID!,
clientSecret: environment.GOOGLE_CLIENT_SECRET!, clientSecret: env.GOOGLE_CLIENT_SECRET!,
activated: true, activated: true,
} }
} }

View File

@ -1,4 +1,5 @@
import { prefixed, DocumentType } from "@budibase/types" import { prefixed, DocumentType } from "@budibase/types"
export { export {
SEPARATOR, SEPARATOR,
UNICODE_MAX, UNICODE_MAX,

View File

@ -6,6 +6,7 @@ import { AppState, DeletedApp, getAppMetadata } from "../cache/appMetadata"
import { isDevApp, isDevAppID, getProdAppID } from "../docIds/conversions" import { isDevApp, isDevAppID, getProdAppID } from "../docIds/conversions"
import { App, Database } from "@budibase/types" import { App, Database } from "@budibase/types"
import { getStartEndKeyURL } from "../docIds" import { getStartEndKeyURL } from "../docIds"
export * from "../docIds" export * from "../docIds"
/** /**

View File

@ -1,5 +1,6 @@
import { APP_DEV_PREFIX, APP_PREFIX } from "../constants" import { APP_DEV_PREFIX, APP_PREFIX } from "../constants"
import { App } from "@budibase/types" import { App } from "@budibase/types"
const NO_APP_ERROR = "No app provided" const NO_APP_ERROR = "No app provided"
export function isDevAppID(appId?: string) { export function isDevAppID(appId?: string) {

View File

@ -1,2 +1,3 @@
import PosthogProcessor from "./PosthogProcessor" import PosthogProcessor from "./PosthogProcessor"
export default PosthogProcessor export default PosthogProcessor

View File

@ -1,7 +1,9 @@
import { testEnv } from "../../../../../tests/extra" import { testEnv } from "../../../../../tests/extra"
import PosthogProcessor from "../PosthogProcessor" import PosthogProcessor from "../PosthogProcessor"
import { Event, IdentityType, Hosting } from "@budibase/types" import { Event, IdentityType, Hosting } from "@budibase/types"
const tk = require("timekeeper") const tk = require("timekeeper")
import * as cache from "../../../../cache/generic" import * as cache from "../../../../cache/generic"
import { CacheKey } from "../../../../cache/generic" import { CacheKey } from "../../../../cache/generic"
import * as context from "../../../../context" import * as context from "../../../../context"

View File

@ -1,5 +1,6 @@
import env from "../environment" import env from "../environment"
import * as context from "../context" import * as context from "../context"
export * from "./installation" export * from "./installation"
/** /**

View File

@ -38,6 +38,7 @@ export * as docIds from "./docIds"
// circular dependencies // circular dependencies
import * as context from "./context" import * as context from "./context"
import * as _tenancy from "./tenancy" import * as _tenancy from "./tenancy"
export const tenancy = { export const tenancy = {
..._tenancy, ..._tenancy,
...context, ...context,

View File

@ -1,7 +1,6 @@
import { newid } from "./utils" import { newid } from "./utils"
import * as events from "./events" import * as events from "./events"
import { StaticDatabases } from "./db" import { StaticDatabases, doWithDB } from "./db"
import { doWithDB } from "./db"
import { Installation, IdentityType, Database } from "@budibase/types" import { Installation, IdentityType, Database } from "@budibase/types"
import * as context from "./context" import * as context from "./context"
import semver from "semver" import semver from "semver"

View File

@ -1,4 +1,5 @@
import { Header } from "../../constants" import { Header } from "../../constants"
const correlator = require("correlation-id") const correlator = require("correlation-id")
export const setHeader = (headers: any) => { export const setHeader = (headers: any) => {

View File

@ -1,5 +1,6 @@
import { Header } from "../../constants" import { Header } from "../../constants"
import { v4 as uuid } from "uuid" import { v4 as uuid } from "uuid"
const correlator = require("correlation-id") const correlator = require("correlation-id")
const correlation = (ctx: any, next: any) => { const correlation = (ctx: any, next: any) => {

View File

@ -1,9 +1,12 @@
import env from "../../environment" import env from "../../environment"
import { logger } from "./logger" import { logger } from "./logger"
import { IncomingMessage } from "http" import { IncomingMessage } from "http"
const pino = require("koa-pino-logger") const pino = require("koa-pino-logger")
import { Options } from "pino-http" import { Options } from "pino-http"
import { Ctx } from "@budibase/types" import { Ctx } from "@budibase/types"
const correlator = require("correlation-id") const correlator = require("correlation-id")
export function pinoSettings(): Options { export function pinoSettings(): Options {

View File

@ -2,6 +2,7 @@ export * as local from "./passport/local"
export * as google from "./passport/sso/google" export * as google from "./passport/sso/google"
export * as oidc from "./passport/sso/oidc" export * as oidc from "./passport/sso/oidc"
import * as datasourceGoogle from "./passport/datasource/google" import * as datasourceGoogle from "./passport/datasource/google"
export const datasource = { export const datasource = {
google: datasourceGoogle, google: datasourceGoogle,
} }

View File

@ -8,6 +8,7 @@ import {
SaveSSOUserFunction, SaveSSOUserFunction,
GoogleInnerConfig, GoogleInnerConfig,
} from "@budibase/types" } from "@budibase/types"
const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy
export function buildVerifyFn(saveUserFn: SaveSSOUserFunction) { export function buildVerifyFn(saveUserFn: SaveSSOUserFunction) {

View File

@ -6,6 +6,7 @@ const mockStrategy = require("passport-google-oauth").OAuth2Strategy
jest.mock("../sso") jest.mock("../sso")
import * as _sso from "../sso" import * as _sso from "../sso"
const sso = jest.mocked(_sso) const sso = jest.mocked(_sso)
const mockSaveUserFn = jest.fn() const mockSaveUserFn = jest.fn()

View File

@ -11,6 +11,7 @@ const mockSaveUser = jest.fn()
jest.mock("../../../../users") jest.mock("../../../../users")
import * as _users from "../../../../users" import * as _users from "../../../../users"
const users = jest.mocked(_users) const users = jest.mocked(_users)
const getErrorMessage = () => { const getErrorMessage = () => {

View File

@ -5,6 +5,7 @@ import { structures } from "../../../tests"
import { ContextUser, ServiceType } from "@budibase/types" import { ContextUser, ServiceType } from "@budibase/types"
import { doInAppContext } from "../../context" import { doInAppContext } from "../../context"
import env from "../../environment" import env from "../../environment"
env._set("SERVICE_TYPE", ServiceType.APPS) env._set("SERVICE_TYPE", ServiceType.APPS)
const appId = "app_aaa" const appId = "app_aaa"

View File

@ -1,4 +1,5 @@
const sanitize = require("sanitize-s3-objectkey") const sanitize = require("sanitize-s3-objectkey")
import AWS from "aws-sdk" import AWS from "aws-sdk"
import stream, { Readable } from "stream" import stream, { Readable } from "stream"
import fetch from "node-fetch" import fetch from "node-fetch"

View File

@ -1,7 +1,12 @@
import { BuiltinPermissionID, PermissionLevel } from "./permissions" import { BuiltinPermissionID, PermissionLevel } from "./permissions"
import { prefixRoleID, getRoleParams, DocumentType, SEPARATOR } from "../db" import {
prefixRoleID,
getRoleParams,
DocumentType,
SEPARATOR,
doWithDB,
} from "../db"
import { getAppDB } from "../context" import { getAppDB } from "../context"
import { doWithDB } from "../db"
import { Screen, Role as RoleDoc } from "@budibase/types" import { Screen, Role as RoleDoc } from "@budibase/types"
import cloneDeep from "lodash/fp/cloneDeep" import cloneDeep from "lodash/fp/cloneDeep"

View File

@ -1,6 +1,7 @@
const redis = require("../redis/init") const redis = require("../redis/init")
const { v4: uuidv4 } = require("uuid") const { v4: uuidv4 } = require("uuid")
const { logWarn } = require("../logging") const { logWarn } = require("../logging")
import env from "../environment" import env from "../environment"
import { import {
Session, Session,

View File

@ -1,9 +1,8 @@
import env from "../environment" import env from "../environment"
import * as eventHelpers from "./events" import * as eventHelpers from "./events"
import * as accounts from "../accounts"
import * as accountSdk from "../accounts" import * as accountSdk from "../accounts"
import * as cache from "../cache" import * as cache from "../cache"
import { getGlobalDB, getIdentity, getTenantId } from "../context" import { doInTenant, getGlobalDB, getIdentity, getTenantId } from "../context"
import * as dbUtils from "../db" import * as dbUtils from "../db"
import { EmailUnavailableError, HTTPError } from "../errors" import { EmailUnavailableError, HTTPError } from "../errors"
import * as platform from "../platform" import * as platform from "../platform"
@ -11,12 +10,10 @@ import * as sessions from "../security/sessions"
import * as usersCore from "./users" import * as usersCore from "./users"
import { import {
Account, Account,
AllDocsResponse,
BulkUserCreated, BulkUserCreated,
BulkUserDeleted, BulkUserDeleted,
isSSOAccount, isSSOAccount,
isSSOUser, isSSOUser,
RowResponse,
SaveUserOpts, SaveUserOpts,
User, User,
UserStatus, UserStatus,
@ -467,7 +464,7 @@ export class UserDB {
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
// root account holder can't be deleted from inside budibase // root account holder can't be deleted from inside budibase
const email = dbUser.email const email = dbUser.email
const account = await accounts.getAccount(email) const account = await accountSdk.getAccount(email)
if (account) { if (account) {
if (dbUser.userId === getIdentity()!._id) { if (dbUser.userId === getIdentity()!._id) {
throw new HTTPError('Please visit "Account" to delete this user', 400) throw new HTTPError('Please visit "Account" to delete this user', 400)
@ -488,6 +485,37 @@ export class UserDB {
await sessions.invalidateSessions(userId, { reason: "deletion" }) await sessions.invalidateSessions(userId, { reason: "deletion" })
} }
static async createAdminUser(
email: string,
password: string,
tenantId: string,
opts?: { ssoId?: string; hashPassword?: boolean; requirePassword?: boolean }
) {
const user: User = {
email: email,
password: password,
createdAt: Date.now(),
roles: {},
builder: {
global: true,
},
admin: {
global: true,
},
tenantId,
}
if (opts?.ssoId) {
user.ssoId = opts.ssoId
}
// always bust checklist beforehand, if an error occurs but can proceed, don't get
// stuck in a cycle
await cache.bustCache(cache.CacheKey.CHECKLIST)
return await UserDB.save(user, {
hashPassword: opts?.hashPassword,
requirePassword: opts?.requirePassword,
})
}
static async getGroups(groupIds: string[]) { static async getGroups(groupIds: string[]) {
return await this.groups.getBulk(groupIds) return await this.groups.getBulk(groupIds)
} }

View File

@ -43,7 +43,7 @@ function removeUserPassword(users: User | User[]) {
return users return users
} }
export const isSupportedUserSearch = (query: SearchQuery) => { export function isSupportedUserSearch(query: SearchQuery) {
const allowed = [ const allowed = [
{ op: SearchQueryOperators.STRING, key: "email" }, { op: SearchQueryOperators.STRING, key: "email" },
{ op: SearchQueryOperators.EQUAL, key: "_id" }, { op: SearchQueryOperators.EQUAL, key: "_id" },
@ -68,10 +68,10 @@ export const isSupportedUserSearch = (query: SearchQuery) => {
return true return true
} }
export const bulkGetGlobalUsersById = async ( export async function bulkGetGlobalUsersById(
userIds: string[], userIds: string[],
opts?: GetOpts opts?: GetOpts
) => { ) {
const db = getGlobalDB() const db = getGlobalDB()
let users = ( let users = (
await db.allDocs({ await db.allDocs({
@ -85,7 +85,7 @@ export const bulkGetGlobalUsersById = async (
return users return users
} }
export const getAllUserIds = async () => { export async function getAllUserIds() {
const db = getGlobalDB() const db = getGlobalDB()
const startKey = `${DocumentType.USER}${SEPARATOR}` const startKey = `${DocumentType.USER}${SEPARATOR}`
const response = await db.allDocs({ const response = await db.allDocs({
@ -95,7 +95,7 @@ export const getAllUserIds = async () => {
return response.rows.map(row => row.id) return response.rows.map(row => row.id)
} }
export const bulkUpdateGlobalUsers = async (users: User[]) => { export async function bulkUpdateGlobalUsers(users: User[]) {
const db = getGlobalDB() const db = getGlobalDB()
return (await db.bulkDocs(users)) as BulkDocsResponse return (await db.bulkDocs(users)) as BulkDocsResponse
} }
@ -113,10 +113,10 @@ export async function getById(id: string, opts?: GetOpts): Promise<User> {
* Given an email address this will use a view to search through * Given an email address this will use a view to search through
* all the users to find one with this email address. * all the users to find one with this email address.
*/ */
export const getGlobalUserByEmail = async ( export async function getGlobalUserByEmail(
email: String, email: String,
opts?: GetOpts opts?: GetOpts
): Promise<User | undefined> => { ): Promise<User | undefined> {
if (email == null) { if (email == null) {
throw "Must supply an email address to view" throw "Must supply an email address to view"
} }
@ -139,11 +139,23 @@ export const getGlobalUserByEmail = async (
return user return user
} }
export const searchGlobalUsersByApp = async ( export async function doesUserExist(email: string) {
try {
const user = await getGlobalUserByEmail(email)
if (Array.isArray(user) || user != null) {
return true
}
} catch (err) {
return false
}
return false
}
export async function searchGlobalUsersByApp(
appId: any, appId: any,
opts: DatabaseQueryOpts, opts: DatabaseQueryOpts,
getOpts?: GetOpts getOpts?: GetOpts
) => { ) {
if (typeof appId !== "string") { if (typeof appId !== "string") {
throw new Error("Must provide a string based app ID") throw new Error("Must provide a string based app ID")
} }
@ -167,10 +179,10 @@ export const searchGlobalUsersByApp = async (
Return any user who potentially has access to the application Return any user who potentially has access to the application
Admins, developers and app users with the explicitly role. Admins, developers and app users with the explicitly role.
*/ */
export const searchGlobalUsersByAppAccess = async ( export async function searchGlobalUsersByAppAccess(
appId: any, appId: any,
opts?: { limit?: number } opts?: { limit?: number }
) => { ) {
const roleSelector = `roles.${appId}` const roleSelector = `roles.${appId}`
let orQuery: any[] = [ let orQuery: any[] = [
@ -205,7 +217,7 @@ export const searchGlobalUsersByAppAccess = async (
return resp.rows return resp.rows
} }
export const getGlobalUserByAppPage = (appId: string, user: User) => { export function getGlobalUserByAppPage(appId: string, user: User) {
if (!user) { if (!user) {
return return
} }
@ -215,11 +227,11 @@ export const getGlobalUserByAppPage = (appId: string, user: User) => {
/** /**
* Performs a starts with search on the global email view. * Performs a starts with search on the global email view.
*/ */
export const searchGlobalUsersByEmail = async ( export async function searchGlobalUsersByEmail(
email: string | unknown, email: string | unknown,
opts: any, opts: any,
getOpts?: GetOpts getOpts?: GetOpts
) => { ) {
if (typeof email !== "string") { if (typeof email !== "string") {
throw new Error("Must provide a string to search by") throw new Error("Must provide a string to search by")
} }
@ -242,12 +254,12 @@ export const searchGlobalUsersByEmail = async (
} }
const PAGE_LIMIT = 8 const PAGE_LIMIT = 8
export const paginatedUsers = async ({ export async function paginatedUsers({
bookmark, bookmark,
query, query,
appId, appId,
limit, limit,
}: SearchUsersRequest = {}) => { }: SearchUsersRequest = {}) {
const db = getGlobalDB() const db = getGlobalDB()
const pageSize = limit ?? PAGE_LIMIT const pageSize = limit ?? PAGE_LIMIT
const pageLimit = pageSize + 1 const pageLimit = pageSize + 1

View File

@ -1,4 +1,5 @@
import env from "../environment" import env from "../environment"
export * from "../docIds/newid" export * from "../docIds/newid"
const bcrypt = env.JS_BCRYPT ? require("bcryptjs") : require("bcrypt") const bcrypt = env.JS_BCRYPT ? require("bcryptjs") : require("bcrypt")

View File

@ -11,6 +11,7 @@ import {
TenantResolutionStrategy, TenantResolutionStrategy,
} from "@budibase/types" } from "@budibase/types"
import type { SetOption } from "cookies" import type { SetOption } from "cookies"
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const APP_PREFIX = DocumentType.APP + SEPARATOR const APP_PREFIX = DocumentType.APP + SEPARATOR

View File

@ -1,3 +1,4 @@
jest.mock("../../../../src/logging/alerts") jest.mock("../../../../src/logging/alerts")
import * as _alerts from "../../../../src/logging/alerts" import * as _alerts from "../../../../src/logging/alerts"
export const alerts = jest.mocked(_alerts) export const alerts = jest.mocked(_alerts)

View File

@ -1,5 +1,6 @@
jest.mock("../../../../src/accounts") jest.mock("../../../../src/accounts")
import * as _accounts from "../../../../src/accounts" import * as _accounts from "../../../../src/accounts"
export const accounts = jest.mocked(_accounts) export const accounts = jest.mocked(_accounts)
export * as date from "./date" export * as date from "./date"

View File

@ -1,2 +1,3 @@
import Chance from "./Chance" import Chance from "./Chance"
export const generator = new Chance() export const generator = new Chance()

View File

@ -9,6 +9,7 @@ mocks.fetch.enable()
// mock all dates to 2020-01-01T00:00:00.000Z // mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests // use tk.reset() to use real dates in individual tests
import tk from "timekeeper" import tk from "timekeeper"
tk.freeze(mocks.date.MOCK_DATE) tk.freeze(mocks.date.MOCK_DATE)
if (!process.env.DEBUG) { if (!process.env.DEBUG) {

View File

@ -1,5 +1,6 @@
<script> <script>
import "@spectrum-css/actiongroup/dist/index-vars.css" import "@spectrum-css/actiongroup/dist/index-vars.css"
export let vertical = false export let vertical = false
export let justified = false export let justified = false
export let quiet = false export let quiet = false

View File

@ -1,5 +1,6 @@
<script> <script>
import "@spectrum-css/avatar/dist/index-vars.css" import "@spectrum-css/avatar/dist/index-vars.css"
let sizes = new Map([ let sizes = new Map([
["XXS", "--spectrum-alias-avatar-size-50"], ["XXS", "--spectrum-alias-avatar-size-50"],
["XS", "--spectrum-alias-avatar-size-75"], ["XS", "--spectrum-alias-avatar-size-75"],

View File

@ -1,5 +1,6 @@
<script> <script>
import "@spectrum-css/buttongroup/dist/index-vars.css" import "@spectrum-css/buttongroup/dist/index-vars.css"
export let vertical = false export let vertical = false
export let gap = "" export let gap = ""

View File

@ -1,5 +1,6 @@
<script> <script>
import "@spectrum-css/divider/dist/index-vars.css" import "@spectrum-css/divider/dist/index-vars.css"
export let size = "M" export let size = "M"
export let vertical = false export let vertical = false

View File

@ -3,8 +3,7 @@
import Button from "../Button/Button.svelte" import Button from "../Button/Button.svelte"
import Body from "../Typography/Body.svelte" import Body from "../Typography/Body.svelte"
import Heading from "../Typography/Heading.svelte" import Heading from "../Typography/Heading.svelte"
import { setContext } from "svelte" import { setContext, createEventDispatcher } from "svelte"
import { createEventDispatcher } from "svelte"
import { generate } from "shortid" import { generate } from "shortid"
export let title export let title

View File

@ -1,5 +1,6 @@
<script> <script>
import Input from "../Form/Input.svelte" import Input from "../Form/Input.svelte"
let value = "" let value = ""
</script> </script>

View File

@ -4,6 +4,7 @@
import Icon from "../Icon/Icon.svelte" import Icon from "../Icon/Icon.svelte"
import Popover from "../Popover/Popover.svelte" import Popover from "../Popover/Popover.svelte"
import { onMount } from "svelte" import { onMount } from "svelte"
const flipDurationMs = 150 const flipDurationMs = 150
export let constraints export let constraints

View File

@ -1,11 +1,10 @@
<script> <script>
import "@spectrum-css/popover/dist/index-vars.css" import "@spectrum-css/popover/dist/index-vars.css"
import Portal from "svelte-portal" import Portal from "svelte-portal"
import { createEventDispatcher } from "svelte" import { createEventDispatcher, getContext } from "svelte"
import positionDropdown from "../Actions/position_dropdown" import positionDropdown from "../Actions/position_dropdown"
import clickOutside from "../Actions/click_outside" import clickOutside from "../Actions/click_outside"
import { fly } from "svelte/transition" import { fly } from "svelte/transition"
import { getContext } from "svelte"
import Context from "../context" import Context from "../context"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -1,7 +1,9 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
const multilevel = getContext("sidenav-type") const multilevel = getContext("sidenav-type")
import Badge from "../Badge/Badge.svelte" import Badge from "../Badge/Badge.svelte"
export let href = "" export let href = ""
export let external = false export let external = false
export let heading = "" export let heading = ""

View File

@ -1,6 +1,7 @@
<script> <script>
import { setContext } from "svelte" import { setContext } from "svelte"
import "@spectrum-css/sidenav/dist/index-vars.css" import "@spectrum-css/sidenav/dist/index-vars.css"
export let multilevel = false export let multilevel = false
setContext("sidenav-type", multilevel) setContext("sidenav-type", multilevel)
</script> </script>

View File

@ -1,6 +1,7 @@
<script> <script>
import "@spectrum-css/label/dist/index-vars.css" import "@spectrum-css/label/dist/index-vars.css"
import Badge from "../Badge/Badge.svelte" import Badge from "../Badge/Badge.svelte"
export let value export let value
const displayLimit = 5 const displayLimit = 5

View File

@ -1,6 +1,7 @@
<script> <script>
import { getContext, onMount, createEventDispatcher } from "svelte" import { getContext, onMount, createEventDispatcher } from "svelte"
import Portal from "svelte-portal" import Portal from "svelte-portal"
export let title export let title
export let icon = "" export let icon = ""
export let id export let id

View File

@ -1,4 +1,5 @@
import { helpers } from "@budibase/shared-core" import { helpers } from "@budibase/shared-core"
export const deepGet = helpers.deepGet export const deepGet = helpers.deepGet
/** /**

View File

@ -228,7 +228,12 @@ export const getContextProviderComponents = (
/** /**
* Gets all data provider components above a component. * Gets all data provider components above a component.
*/ */
export const getActionProviderComponents = (asset, componentId, actionType) => { export const getActionProviders = (
asset,
componentId,
actionType,
options = { includeSelf: false }
) => {
if (!asset || !componentId) { if (!asset || !componentId) {
return [] return []
} }
@ -236,13 +241,30 @@ export const getActionProviderComponents = (asset, componentId, actionType) => {
// Get the component tree leading up to this component, ignoring the component // Get the component tree leading up to this component, ignoring the component
// itself // itself
const path = findComponentPath(asset.props, componentId) const path = findComponentPath(asset.props, componentId)
path.pop() if (!options?.includeSelf) {
path.pop()
}
// Filter by only data provider components // Find matching contexts and generate bindings
return path.filter(component => { let providers = []
path.forEach(component => {
const def = store.actions.components.getDefinition(component._component) const def = store.actions.components.getDefinition(component._component)
return def?.actions?.includes(actionType) const actions = (def?.actions || []).map(action => {
return typeof action === "string" ? { type: action } : action
})
const action = actions.find(x => x.type === actionType)
if (action) {
let runtimeBinding = component._id
if (action.suffix) {
runtimeBinding += `-${action.suffix}`
}
providers.push({
readableBinding: component._instanceName,
runtimeBinding,
})
}
}) })
return providers
} }
/** /**

View File

@ -4,11 +4,10 @@ import { getTemporalStore } from "./store/temporal"
import { getThemeStore } from "./store/theme" import { getThemeStore } from "./store/theme"
import { getUserStore } from "./store/users" import { getUserStore } from "./store/users"
import { getDeploymentStore } from "./store/deployments" import { getDeploymentStore } from "./store/deployments"
import { derived, writable } from "svelte/store" import { derived, writable, get } from "svelte/store"
import { findComponent, findComponentPath } from "./componentUtils" import { findComponent, findComponentPath } from "./componentUtils"
import { RoleUtils } from "@budibase/frontend-core" import { RoleUtils } from "@budibase/frontend-core"
import { createHistoryStore } from "builderStore/store/history" import { createHistoryStore } from "builderStore/store/history"
import { get } from "svelte/store"
export const store = getFrontendStore() export const store = getFrontendStore()
export const automationStore = getAutomationStore() export const automationStore = getAutomationStore()

View File

@ -7,11 +7,9 @@ import {
} from "builderStore" } from "builderStore"
import { datasources, tables } from "stores/backend" import { datasources, tables } from "stores/backend"
import { get } from "svelte/store" import { get } from "svelte/store"
import { auth } from "stores/portal" import { auth, apps } from "stores/portal"
import { SocketEvent, BuilderSocketEvent } from "@budibase/shared-core" import { SocketEvent, BuilderSocketEvent, helpers } from "@budibase/shared-core"
import { apps } from "stores/portal"
import { notifications } from "@budibase/bbui" import { notifications } from "@budibase/bbui"
import { helpers } from "@budibase/shared-core"
export const createBuilderWebsocket = appId => { export const createBuilderWebsocket = appId => {
const socket = createWebsocket("/socket/builder") const socket = createWebsocket("/socket/builder")

View File

@ -1,5 +1,9 @@
<script> <script>
import { automationStore, selectedAutomation } from "builderStore" import {
automationStore,
selectedAutomation,
automationHistoryStore,
} from "builderStore"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import FlowItem from "./FlowItem.svelte" import FlowItem from "./FlowItem.svelte"
import TestDataModal from "./TestDataModal.svelte" import TestDataModal from "./TestDataModal.svelte"
@ -8,7 +12,6 @@
import { Icon, notifications, Modal } from "@budibase/bbui" import { Icon, notifications, Modal } from "@budibase/bbui"
import { ActionStepID } from "constants/backend/automations" import { ActionStepID } from "constants/backend/automations"
import UndoRedoControl from "components/common/UndoRedoControl.svelte" import UndoRedoControl from "components/common/UndoRedoControl.svelte"
import { automationHistoryStore } from "builderStore"
export let automation export let automation

View File

@ -1,7 +1,7 @@
<script> <script>
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import { notifications } from "@budibase/bbui"
import { import {
notifications,
Input, Input,
InlineAlert, InlineAlert,
ModalContent, ModalContent,

View File

@ -1,7 +1,12 @@
<script> <script>
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import { notifications } from "@budibase/bbui" import {
import { Icon, Input, ModalContent, Modal } from "@budibase/bbui" notifications,
Icon,
Input,
ModalContent,
Modal,
} from "@budibase/bbui"
export let automation export let automation
export let onCancel = undefined export let onCancel = undefined

View File

@ -38,12 +38,11 @@
EditorModes, EditorModes,
} from "components/common/CodeEditor" } from "components/common/CodeEditor"
import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte" import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte"
import { LuceneUtils } from "@budibase/frontend-core" import { LuceneUtils, Utils } from "@budibase/frontend-core"
import { import {
getSchemaForDatasourcePlus, getSchemaForDatasourcePlus,
getEnvironmentBindings, getEnvironmentBindings,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { Utils } from "@budibase/frontend-core"
import { TriggerStepID, ActionStepID } from "constants/backend/automations" import { TriggerStepID, ActionStepID } from "constants/backend/automations"
import { onMount } from "svelte" import { onMount } from "svelte"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"

View File

@ -2,6 +2,7 @@
import { Button, Select, Input, Label } from "@budibase/bbui" import { Button, Select, Input, Label } from "@budibase/bbui"
import { onMount, createEventDispatcher } from "svelte" import { onMount, createEventDispatcher } from "svelte"
import { flags } from "stores/backend" import { flags } from "stores/backend"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
export let value export let value

View File

@ -1,8 +1,7 @@
<script> <script>
import { Icon, notifications } from "@budibase/bbui" import { Icon, notifications, ModalContent } from "@budibase/bbui"
import { automationStore, selectedAutomation } from "builderStore" import { automationStore, selectedAutomation } from "builderStore"
import WebhookDisplay from "./WebhookDisplay.svelte" import WebhookDisplay from "./WebhookDisplay.svelte"
import { ModalContent } from "@budibase/bbui"
import { onMount, onDestroy } from "svelte" import { onMount, onDestroy } from "svelte"
const POLL_RATE_MS = 2500 const POLL_RATE_MS = 2500

View File

@ -1,11 +1,15 @@
<script> <script>
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { tables } from "stores/backend" import { tables, roles } from "stores/backend"
import { roles } from "stores/backend" import {
import { notifications } from "@budibase/bbui" notifications,
keepOpen,
ModalContent,
Select,
Link,
} from "@budibase/bbui"
import RowFieldControl from "../RowFieldControl.svelte" import RowFieldControl from "../RowFieldControl.svelte"
import { API } from "api" import { API } from "api"
import { keepOpen, ModalContent, Select, Link } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte" import ErrorsBox from "components/common/ErrorsBox.svelte"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"

View File

@ -1,8 +1,14 @@
<script> <script>
import { keepOpen, ModalContent, Select, Input, Button } from "@budibase/bbui" import {
keepOpen,
ModalContent,
Select,
Input,
Button,
notifications,
} from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import { API } from "api" import { API } from "api"
import { notifications } from "@budibase/bbui"
import ErrorsBox from "components/common/ErrorsBox.svelte" import ErrorsBox from "components/common/ErrorsBox.svelte"
import { roles } from "stores/backend" import { roles } from "stores/backend"

View File

@ -1,8 +1,7 @@
<script> <script>
import { get } from "svelte/store" import { get } from "svelte/store"
import { datasources, integrations } from "stores/backend" import { datasources, integrations } from "stores/backend"
import { notifications } from "@budibase/bbui" import { notifications, Input, ModalContent, Modal } from "@budibase/bbui"
import { Input, ModalContent, Modal } from "@budibase/bbui"
import { integrationForDatasource } from "stores/selectors" import { integrationForDatasource } from "stores/selectors"
let error = "" let error = ""

View File

@ -1,8 +1,7 @@
<script> <script>
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { datasources } from "stores/backend" import { datasources } from "stores/backend"
import { notifications } from "@budibase/bbui" import { notifications, ActionMenu, MenuItem, Icon } from "@budibase/bbui"
import { ActionMenu, MenuItem, Icon } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import UpdateDatasourceModal from "components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte" import UpdateDatasourceModal from "components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte"
import { BUDIBASE_DATASOURCE_TYPE } from "constants/backend" import { BUDIBASE_DATASOURCE_TYPE } from "constants/backend"

View File

@ -1,5 +1,8 @@
<script> <script>
import { RelationshipType } from "constants/backend" import {
RelationshipType,
PrettyRelationshipDefinitions,
} from "constants/backend"
import { import {
keepOpen, keepOpen,
Button, Button,
@ -8,13 +11,12 @@
Select, Select,
Detail, Detail,
Body, Body,
Helpers,
} from "@budibase/bbui" } from "@budibase/bbui"
import { tables } from "stores/backend" import { tables } from "stores/backend"
import { Helpers } from "@budibase/bbui"
import { RelationshipErrorChecker } from "./relationshipErrors" import { RelationshipErrorChecker } from "./relationshipErrors"
import { onMount } from "svelte" import { onMount } from "svelte"
import RelationshipSelector from "components/common/RelationshipSelector.svelte" import RelationshipSelector from "components/common/RelationshipSelector.svelte"
import { PrettyRelationshipDefinitions } from "constants/backend"
export let save export let save
export let datasource export let datasource

View File

@ -1,9 +1,13 @@
<script> <script>
import { goto, url } from "@roxi/routify" import { goto, url } from "@roxi/routify"
import { tables } from "stores/backend" import { tables, datasources } from "stores/backend"
import { notifications } from "@budibase/bbui" import {
import { Input, Label, ModalContent, Layout } from "@budibase/bbui" notifications,
import { datasources } from "stores/backend" Input,
Label,
ModalContent,
Layout,
} from "@budibase/bbui"
import TableDataImport from "../TableDataImport.svelte" import TableDataImport from "../TableDataImport.svelte"
import { import {
BUDIBASE_INTERNAL_DB_ID, BUDIBASE_INTERNAL_DB_ID,

View File

@ -1,5 +1,6 @@
<script> <script>
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
export let size export let size
export let svgHtml export let svgHtml

View File

@ -9,18 +9,18 @@
Heading, Heading,
Icon, Icon,
} from "@budibase/bbui" } from "@budibase/bbui"
import { createEventDispatcher, onMount } from "svelte" import { createEventDispatcher, onMount, getContext } from "svelte"
import { import {
isValid, isValid,
decodeJSBinding, decodeJSBinding,
encodeJSBinding, encodeJSBinding,
convertToJS,
} from "@budibase/string-templates" } from "@budibase/string-templates"
import { import {
readableToRuntimeBinding, readableToRuntimeBinding,
runtimeToReadableBinding, runtimeToReadableBinding,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { convertToJS } from "@budibase/string-templates"
import { admin } from "stores/portal" import { admin } from "stores/portal"
import CodeEditor from "../CodeEditor/CodeEditor.svelte" import CodeEditor from "../CodeEditor/CodeEditor.svelte"
import { import {
@ -32,7 +32,6 @@
hbInsert, hbInsert,
jsInsert, jsInsert,
} from "../CodeEditor" } from "../CodeEditor"
import { getContext } from "svelte"
import BindingPicker from "./BindingPicker.svelte" import BindingPicker from "./BindingPicker.svelte"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -1,5 +1,6 @@
<script> <script>
import { capitalise } from "helpers" import { capitalise } from "helpers"
export let value export let value
</script> </script>

View File

@ -1,5 +1,6 @@
<script> <script>
import dayjs from "dayjs" import dayjs from "dayjs"
export let value export let value
</script> </script>

View File

@ -1,7 +1,6 @@
<script> <script>
import { ActionButton, Button, Drawer } from "@budibase/bbui" import { ActionButton, Button, Drawer, notifications } from "@budibase/bbui"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { notifications } from "@budibase/bbui"
import ButtonActionDrawer from "./ButtonActionDrawer.svelte" import ButtonActionDrawer from "./ButtonActionDrawer.svelte"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"

View File

@ -1,17 +1,19 @@
<script> <script>
import { Select, Label } from "@budibase/bbui" import { Select, Label } from "@budibase/bbui"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { getActionProviderComponents } from "builderStore/dataBinding" import { getActionProviders } from "builderStore/dataBinding"
import { onMount } from "svelte" import { onMount } from "svelte"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let parameters export let parameters
export let bindings = [] export let bindings = []
export let nested
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviders(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"ChangeFormStep" "ChangeFormStep",
{ includeSelf: nested }
) )
const typeOptions = [ const typeOptions = [
@ -46,8 +48,8 @@
placeholder={null} placeholder={null}
bind:value={parameters.componentId} bind:value={parameters.componentId}
options={actionProviders} options={actionProviders}
getOptionLabel={x => x._instanceName} getOptionLabel={x => x.readableBinding}
getOptionValue={x => x._id} getOptionValue={x => x.runtimeBinding}
/> />
<Label small>Step</Label> <Label small>Step</Label>
<Select bind:value={parameters.type} options={typeOptions} /> <Select bind:value={parameters.type} options={typeOptions} />

View File

@ -1,14 +1,16 @@
<script> <script>
import { Select, Label } from "@budibase/bbui" import { Select, Label } from "@budibase/bbui"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { getActionProviderComponents } from "builderStore/dataBinding" import { getActionProviders } from "builderStore/dataBinding"
export let parameters export let parameters
export let nested
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviders(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"ClearForm" "ClearForm",
{ includeSelf: nested }
) )
</script> </script>
@ -17,8 +19,8 @@
<Select <Select
bind:value={parameters.componentId} bind:value={parameters.componentId}
options={actionProviders} options={actionProviders}
getOptionLabel={x => x._instanceName} getOptionLabel={x => x.readableBinding}
getOptionValue={x => x._id} getOptionValue={x => x.runtimeBinding}
/> />
</div> </div>

View File

@ -2,6 +2,7 @@
import { Select, Body } from "@budibase/bbui" import { Select, Body } from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
export let parameters export let parameters
export let bindings export let bindings

View File

@ -2,27 +2,20 @@
import { Select, Label, Body, Checkbox, Input } from "@budibase/bbui" import { Select, Label, Body, Checkbox, Input } from "@budibase/bbui"
import { store, currentAsset } from "builderStore" import { store, currentAsset } from "builderStore"
import { tables, viewsV2 } from "stores/backend" import { tables, viewsV2 } from "stores/backend"
import { import { getSchemaForDatasourcePlus } from "builderStore/dataBinding"
getContextProviderComponents,
getSchemaForDatasourcePlus,
} from "builderStore/dataBinding"
import SaveFields from "./SaveFields.svelte" import SaveFields from "./SaveFields.svelte"
import { getDatasourceLikeProviders } from "components/design/settings/controls/ButtonActionEditor/actions/utils"
export let parameters export let parameters
export let bindings = [] export let bindings = []
export let nested
$: formComponents = getContextProviderComponents( $: providerOptions = getDatasourceLikeProviders({
$currentAsset, asset: $currentAsset,
$store.selectedComponentId, componentId: $store.selectedComponentId,
"form" nested,
) })
$: schemaComponents = getContextProviderComponents( $: schemaFields = getSchemaFields(parameters?.tableId)
$currentAsset,
$store.selectedComponentId,
"schema"
)
$: providerOptions = getProviderOptions(formComponents, schemaComponents)
$: schemaFields = getSchemaFields($currentAsset, parameters?.tableId)
$: tableOptions = $tables.list.map(table => ({ $: tableOptions = $tables.list.map(table => ({
label: table.name, label: table.name,
resourceId: table._id, resourceId: table._id,
@ -33,44 +26,8 @@
})) }))
$: options = [...(tableOptions || []), ...(viewOptions || [])] $: options = [...(tableOptions || []), ...(viewOptions || [])]
// Gets a context definition of a certain type from a component definition const getSchemaFields = resourceId => {
const extractComponentContext = (component, contextType) => { const { schema } = getSchemaForDatasourcePlus(resourceId)
const def = store.actions.components.getDefinition(component?._component)
if (!def) {
return null
}
const contexts = Array.isArray(def.context) ? def.context : [def.context]
return contexts.find(context => context?.type === contextType)
}
// Gets options for valid context keys which provide valid data to submit
const getProviderOptions = (formComponents, schemaComponents) => {
const formContexts = formComponents.map(component => ({
component,
context: extractComponentContext(component, "form"),
}))
const schemaContexts = schemaComponents.map(component => ({
component,
context: extractComponentContext(component, "schema"),
}))
const allContexts = formContexts.concat(schemaContexts)
return allContexts.map(({ component, context }) => {
let runtimeBinding = component._id
if (context.suffix) {
runtimeBinding += `-${context.suffix}`
}
return {
label: component._instanceName,
value: runtimeBinding,
}
})
}
const getSchemaFields = (asset, tableId) => {
const { schema } = getSchemaForDatasourcePlus(tableId)
delete schema._id
delete schema._rev
return Object.values(schema || {}) return Object.values(schema || {})
} }

View File

@ -1,14 +1,16 @@
<script> <script>
import { Select, Label } from "@budibase/bbui" import { Select, Label } from "@budibase/bbui"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { getActionProviderComponents } from "builderStore/dataBinding" import { getActionProviders } from "builderStore/dataBinding"
export let parameters export let parameters
export let nested
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviders(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"RefreshDatasource" "RefreshDatasource",
{ includeSelf: nested }
) )
</script> </script>
@ -17,8 +19,8 @@
<Select <Select
bind:value={parameters.componentId} bind:value={parameters.componentId}
options={actionProviders} options={actionProviders}
getOptionLabel={x => x._instanceName} getOptionLabel={x => x.readableBinding}
getOptionValue={x => x._id} getOptionValue={x => x.runtimeBinding}
/> />
</div> </div>

View File

@ -2,29 +2,19 @@
import { Select, Label, Body, Checkbox, Input } from "@budibase/bbui" import { Select, Label, Body, Checkbox, Input } from "@budibase/bbui"
import { store, currentAsset } from "builderStore" import { store, currentAsset } from "builderStore"
import { tables, viewsV2 } from "stores/backend" import { tables, viewsV2 } from "stores/backend"
import { import { getSchemaForDatasourcePlus } from "builderStore/dataBinding"
getContextProviderComponents,
getSchemaForDatasourcePlus,
} from "builderStore/dataBinding"
import SaveFields from "./SaveFields.svelte" import SaveFields from "./SaveFields.svelte"
import { getDatasourceLikeProviders } from "components/design/settings/controls/ButtonActionEditor/actions/utils"
export let parameters export let parameters
export let bindings = [] export let bindings = []
export let nested export let nested
$: formComponents = getContextProviderComponents( $: providerOptions = getDatasourceLikeProviders({
$currentAsset, asset: $currentAsset,
$store.selectedComponentId, componentId: $store.selectedComponentId,
"form", nested,
{ includeSelf: nested } })
)
$: schemaComponents = getContextProviderComponents(
$currentAsset,
$store.selectedComponentId,
"schema",
{ includeSelf: nested }
)
$: providerOptions = getProviderOptions(formComponents, schemaComponents)
$: schemaFields = getSchemaFields(parameters?.tableId) $: schemaFields = getSchemaFields(parameters?.tableId)
$: tableOptions = $tables.list.map(table => ({ $: tableOptions = $tables.list.map(table => ({
label: table.name, label: table.name,
@ -36,40 +26,6 @@
})) }))
$: options = [...(tableOptions || []), ...(viewOptions || [])] $: options = [...(tableOptions || []), ...(viewOptions || [])]
// Gets a context definition of a certain type from a component definition
const extractComponentContext = (component, contextType) => {
const def = store.actions.components.getDefinition(component?._component)
if (!def) {
return null
}
const contexts = Array.isArray(def.context) ? def.context : [def.context]
return contexts.find(context => context?.type === contextType)
}
// Gets options for valid context keys which provide valid data to submit
const getProviderOptions = (formComponents, schemaComponents) => {
const formContexts = formComponents.map(component => ({
component,
context: extractComponentContext(component, "form"),
}))
const schemaContexts = schemaComponents.map(component => ({
component,
context: extractComponentContext(component, "schema"),
}))
const allContexts = formContexts.concat(schemaContexts)
return allContexts.map(({ component, context }) => {
let runtimeBinding = component._id
if (context.suffix) {
runtimeBinding += `-${context.suffix}`
}
return {
label: component._instanceName,
value: runtimeBinding,
}
})
}
const getSchemaFields = resourceId => { const getSchemaFields = resourceId => {
const { schema } = getSchemaForDatasourcePlus(resourceId) const { schema } = getSchemaForDatasourcePlus(resourceId)
return Object.values(schema || {}) return Object.values(schema || {})

View File

@ -1,22 +1,36 @@
<script> <script>
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { Label, Combobox, Select } from "@budibase/bbui" import { Label, Combobox, Select } from "@budibase/bbui"
import { import { getActionProviders, buildFormSchema } from "builderStore/dataBinding"
getActionProviderComponents,
buildFormSchema,
} from "builderStore/dataBinding"
import { findComponent } from "builderStore/componentUtils" import { findComponent } from "builderStore/componentUtils"
export let parameters export let parameters
export let nested
$: formComponent = findComponent($currentAsset.props, parameters.componentId) $: formComponent = getFormComponent(
$currentAsset.props,
parameters.componentId
)
$: formSchema = buildFormSchema(formComponent) $: formSchema = buildFormSchema(formComponent)
$: fieldOptions = Object.keys(formSchema || {}) $: fieldOptions = Object.keys(formSchema || {})
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviders(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"ScrollTo" "ScrollTo",
{ includeSelf: nested }
) )
const getFormComponent = (asset, id) => {
let component = findComponent(asset, id)
if (component) {
return component
}
// Check for block component IDs, and use the block itself instead
if (id?.includes("-")) {
return findComponent(asset, id.split("-")[0])
}
return null
}
</script> </script>
<div class="root"> <div class="root">
@ -24,8 +38,8 @@
<Select <Select
bind:value={parameters.componentId} bind:value={parameters.componentId}
options={actionProviders} options={actionProviders}
getOptionLabel={x => x._instanceName} getOptionLabel={x => x.readableBinding}
getOptionValue={x => x._id} getOptionValue={x => x.runtimeBinding}
/> />
<Label small>Field</Label> <Label small>Field</Label>
<Combobox bind:value={parameters.field} options={fieldOptions} /> <Combobox bind:value={parameters.field} options={fieldOptions} />

View File

@ -3,14 +3,12 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { import { getActionProviders, buildFormSchema } from "builderStore/dataBinding"
getActionProviderComponents,
buildFormSchema,
} from "builderStore/dataBinding"
import { findComponent } from "builderStore/componentUtils" import { findComponent } from "builderStore/componentUtils"
export let parameters export let parameters
export let bindings = [] export let bindings = []
export let nested
const typeOptions = [ const typeOptions = [
{ {
@ -23,15 +21,31 @@
}, },
] ]
$: formComponent = findComponent($currentAsset.props, parameters.componentId) $: formComponent = getFormComponent(
$currentAsset.props,
parameters.componentId
)
$: formSchema = buildFormSchema(formComponent) $: formSchema = buildFormSchema(formComponent)
$: fieldOptions = Object.keys(formSchema || {}) $: fieldOptions = Object.keys(formSchema || {})
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviders(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"ValidateForm" "ValidateForm",
{ includeSelf: nested }
) )
const getFormComponent = (asset, id) => {
let component = findComponent(asset, id)
if (component) {
return component
}
// Check for block component IDs, and use the block itself instead
if (id?.includes("-")) {
return findComponent(asset, id.split("-")[0])
}
return null
}
onMount(() => { onMount(() => {
if (!parameters.type) { if (!parameters.type) {
parameters.type = "set" parameters.type = "set"
@ -44,8 +58,8 @@
<Select <Select
bind:value={parameters.componentId} bind:value={parameters.componentId}
options={actionProviders} options={actionProviders}
getOptionLabel={x => x._instanceName} getOptionLabel={x => x.readableBinding}
getOptionValue={x => x._id} getOptionValue={x => x.runtimeBinding}
/> />
<Label small>Type</Label> <Label small>Type</Label>
<Select <Select

View File

@ -1,14 +1,16 @@
<script> <script>
import { Select, Label } from "@budibase/bbui" import { Select, Label } from "@budibase/bbui"
import { currentAsset, store } from "builderStore" import { currentAsset, store } from "builderStore"
import { getActionProviderComponents } from "builderStore/dataBinding" import { getActionProviders } from "builderStore/dataBinding"
export let parameters export let parameters
export let nested
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviders(
$currentAsset, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"ValidateForm" "ValidateForm",
{ includeSelf: nested }
) )
</script> </script>
@ -17,8 +19,8 @@
<Select <Select
bind:value={parameters.componentId} bind:value={parameters.componentId}
options={actionProviders} options={actionProviders}
getOptionLabel={x => x._instanceName} getOptionLabel={x => x.readableBinding}
getOptionValue={x => x._id} getOptionValue={x => x.runtimeBinding}
/> />
<div /> <div />
</div> </div>

View File

@ -0,0 +1,82 @@
import { getContextProviderComponents } from "builderStore/dataBinding"
import { store } from "builderStore"
import { capitalise } from "helpers"
// Generates bindings for all components that provider "datasource like"
// contexts. This includes "form" contexts and "schema" contexts. This is used
// by various button actions as candidates for whole "row" objects.
// Some examples are saving rows or duplicating rows.
export const getDatasourceLikeProviders = ({ asset, componentId, nested }) => {
// Get all form context providers
const formComponents = getContextProviderComponents(
asset,
componentId,
"form",
{ includeSelf: nested }
)
// Get all schema context providers
const schemaComponents = getContextProviderComponents(
asset,
componentId,
"schema",
{ includeSelf: nested }
)
// Generate contexts for all form providers
const formContexts = formComponents.map(component => ({
component,
context: extractComponentContext(component, "form"),
}))
// Generate contexts for all schema providers
const schemaContexts = schemaComponents.map(component => ({
component,
context: extractComponentContext(component, "schema"),
}))
// Check for duplicate contexts by the same component. In this case, attempt
// to label contexts with their suffixes
schemaContexts.forEach(schemaContext => {
// Check if we have a form context for this component
const id = schemaContext.component._id
const existing = formContexts.find(x => x.component._id === id)
if (existing) {
if (existing.context.suffix) {
const suffix = capitalise(existing.context.suffix)
existing.readableSuffix = ` - ${suffix}`
}
if (schemaContext.context.suffix) {
const suffix = capitalise(schemaContext.context.suffix)
schemaContext.readableSuffix = ` - ${suffix}`
}
}
})
// Generate bindings for all contexts
const allContexts = formContexts.concat(schemaContexts)
return allContexts.map(({ component, context, readableSuffix }) => {
let readableBinding = component._instanceName
let runtimeBinding = component._id
if (context.suffix) {
runtimeBinding += `-${context.suffix}`
}
if (readableSuffix) {
readableBinding += readableSuffix
}
return {
label: readableBinding,
value: runtimeBinding,
}
})
}
// Gets a context definition of a certain type from a component definition
const extractComponentContext = (component, contextType) => {
const def = store.actions.components.getDefinition(component?._component)
if (!def) {
return null
}
const contexts = Array.isArray(def.context) ? def.context : [def.context]
return contexts.find(context => context?.type === contextType)
}

View File

@ -24,8 +24,9 @@
queries as queriesStore, queries as queriesStore,
viewsV2 as viewsV2Store, viewsV2 as viewsV2Store,
views as viewsStore, views as viewsStore,
datasources,
integrations,
} from "stores/backend" } from "stores/backend"
import { datasources, integrations } from "stores/backend"
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte" import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
import IntegrationQueryEditor from "components/integration/index.svelte" import IntegrationQueryEditor from "components/integration/index.svelte"
import { makePropSafe as safe } from "@budibase/string-templates" import { makePropSafe as safe } from "@budibase/string-templates"

View File

@ -1,8 +1,7 @@
<script> <script>
import { dndzone } from "svelte-dnd-action" import { dndzone } from "svelte-dnd-action"
import { createEventDispatcher } from "svelte" import { createEventDispatcher, setContext } from "svelte"
import { generate } from "shortid" import { generate } from "shortid"
import { setContext } from "svelte"
import { writable, get } from "svelte/store" import { writable, get } from "svelte/store"
import DragHandle from "./drag-handle.svelte" import DragHandle from "./drag-handle.svelte"

View File

@ -2,9 +2,8 @@
import { Icon, Popover, Layout } from "@budibase/bbui" import { Icon, Popover, Layout } from "@budibase/bbui"
import { store } from "builderStore" import { store } from "builderStore"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { createEventDispatcher } from "svelte" import { createEventDispatcher, getContext } from "svelte"
import ComponentSettingsSection from "../../../../pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte" import ComponentSettingsSection from "../../../../pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte"
import { getContext } from "svelte"
export let anchor export let anchor
export let componentInstance export let componentInstance

View File

@ -7,10 +7,9 @@
getBindableProperties, getBindableProperties,
getComponentBindableProperties, getComponentBindableProperties,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { currentAsset } from "builderStore" import { currentAsset, store, selectedScreen } from "builderStore"
import DraggableList from "../DraggableList/DraggableList.svelte" import DraggableList from "../DraggableList/DraggableList.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { store, selectedScreen } from "builderStore"
import FieldSetting from "./FieldSetting.svelte" import FieldSetting from "./FieldSetting.svelte"
import { convertOldFieldFormat, getComponentForField } from "./utils" import { convertOldFieldFormat, getComponentForField } from "./utils"

View File

@ -1,2 +1,3 @@
import FlatButtonGroup from "./FlatButtonGroup.svelte" import FlatButtonGroup from "./FlatButtonGroup.svelte"
export default FlatButtonGroup export default FlatButtonGroup

View File

@ -1,6 +1,7 @@
<script> <script>
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
import { getUserBindings } from "builderStore/dataBinding" import { getUserBindings } from "builderStore/dataBinding"
export let bindable = true export let bindable = true
export let queryBindings = [] export let queryBindings = []
export let hideHeading = false export let hideHeading = false

View File

@ -11,6 +11,7 @@
import { environment } from "stores/portal" import { environment } from "stores/portal"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { getContext } from "svelte" import { getContext } from "svelte"
const modalContext = getContext(Context.Modal) const modalContext = getContext(Context.Modal)
export let save export let save

View File

@ -1,6 +1,5 @@
<script> <script>
import { ModalContent } from "@budibase/bbui" import { ModalContent, Select } from "@budibase/bbui"
import { Select } from "@budibase/bbui"
import { themeStore } from "builderStore" import { themeStore } from "builderStore"
import { Constants } from "@budibase/frontend-core" import { Constants } from "@budibase/frontend-core"
</script> </script>

View File

@ -2,6 +2,7 @@
import { Body, ProgressBar, Heading, Icon, Link } from "@budibase/bbui" import { Body, ProgressBar, Heading, Icon, Link } from "@budibase/bbui"
import { admin, auth } from "../../stores/portal" import { admin, auth } from "../../stores/portal"
import { onMount } from "svelte" import { onMount } from "svelte"
export let usage export let usage
export let warnWhenFull = false export let warnWhenFull = false

View File

@ -1,10 +1,17 @@
<script> <script>
import { Button, Heading, notifications, Layout, Body } from "@budibase/bbui" import {
Button,
Heading,
notifications,
Layout,
Body,
FancyForm,
FancyInput,
} from "@budibase/bbui"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { API } from "api" import { API } from "api"
import { admin, auth } from "stores/portal" import { admin, auth } from "stores/portal"
import Logo from "assets/bb-emblem.svg" import Logo from "assets/bb-emblem.svg"
import { FancyForm, FancyInput } from "@budibase/bbui"
import { TestimonialPage } from "@budibase/frontend-core/src/components" import { TestimonialPage } from "@budibase/frontend-core/src/components"
import { passwordsMatch, handleError } from "../auth/_components/utils" import { passwordsMatch, handleError } from "../auth/_components/utils"

View File

@ -1,6 +1,6 @@
<script> <script>
import { Heading, Body, Layout, Button, Modal } from "@budibase/bbui" import { Heading, Body, Layout, Button, Modal } from "@budibase/bbui"
import { automationStore, selectedAutomation } from "builderStore" import { automationStore, selectedAutomation, store } from "builderStore"
import AutomationPanel from "components/automation/AutomationPanel/AutomationPanel.svelte" import AutomationPanel from "components/automation/AutomationPanel/AutomationPanel.svelte"
import CreateAutomationModal from "components/automation/AutomationPanel/CreateAutomationModal.svelte" import CreateAutomationModal from "components/automation/AutomationPanel/CreateAutomationModal.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
@ -8,7 +8,6 @@
import { onDestroy, onMount } from "svelte" import { onDestroy, onMount } from "svelte"
import { syncURLToState } from "helpers/urlStateSync" import { syncURLToState } from "helpers/urlStateSync"
import * as routify from "@roxi/routify" import * as routify from "@roxi/routify"
import { store } from "builderStore"
$: automationId = $selectedAutomation?._id $: automationId = $selectedAutomation?._id
$: store.actions.websocket.selectResource(automationId) $: store.actions.websocket.selectResource(automationId)

View File

@ -1,5 +1,5 @@
<script> <script>
import { Modal, keepOpen } from "@budibase/bbui" import { Modal, keepOpen, notifications } from "@budibase/bbui"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { IntegrationTypes } from "constants/backend" import { IntegrationTypes } from "constants/backend"
import GoogleAuthPrompt from "./GoogleAuthPrompt.svelte" import GoogleAuthPrompt from "./GoogleAuthPrompt.svelte"
@ -11,7 +11,6 @@
import { createOnGoogleAuthStore } from "./stores/onGoogleAuth.js" import { createOnGoogleAuthStore } from "./stores/onGoogleAuth.js"
import { createDatasourceCreationStore } from "./stores/datasourceCreation.js" import { createDatasourceCreationStore } from "./stores/datasourceCreation.js"
import { configFromIntegration } from "stores/selectors" import { configFromIntegration } from "stores/selectors"
import { notifications } from "@budibase/bbui"
export let loading = false export let loading = false
const store = createDatasourceCreationStore() const store = createDatasourceCreationStore()

View File

@ -2,6 +2,7 @@
import { Body } from "@budibase/bbui" import { Body } from "@budibase/bbui"
import FontAwesomeIcon from "components/common/FontAwesomeIcon.svelte" import FontAwesomeIcon from "components/common/FontAwesomeIcon.svelte"
import { IntegrationTypes } from "constants/backend" import { IntegrationTypes } from "constants/backend"
export let datasource export let datasource
const getSubtitle = datasource => { const getSubtitle = datasource => {
if (datasource.source === IntegrationTypes.REST) { if (datasource.source === IntegrationTypes.REST) {

View File

@ -1,8 +1,7 @@
<script> <script>
import { Table, Modal, Layout, ActionButton } from "@budibase/bbui" import { Table, Modal, Layout, ActionButton, Helpers } from "@budibase/bbui"
import AuthTypeRenderer from "./AuthTypeRenderer.svelte" import AuthTypeRenderer from "./AuthTypeRenderer.svelte"
import RestAuthenticationModal from "./RestAuthenticationModal.svelte" import RestAuthenticationModal from "./RestAuthenticationModal.svelte"
import { Helpers } from "@budibase/bbui"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
export let authConfigs = [] export let authConfigs = []

View File

@ -1,5 +1,6 @@
<script> <script>
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
$redirect("../../") $redirect("../../")
</script> </script>

View File

@ -1,5 +1,6 @@
<script> <script>
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
$redirect("../") $redirect("../")
</script> </script>

View File

@ -1,6 +1,7 @@
<script> <script>
import { DetailSummary } from "@budibase/bbui" import { DetailSummary } from "@budibase/bbui"
import InfoDisplay from "./InfoDisplay.svelte" import InfoDisplay from "./InfoDisplay.svelte"
export let componentDefinition export let componentDefinition
</script> </script>

View File

@ -6,13 +6,12 @@
import DesignSection from "./DesignSection.svelte" import DesignSection from "./DesignSection.svelte"
import CustomStylesSection from "./CustomStylesSection.svelte" import CustomStylesSection from "./CustomStylesSection.svelte"
import ConditionalUISection from "./ConditionalUISection.svelte" import ConditionalUISection from "./ConditionalUISection.svelte"
import { notifications } from "@budibase/bbui" import { notifications, ActionButton } from "@budibase/bbui"
import { import {
getBindableProperties, getBindableProperties,
getComponentBindableProperties, getComponentBindableProperties,
} from "builderStore/dataBinding" } from "builderStore/dataBinding"
import { ActionButton } from "@budibase/bbui"
import { capitalise } from "helpers" import { capitalise } from "helpers"
const onUpdateName = async value => { const onUpdateName = async value => {

View File

@ -1,7 +1,7 @@
<script> <script>
import { get } from "svelte/store" import { get } from "svelte/store"
import { Helpers } from "@budibase/bbui"
import { import {
Helpers,
Input, Input,
Checkbox, Checkbox,
Banner, Banner,

View File

@ -1,13 +1,14 @@
<script> <script>
import { store, userSelectedResourceMap } from "builderStore"
import ComponentDropdownMenu from "./ComponentDropdownMenu.svelte"
import NavItem from "components/common/NavItem.svelte"
import { notifications } from "@budibase/bbui"
import { import {
store,
userSelectedResourceMap,
selectedComponentPath, selectedComponentPath,
selectedComponent, selectedComponent,
selectedScreen, selectedScreen,
} from "builderStore" } from "builderStore"
import ComponentDropdownMenu from "./ComponentDropdownMenu.svelte"
import NavItem from "components/common/NavItem.svelte"
import { notifications } from "@budibase/bbui"
import { import {
findComponentPath, findComponentPath,
getComponentText, getComponentText,

View File

@ -4,10 +4,9 @@
import { store, selectedScreen, userSelectedResourceMap } from "builderStore" import { store, selectedScreen, userSelectedResourceMap } from "builderStore"
import NavItem from "components/common/NavItem.svelte" import NavItem from "components/common/NavItem.svelte"
import ComponentTree from "./ComponentTree.svelte" import ComponentTree from "./ComponentTree.svelte"
import { dndStore } from "./dndStore.js" import { dndStore, DropPosition } from "./dndStore.js"
import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte" import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte"
import DNDPositionIndicator from "./DNDPositionIndicator.svelte" import DNDPositionIndicator from "./DNDPositionIndicator.svelte"
import { DropPosition } from "./dndStore"
import ComponentKeyHandler from "./ComponentKeyHandler.svelte" import ComponentKeyHandler from "./ComponentKeyHandler.svelte"
import ComponentScrollWrapper from "./ComponentScrollWrapper.svelte" import ComponentScrollWrapper from "./ComponentScrollWrapper.svelte"

View File

@ -1,5 +1,6 @@
<script> <script>
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
export let datasource export let datasource
export let selected = false export let selected = false

View File

@ -1,4 +1,5 @@
<script> <script>
import { redirect } from "@roxi/routify" import { redirect } from "@roxi/routify"
$redirect("./data") $redirect("./data")
</script> </script>

View File

@ -1,5 +1,6 @@
<script> <script>
import { Badge } from "@budibase/bbui" import { Badge } from "@budibase/bbui"
export let value export let value
$: isError = !value || value.toLowerCase() === "error" $: isError = !value || value.toLowerCase() === "error"

View File

@ -1,5 +1,6 @@
<script> <script>
import { BackupTrigger } from "constants/backend/backups" import { BackupTrigger } from "constants/backend/backups"
export let row export let row
$: trigger = row?.trigger || "manual" $: trigger = row?.trigger || "manual"

Some files were not shown because too many files have changed in this diff Show More