Merge branch 'rework-grid-block-height' of github.com:Budibase/budibase into rework-grid-block-height
This commit is contained in:
commit
8a230337e8
53 changed files with 782 additions and 584 deletions
3
.github/AUTHORS.md
vendored
3
.github/AUTHORS.md
vendored
|
@ -8,4 +8,5 @@ Contributors
|
||||||
* Andrew Kingston - [@aptkingston](https://github.com/aptkingston)
|
* Andrew Kingston - [@aptkingston](https://github.com/aptkingston)
|
||||||
* Michael Drury - [@mike12345567](https://github.com/mike12345567)
|
* Michael Drury - [@mike12345567](https://github.com/mike12345567)
|
||||||
* Peter Clement - [@PClmnt](https://github.com/PClmnt)
|
* Peter Clement - [@PClmnt](https://github.com/PClmnt)
|
||||||
* Rory Powell - [@Rory-Powell](https://github.com/Rory-Powell)
|
* Rory Powell - [@Rory-Powell](https://github.com/Rory-Powell)
|
||||||
|
* Michaël St-Georges [@CSLTech](https://github.com/CSLTech)
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.26.1",
|
"version": "2.26.4",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -9,6 +9,9 @@ import {
|
||||||
AutomationAttachmentContent,
|
AutomationAttachmentContent,
|
||||||
BucketedContent,
|
BucketedContent,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
import stream from "stream"
|
||||||
|
import streamWeb from "node:stream/web"
|
||||||
|
|
||||||
/****************************************************
|
/****************************************************
|
||||||
* NOTE: When adding a new bucket - name *
|
* NOTE: When adding a new bucket - name *
|
||||||
* sure that S3 usages (like budibase-infra) *
|
* sure that S3 usages (like budibase-infra) *
|
||||||
|
@ -53,12 +56,10 @@ export const bucketTTLConfig = (
|
||||||
Rules: [lifecycleRule],
|
Rules: [lifecycleRule],
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
return {
|
||||||
Bucket: bucketName,
|
Bucket: bucketName,
|
||||||
LifecycleConfiguration: lifecycleConfiguration,
|
LifecycleConfiguration: lifecycleConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
return params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUrlAttachment(
|
async function processUrlAttachment(
|
||||||
|
@ -69,9 +70,12 @@ async function processUrlAttachment(
|
||||||
throw new Error(`Unexpected response ${response.statusText}`)
|
throw new Error(`Unexpected response ${response.statusText}`)
|
||||||
}
|
}
|
||||||
const fallbackFilename = path.basename(new URL(attachment.url).pathname)
|
const fallbackFilename = path.basename(new URL(attachment.url).pathname)
|
||||||
|
if (!response.body) {
|
||||||
|
throw new Error("No response received for attachment")
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
filename: attachment.filename || fallbackFilename,
|
filename: attachment.filename || fallbackFilename,
|
||||||
content: response.body,
|
content: stream.Readable.fromWeb(response.body as streamWeb.ReadableStream),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,8 @@ export default function positionDropdown(element, opts) {
|
||||||
applyXStrategy(Strategies.StartToEnd)
|
applyXStrategy(Strategies.StartToEnd)
|
||||||
} else if (align === "left-outside") {
|
} else if (align === "left-outside") {
|
||||||
applyXStrategy(Strategies.EndToStart)
|
applyXStrategy(Strategies.EndToStart)
|
||||||
|
} else if (align === "center") {
|
||||||
|
applyXStrategy(Strategies.MidPoint)
|
||||||
} else {
|
} else {
|
||||||
applyXStrategy(Strategies.StartToStart)
|
applyXStrategy(Strategies.StartToStart)
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,6 +374,16 @@
|
||||||
return `${value.title || (key === "row" ? "Table" : key)} ${requiredSuffix}`
|
return `${value.title || (key === "row" ? "Table" : key)} ${requiredSuffix}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleAttachmentParams(keyValueObj) {
|
||||||
|
let params = {}
|
||||||
|
if (keyValueObj?.length) {
|
||||||
|
for (let param of keyValueObj) {
|
||||||
|
params[param.url] = param.filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
await environment.loadVariables()
|
await environment.loadVariables()
|
||||||
|
@ -381,15 +391,6 @@
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const handleAttachmentParams = keyValuObj => {
|
|
||||||
let params = {}
|
|
||||||
if (keyValuObj?.length) {
|
|
||||||
for (let param of keyValuObj) {
|
|
||||||
params[param.url] = param.filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
|
|
|
@ -25,21 +25,21 @@
|
||||||
return !!schema.constraints?.inclusion?.length
|
return !!schema.constraints?.inclusion?.length
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAttachmentParams = keyValuObj => {
|
function handleAttachmentParams(keyValueObj) {
|
||||||
let params = {}
|
let params = {}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
schema.type === FieldType.ATTACHMENT_SINGLE &&
|
schema.type === FieldType.ATTACHMENT_SINGLE &&
|
||||||
Object.keys(keyValuObj).length === 0
|
Object.keys(keyValueObj).length === 0
|
||||||
) {
|
) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
if (!Array.isArray(keyValuObj)) {
|
if (!Array.isArray(keyValueObj) && keyValueObj) {
|
||||||
keyValuObj = [keyValuObj]
|
keyValueObj = [keyValueObj]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyValuObj.length) {
|
if (keyValueObj.length) {
|
||||||
for (let param of keyValuObj) {
|
for (let param of keyValueObj) {
|
||||||
params[param.url] = param.filename
|
params[param.url] = param.filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,8 @@
|
||||||
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
|
||||||
import Editor from "../../integration/QueryEditor.svelte"
|
import Editor from "../../integration/QueryEditor.svelte"
|
||||||
|
|
||||||
export let defaultValue
|
|
||||||
export let meta
|
export let meta
|
||||||
export let value = defaultValue || (meta.type === "boolean" ? false : "")
|
export let value
|
||||||
export let readonly
|
export let readonly
|
||||||
export let error
|
export let error
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { datasources, tables, integrations, appStore } from "stores/builder"
|
import { datasources, tables, integrations, appStore } from "stores/builder"
|
||||||
|
import { admin } from "stores/portal"
|
||||||
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
||||||
import { TableNames } from "constants"
|
import { TableNames } from "constants"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
|
@ -63,6 +64,7 @@
|
||||||
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
||||||
showAvatars={false}
|
showAvatars={false}
|
||||||
on:updatedatasource={handleGridTableUpdate}
|
on:updatedatasource={handleGridTableUpdate}
|
||||||
|
isCloud={$admin.cloud}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="filter">
|
<svelte:fragment slot="filter">
|
||||||
{#if isUsersTable && $appStore.features.disableUserMetadata}
|
{#if isUsersTable && $appStore.features.disableUserMetadata}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { viewsV2 } from "stores/builder"
|
import { viewsV2 } from "stores/builder"
|
||||||
|
import { admin } from "stores/portal"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import GridCreateEditRowModal from "components/backend/DataTable/modals/grid/GridCreateEditRowModal.svelte"
|
import GridCreateEditRowModal from "components/backend/DataTable/modals/grid/GridCreateEditRowModal.svelte"
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
allowDeleteRows
|
allowDeleteRows
|
||||||
showAvatars={false}
|
showAvatars={false}
|
||||||
on:updatedatasource={handleGridViewUpdate}
|
on:updatedatasource={handleGridViewUpdate}
|
||||||
|
isCloud={$admin.cloud}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="filter">
|
<svelte:fragment slot="filter">
|
||||||
<GridFilterButton />
|
<GridFilterButton />
|
||||||
|
|
|
@ -398,44 +398,50 @@
|
||||||
if (!externalTable) {
|
if (!externalTable) {
|
||||||
return [
|
return [
|
||||||
FIELDS.STRING,
|
FIELDS.STRING,
|
||||||
FIELDS.BARCODEQR,
|
FIELDS.NUMBER,
|
||||||
FIELDS.LONGFORM,
|
|
||||||
FIELDS.OPTIONS,
|
FIELDS.OPTIONS,
|
||||||
FIELDS.ARRAY,
|
FIELDS.ARRAY,
|
||||||
FIELDS.NUMBER,
|
|
||||||
FIELDS.BIGINT,
|
|
||||||
FIELDS.BOOLEAN,
|
FIELDS.BOOLEAN,
|
||||||
FIELDS.DATETIME,
|
FIELDS.DATETIME,
|
||||||
FIELDS.ATTACHMENT_SINGLE,
|
|
||||||
FIELDS.ATTACHMENTS,
|
|
||||||
FIELDS.LINK,
|
FIELDS.LINK,
|
||||||
FIELDS.FORMULA,
|
FIELDS.LONGFORM,
|
||||||
FIELDS.JSON,
|
|
||||||
FIELDS.USER,
|
FIELDS.USER,
|
||||||
FIELDS.USERS,
|
FIELDS.USERS,
|
||||||
|
FIELDS.ATTACHMENT_SINGLE,
|
||||||
|
FIELDS.ATTACHMENTS,
|
||||||
|
FIELDS.FORMULA,
|
||||||
|
FIELDS.JSON,
|
||||||
|
FIELDS.BARCODEQR,
|
||||||
|
FIELDS.BIGINT,
|
||||||
FIELDS.AUTO,
|
FIELDS.AUTO,
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
let fields = [
|
let fields = [
|
||||||
FIELDS.STRING,
|
FIELDS.STRING,
|
||||||
FIELDS.BARCODEQR,
|
|
||||||
FIELDS.LONGFORM,
|
|
||||||
FIELDS.OPTIONS,
|
|
||||||
FIELDS.DATETIME,
|
|
||||||
FIELDS.NUMBER,
|
FIELDS.NUMBER,
|
||||||
|
FIELDS.OPTIONS,
|
||||||
|
FIELDS.ARRAY,
|
||||||
FIELDS.BOOLEAN,
|
FIELDS.BOOLEAN,
|
||||||
FIELDS.FORMULA,
|
FIELDS.DATETIME,
|
||||||
FIELDS.BIGINT,
|
FIELDS.LINK,
|
||||||
|
FIELDS.LONGFORM,
|
||||||
FIELDS.USER,
|
FIELDS.USER,
|
||||||
|
FIELDS.USERS,
|
||||||
|
FIELDS.FORMULA,
|
||||||
|
FIELDS.BARCODEQR,
|
||||||
|
FIELDS.BIGINT,
|
||||||
]
|
]
|
||||||
|
|
||||||
if (datasource && datasource.source !== SourceName.GOOGLE_SHEETS) {
|
// Filter out multiple users for google sheets
|
||||||
fields.push(FIELDS.USERS)
|
if (datasource?.source === SourceName.GOOGLE_SHEETS) {
|
||||||
|
fields = fields.filter(x => x !== FIELDS.USERS)
|
||||||
}
|
}
|
||||||
// no-sql or a spreadsheet
|
|
||||||
if (!externalTable || table.sql) {
|
// Filter out SQL-specific types for non-SQL datasources
|
||||||
fields = [...fields, FIELDS.LINK, FIELDS.ARRAY]
|
if (!table.sql) {
|
||||||
|
fields = fields.filter(x => x !== FIELDS.LINK && x !== FIELDS.ARRAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
50
packages/builder/src/components/common/Dropzone.spec.js
Normal file
50
packages/builder/src/components/common/Dropzone.spec.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import { it, expect, describe, vi } from "vitest"
|
||||||
|
import Dropzone from "./Dropzone.svelte"
|
||||||
|
import { render, fireEvent } from "@testing-library/svelte"
|
||||||
|
import { notifications } from "@budibase/bbui"
|
||||||
|
import { admin } from "stores/portal"
|
||||||
|
|
||||||
|
vi.spyOn(notifications, "error").mockImplementation(() => {})
|
||||||
|
|
||||||
|
describe("Dropzone", () => {
|
||||||
|
let instance = null
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("that the Dropzone is rendered", () => {
|
||||||
|
instance = render(Dropzone, {})
|
||||||
|
expect(instance).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Ensure the correct error message is shown when uploading the file in cloud", async () => {
|
||||||
|
admin.subscribe = vi.fn().mockImplementation(callback => {
|
||||||
|
callback({ cloud: true })
|
||||||
|
return () => {}
|
||||||
|
})
|
||||||
|
instance = render(Dropzone, { props: { fileSizeLimit: 1000000 } }) // 1MB
|
||||||
|
const fileInput = instance.getByLabelText("Select a file to upload")
|
||||||
|
const file = new File(["hello".repeat(2000000)], "hello.png", {
|
||||||
|
type: "image/png",
|
||||||
|
})
|
||||||
|
await fireEvent.change(fileInput, { target: { files: [file] } })
|
||||||
|
expect(notifications.error).toHaveBeenCalledWith(
|
||||||
|
"Files cannot exceed 1MB. Please try again with smaller files."
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Ensure the file size error message is not shown when running on self host", async () => {
|
||||||
|
admin.subscribe = vi.fn().mockImplementation(callback => {
|
||||||
|
callback({ cloud: false })
|
||||||
|
return () => {}
|
||||||
|
})
|
||||||
|
instance = render(Dropzone, { props: { fileSizeLimit: 1000000 } }) // 1MB
|
||||||
|
const fileInput = instance.getByLabelText("Select a file to upload")
|
||||||
|
const file = new File(["hello".repeat(2000000)], "hello.png", {
|
||||||
|
type: "image/png",
|
||||||
|
})
|
||||||
|
await fireEvent.change(fileInput, { target: { files: [file] } })
|
||||||
|
expect(notifications.error).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,9 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { Dropzone, notifications } from "@budibase/bbui"
|
import { Dropzone, notifications } from "@budibase/bbui"
|
||||||
|
import { admin } from "stores/portal"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
|
||||||
export let value = []
|
export let value = []
|
||||||
export let label
|
export let label
|
||||||
|
export let fileSizeLimit = undefined
|
||||||
|
|
||||||
const BYTES_IN_MB = 1000000
|
const BYTES_IN_MB = 1000000
|
||||||
|
|
||||||
|
@ -34,5 +36,6 @@
|
||||||
{label}
|
{label}
|
||||||
{...$$restProps}
|
{...$$restProps}
|
||||||
{processFiles}
|
{processFiles}
|
||||||
{handleFileTooLarge}
|
handleFileTooLarge={$admin.cloud ? handleFileTooLarge : null}
|
||||||
|
{fileSizeLimit}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
export let app
|
export let app
|
||||||
export let color
|
export let color
|
||||||
export let autoSave = false
|
export let autoSave = false
|
||||||
|
export let disabled = false
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,12 +15,16 @@
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="editable-icon">
|
<div class="editable-icon">
|
||||||
<div class="hover" on:click={modal.show}>
|
{#if !disabled}
|
||||||
<Icon name="Edit" {size} color="var(--spectrum-global-color-gray-600)" />
|
<div class="hover" on:click={modal.show}>
|
||||||
</div>
|
<Icon name="Edit" {size} color="var(--spectrum-global-color-gray-600)" />
|
||||||
<div class="normal">
|
</div>
|
||||||
|
<div class="normal">
|
||||||
|
<Icon name={name || "Apps"} {size} {color} />
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
<Icon {name} {size} {color} />
|
<Icon {name} {size} {color} />
|
||||||
</div>
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
|
|
214
packages/builder/src/components/common/UpdateAppForm.svelte
Normal file
214
packages/builder/src/components/common/UpdateAppForm.svelte
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
<script>
|
||||||
|
import { Button, Label, Icon, Input, notifications } from "@budibase/bbui"
|
||||||
|
import { AppStatus } from "constants"
|
||||||
|
import { appStore, initialise } from "stores/builder"
|
||||||
|
import { appsStore } from "stores/portal"
|
||||||
|
import { API } from "api"
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
import { createValidationStore } from "helpers/validation/yup"
|
||||||
|
import * as appValidation from "helpers/validation/yup/app"
|
||||||
|
import EditableIcon from "components/common/EditableIcon.svelte"
|
||||||
|
import { isEqual } from "lodash"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
export let alignActions = "left"
|
||||||
|
|
||||||
|
const values = writable({})
|
||||||
|
const validation = createValidationStore()
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
let updating = false
|
||||||
|
let edited = false
|
||||||
|
let initialised = false
|
||||||
|
|
||||||
|
$: filteredApps = $appsStore.apps.filter(app => app.devId == $appStore.appId)
|
||||||
|
$: app = filteredApps.length ? filteredApps[0] : {}
|
||||||
|
$: appDeployed = app?.status === AppStatus.DEPLOYED
|
||||||
|
|
||||||
|
$: appName = $appStore.name
|
||||||
|
$: appURL = $appStore.url
|
||||||
|
$: appIconName = $appStore.icon?.name
|
||||||
|
$: appIconColor = $appStore.icon?.color
|
||||||
|
|
||||||
|
$: appMeta = {
|
||||||
|
name: appName,
|
||||||
|
url: appURL,
|
||||||
|
iconName: appIconName,
|
||||||
|
iconColor: appIconColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
const initForm = appMeta => {
|
||||||
|
edited = false
|
||||||
|
values.set({
|
||||||
|
...appMeta,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!initialised) {
|
||||||
|
setupValidation()
|
||||||
|
initialised = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = (vals, appMeta) => {
|
||||||
|
const { url } = vals || {}
|
||||||
|
validation.check({
|
||||||
|
...vals,
|
||||||
|
url: url?.[0] === "/" ? url.substring(1, url.length) : url,
|
||||||
|
})
|
||||||
|
edited = !isEqual(vals, appMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On app/apps update, reset the state.
|
||||||
|
$: initForm(appMeta)
|
||||||
|
$: validate($values, appMeta)
|
||||||
|
|
||||||
|
const resolveAppUrl = (template, name) => {
|
||||||
|
let parsedName
|
||||||
|
const resolvedName = resolveAppName(null, name)
|
||||||
|
parsedName = resolvedName ? resolvedName.toLowerCase() : ""
|
||||||
|
const parsedUrl = parsedName ? parsedName.replace(/\s+/g, "-") : ""
|
||||||
|
return encodeURI(parsedUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameToUrl = appName => {
|
||||||
|
let resolvedUrl = resolveAppUrl(null, appName)
|
||||||
|
tidyUrl(resolvedUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolveAppName = (template, name) => {
|
||||||
|
if (template && !name) {
|
||||||
|
return template.name
|
||||||
|
}
|
||||||
|
return name ? name.trim() : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const tidyUrl = url => {
|
||||||
|
if (url && !url.startsWith("/")) {
|
||||||
|
url = `/${url}`
|
||||||
|
}
|
||||||
|
$values.url = url === "" ? null : url
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateIcon = e => {
|
||||||
|
const { name, color } = e.detail
|
||||||
|
$values.iconColor = color
|
||||||
|
$values.iconName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
const setupValidation = async () => {
|
||||||
|
appValidation.name(validation, {
|
||||||
|
apps: $appsStore.apps,
|
||||||
|
currentApp: app,
|
||||||
|
})
|
||||||
|
appValidation.url(validation, {
|
||||||
|
apps: $appsStore.apps,
|
||||||
|
currentApp: app,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateApp() {
|
||||||
|
try {
|
||||||
|
await appsStore.save($appStore.appId, {
|
||||||
|
name: $values.name?.trim(),
|
||||||
|
url: $values.url?.trim(),
|
||||||
|
icon: {
|
||||||
|
name: $values.iconName,
|
||||||
|
color: $values.iconColor,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await initialiseApp()
|
||||||
|
notifications.success("App update successful")
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
notifications.error("Error updating app")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialiseApp = async () => {
|
||||||
|
const applicationPkg = await API.fetchAppPackage($appStore.appId)
|
||||||
|
await initialise(applicationPkg)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="form">
|
||||||
|
<div class="fields">
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Name</Label>
|
||||||
|
<Input
|
||||||
|
bind:value={$values.name}
|
||||||
|
error={$validation.touched.name && $validation.errors.name}
|
||||||
|
on:blur={() => ($validation.touched.name = true)}
|
||||||
|
on:change={nameToUrl($values.name)}
|
||||||
|
disabled={appDeployed}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">URL</Label>
|
||||||
|
<Input
|
||||||
|
bind:value={$values.url}
|
||||||
|
error={$validation.touched.url && $validation.errors.url}
|
||||||
|
on:blur={() => ($validation.touched.url = true)}
|
||||||
|
on:change={tidyUrl($values.url)}
|
||||||
|
placeholder={$values.url
|
||||||
|
? $values.url
|
||||||
|
: `/${resolveAppUrl(null, $values.name)}`}
|
||||||
|
disabled={appDeployed}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Icon</Label>
|
||||||
|
<EditableIcon
|
||||||
|
{app}
|
||||||
|
size="XL"
|
||||||
|
name={$values.iconName}
|
||||||
|
color={$values.iconColor}
|
||||||
|
on:change={updateIcon}
|
||||||
|
disabled={appDeployed}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="actions" class:right={alignActions === "right"}>
|
||||||
|
{#if !appDeployed}
|
||||||
|
<Button
|
||||||
|
cta
|
||||||
|
on:click={async () => {
|
||||||
|
updating = true
|
||||||
|
await updateApp()
|
||||||
|
updating = false
|
||||||
|
dispatch("updated")
|
||||||
|
}}
|
||||||
|
disabled={appDeployed || updating || !edited || !$validation.valid}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
{:else}
|
||||||
|
<div class="edit-info">
|
||||||
|
<Icon size="S" name="Info" /> Unpublish your app to edit name and URL
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.actions.right {
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
.fields {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
}
|
||||||
|
.field {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 80px 220px;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.edit-info {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,68 @@
|
||||||
|
<script>
|
||||||
|
import { Popover, Layout, Icon } from "@budibase/bbui"
|
||||||
|
import UpdateAppForm from "./UpdateAppForm.svelte"
|
||||||
|
|
||||||
|
let formPopover
|
||||||
|
let formPopoverAnchor
|
||||||
|
let formPopoverOpen = false
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={formPopoverAnchor}>
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<div
|
||||||
|
class="app-heading"
|
||||||
|
class:editing={formPopoverOpen}
|
||||||
|
on:click={() => {
|
||||||
|
formPopover.show()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<span class="edit-icon">
|
||||||
|
<Icon size="S" name="Edit" color={"var(--grey-7)"} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
customZindex={998}
|
||||||
|
bind:this={formPopover}
|
||||||
|
align="center"
|
||||||
|
anchor={formPopoverAnchor}
|
||||||
|
offset={20}
|
||||||
|
on:close={() => {
|
||||||
|
formPopoverOpen = false
|
||||||
|
}}
|
||||||
|
on:open={() => {
|
||||||
|
formPopoverOpen = true
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Layout noPadding gap="M">
|
||||||
|
<div class="popover-content">
|
||||||
|
<UpdateAppForm
|
||||||
|
on:updated={() => {
|
||||||
|
formPopover.hide()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.popover-content {
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
.app-heading {
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
}
|
||||||
|
.edit-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.app-heading:hover .edit-icon,
|
||||||
|
.app-heading.editing .edit-icon {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -8,13 +8,11 @@
|
||||||
ActionButton,
|
ActionButton,
|
||||||
Icon,
|
Icon,
|
||||||
Link,
|
Link,
|
||||||
Modal,
|
|
||||||
StatusLight,
|
StatusLight,
|
||||||
AbsTooltip,
|
AbsTooltip,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import RevertModal from "components/deploy/RevertModal.svelte"
|
import RevertModal from "components/deploy/RevertModal.svelte"
|
||||||
import VersionModal from "components/deploy/VersionModal.svelte"
|
import VersionModal from "components/deploy/VersionModal.svelte"
|
||||||
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
|
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import analytics, { Events, EventSource } from "analytics"
|
import analytics, { Events, EventSource } from "analytics"
|
||||||
|
@ -26,7 +24,6 @@
|
||||||
isOnlyUser,
|
isOnlyUser,
|
||||||
appStore,
|
appStore,
|
||||||
deploymentStore,
|
deploymentStore,
|
||||||
initialise,
|
|
||||||
sortedScreens,
|
sortedScreens,
|
||||||
} from "stores/builder"
|
} from "stores/builder"
|
||||||
import TourWrap from "components/portal/onboarding/TourWrap.svelte"
|
import TourWrap from "components/portal/onboarding/TourWrap.svelte"
|
||||||
|
@ -37,7 +34,6 @@
|
||||||
export let loaded
|
export let loaded
|
||||||
|
|
||||||
let unpublishModal
|
let unpublishModal
|
||||||
let updateAppModal
|
|
||||||
let revertModal
|
let revertModal
|
||||||
let versionModal
|
let versionModal
|
||||||
let appActionPopover
|
let appActionPopover
|
||||||
|
@ -61,11 +57,6 @@
|
||||||
$: canPublish = !publishing && loaded && $sortedScreens.length > 0
|
$: canPublish = !publishing && loaded && $sortedScreens.length > 0
|
||||||
$: lastDeployed = getLastDeployedString($deploymentStore, lastOpened)
|
$: lastDeployed = getLastDeployedString($deploymentStore, lastOpened)
|
||||||
|
|
||||||
const initialiseApp = async () => {
|
|
||||||
const applicationPkg = await API.fetchAppPackage($appStore.devId)
|
|
||||||
await initialise(applicationPkg)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getLastDeployedString = deployments => {
|
const getLastDeployedString = deployments => {
|
||||||
return deployments?.length
|
return deployments?.length
|
||||||
? processStringSync("Published {{ duration time 'millisecond' }} ago", {
|
? processStringSync("Published {{ duration time 'millisecond' }} ago", {
|
||||||
|
@ -247,16 +238,12 @@
|
||||||
appActionPopover.hide()
|
appActionPopover.hide()
|
||||||
if (isPublished) {
|
if (isPublished) {
|
||||||
viewApp()
|
viewApp()
|
||||||
} else {
|
|
||||||
updateAppModal.show()
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{$appStore.url}
|
{$appStore.url}
|
||||||
{#if isPublished}
|
{#if isPublished}
|
||||||
<Icon size="S" name="LinkOut" />
|
<Icon size="S" name="LinkOut" />
|
||||||
{:else}
|
|
||||||
<Icon size="S" name="Edit" />
|
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</Body>
|
</Body>
|
||||||
|
@ -330,20 +317,6 @@
|
||||||
Are you sure you want to unpublish the app <b>{selectedApp?.name}</b>?
|
Are you sure you want to unpublish the app <b>{selectedApp?.name}</b>?
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
|
|
||||||
<Modal bind:this={updateAppModal} padding={false} width="600px">
|
|
||||||
<UpdateAppModal
|
|
||||||
app={{
|
|
||||||
name: $appStore.name,
|
|
||||||
url: $appStore.url,
|
|
||||||
icon: $appStore.icon,
|
|
||||||
appId: $appStore.appId,
|
|
||||||
}}
|
|
||||||
onUpdateComplete={async () => {
|
|
||||||
await initialiseApp()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<RevertModal bind:this={revertModal} />
|
<RevertModal bind:this={revertModal} />
|
||||||
<VersionModal hideIcon bind:this={versionModal} />
|
<VersionModal hideIcon bind:this={versionModal} />
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
export let value = null
|
export let value = null
|
||||||
|
|
||||||
$: dataSources = $datasources.list
|
$: dataSources = $datasources.list
|
||||||
.filter(ds => ds.source === "S3" && !ds.config?.endpoint)
|
.filter(ds => ds.source === "S3")
|
||||||
.map(ds => ({
|
.map(ds => ({
|
||||||
label: ds.name,
|
label: ds.name,
|
||||||
value: ds._id,
|
value: ds._id,
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
import { Modal, ModalContent } from "@budibase/bbui"
|
import { Modal, ModalContent } from "@budibase/bbui"
|
||||||
import FreeTrial from "../../../../assets/FreeTrial.svelte"
|
import FreeTrial from "../../../../assets/FreeTrial.svelte"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { auth, licensing } from "stores/portal"
|
import { auth, licensing, admin } from "stores/portal"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { PlanType } from "@budibase/types"
|
import { PlanType } from "@budibase/types"
|
||||||
import { sdk } from "@budibase/shared-core"
|
|
||||||
|
|
||||||
let freeTrialModal
|
let freeTrialModal
|
||||||
|
|
||||||
$: planType = $licensing?.license?.plan?.type
|
$: planType = $licensing?.license?.plan?.type
|
||||||
$: showFreeTrialModal(planType, freeTrialModal)
|
$: showFreeTrialModal(planType, freeTrialModal)
|
||||||
|
$: isOwner = $auth.accountPortalAccess && $admin.cloud
|
||||||
|
|
||||||
const showFreeTrialModal = (planType, freeTrialModal) => {
|
const showFreeTrialModal = (planType, freeTrialModal) => {
|
||||||
if (
|
if (
|
||||||
planType === PlanType.ENTERPRISE_BASIC_TRIAL &&
|
planType === PlanType.ENTERPRISE_BASIC_TRIAL &&
|
||||||
!$auth.user?.freeTrialConfirmedAt &&
|
!$auth.user?.freeTrialConfirmedAt &&
|
||||||
sdk.users.isAdmin($auth.user)
|
isOwner
|
||||||
) {
|
) {
|
||||||
freeTrialModal?.show()
|
freeTrialModal?.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
<script>
|
|
||||||
import { writable, get as svelteGet } from "svelte/store"
|
|
||||||
import {
|
|
||||||
notifications,
|
|
||||||
Input,
|
|
||||||
ModalContent,
|
|
||||||
Layout,
|
|
||||||
Label,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { appsStore } from "stores/portal"
|
|
||||||
import { onMount } from "svelte"
|
|
||||||
import { createValidationStore } from "helpers/validation/yup"
|
|
||||||
import * as appValidation from "helpers/validation/yup/app"
|
|
||||||
import EditableIcon from "../common/EditableIcon.svelte"
|
|
||||||
|
|
||||||
export let app
|
|
||||||
export let onUpdateComplete
|
|
||||||
|
|
||||||
$: appIdParts = app.appId ? app.appId?.split("_") : []
|
|
||||||
$: appId = appIdParts.slice(-1)[0]
|
|
||||||
|
|
||||||
const values = writable({
|
|
||||||
name: app.name,
|
|
||||||
url: app.url,
|
|
||||||
iconName: app.icon?.name,
|
|
||||||
iconColor: app.icon?.color,
|
|
||||||
})
|
|
||||||
const validation = createValidationStore()
|
|
||||||
|
|
||||||
$: {
|
|
||||||
const { url } = $values
|
|
||||||
|
|
||||||
validation.check({
|
|
||||||
...$values,
|
|
||||||
url: url?.[0] === "/" ? url.substring(1, url.length) : url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupValidation = async () => {
|
|
||||||
const applications = svelteGet(appsStore).apps
|
|
||||||
appValidation.name(validation, {
|
|
||||||
apps: applications,
|
|
||||||
currentApp: {
|
|
||||||
...app,
|
|
||||||
appId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
appValidation.url(validation, {
|
|
||||||
apps: applications,
|
|
||||||
currentApp: {
|
|
||||||
...app,
|
|
||||||
appId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// init validation
|
|
||||||
const { url } = $values
|
|
||||||
validation.check({
|
|
||||||
...$values,
|
|
||||||
url: url?.[0] === "/" ? url.substring(1, url.length) : url,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateApp() {
|
|
||||||
try {
|
|
||||||
await appsStore.save(app.appId, {
|
|
||||||
name: $values.name?.trim(),
|
|
||||||
url: $values.url?.trim(),
|
|
||||||
icon: {
|
|
||||||
name: $values.iconName,
|
|
||||||
color: $values.iconColor,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (typeof onUpdateComplete == "function") {
|
|
||||||
onUpdateComplete()
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
notifications.error("Error updating app")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveAppUrl = (template, name) => {
|
|
||||||
let parsedName
|
|
||||||
const resolvedName = resolveAppName(null, name)
|
|
||||||
parsedName = resolvedName ? resolvedName.toLowerCase() : ""
|
|
||||||
const parsedUrl = parsedName ? parsedName.replace(/\s+/g, "-") : ""
|
|
||||||
return encodeURI(parsedUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveAppName = (template, name) => {
|
|
||||||
if (template && !name) {
|
|
||||||
return template.name
|
|
||||||
}
|
|
||||||
return name ? name.trim() : null
|
|
||||||
}
|
|
||||||
|
|
||||||
const tidyUrl = url => {
|
|
||||||
if (url && !url.startsWith("/")) {
|
|
||||||
url = `/${url}`
|
|
||||||
}
|
|
||||||
$values.url = url === "" ? null : url
|
|
||||||
}
|
|
||||||
|
|
||||||
const nameToUrl = appName => {
|
|
||||||
let resolvedUrl = resolveAppUrl(null, appName)
|
|
||||||
tidyUrl(resolvedUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateIcon = e => {
|
|
||||||
const { name, color } = e.detail
|
|
||||||
$values.iconColor = color
|
|
||||||
$values.iconName = name
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(setupValidation)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ModalContent
|
|
||||||
title="Edit name and URL"
|
|
||||||
confirmText="Save"
|
|
||||||
onConfirm={updateApp}
|
|
||||||
disabled={!$validation.valid}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
bind:value={$values.name}
|
|
||||||
error={$validation.touched.name && $validation.errors.name}
|
|
||||||
on:blur={() => ($validation.touched.name = true)}
|
|
||||||
on:change={nameToUrl($values.name)}
|
|
||||||
label="Name"
|
|
||||||
/>
|
|
||||||
<Layout noPadding gap="XS">
|
|
||||||
<Label>Icon</Label>
|
|
||||||
<EditableIcon
|
|
||||||
{app}
|
|
||||||
size="XL"
|
|
||||||
name={$values.iconName}
|
|
||||||
color={$values.iconColor}
|
|
||||||
on:change={updateIcon}
|
|
||||||
/>
|
|
||||||
</Layout>
|
|
||||||
<Input
|
|
||||||
bind:value={$values.url}
|
|
||||||
error={$validation.touched.url && $validation.errors.url}
|
|
||||||
on:blur={() => ($validation.touched.url = true)}
|
|
||||||
on:change={tidyUrl($values.url)}
|
|
||||||
label="URL"
|
|
||||||
placeholder={$values.url
|
|
||||||
? $values.url
|
|
||||||
: `/${resolveAppUrl(null, $values.name)}`}
|
|
||||||
/>
|
|
||||||
</ModalContent>
|
|
|
@ -33,7 +33,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BARCODEQR: {
|
BARCODEQR: {
|
||||||
name: "Barcode/QR",
|
name: "Barcode / QR",
|
||||||
type: FieldType.BARCODEQR,
|
type: FieldType.BARCODEQR,
|
||||||
icon: TypeIconMap[FieldType.BARCODEQR],
|
icon: TypeIconMap[FieldType.BARCODEQR],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -43,7 +43,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
LONGFORM: {
|
LONGFORM: {
|
||||||
name: "Long Form Text",
|
name: "Long form text",
|
||||||
type: FieldType.LONGFORM,
|
type: FieldType.LONGFORM,
|
||||||
icon: TypeIconMap[FieldType.LONGFORM],
|
icon: TypeIconMap[FieldType.LONGFORM],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -53,7 +53,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OPTIONS: {
|
OPTIONS: {
|
||||||
name: "Options",
|
name: "Single select",
|
||||||
type: FieldType.OPTIONS,
|
type: FieldType.OPTIONS,
|
||||||
icon: TypeIconMap[FieldType.OPTIONS],
|
icon: TypeIconMap[FieldType.OPTIONS],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -63,7 +63,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ARRAY: {
|
ARRAY: {
|
||||||
name: "Multi-select",
|
name: "Multi select",
|
||||||
type: FieldType.ARRAY,
|
type: FieldType.ARRAY,
|
||||||
icon: TypeIconMap[FieldType.ARRAY],
|
icon: TypeIconMap[FieldType.ARRAY],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -83,7 +83,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BIGINT: {
|
BIGINT: {
|
||||||
name: "BigInt",
|
name: "Big integer",
|
||||||
type: FieldType.BIGINT,
|
type: FieldType.BIGINT,
|
||||||
icon: TypeIconMap[FieldType.BIGINT],
|
icon: TypeIconMap[FieldType.BIGINT],
|
||||||
},
|
},
|
||||||
|
@ -97,7 +97,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DATETIME: {
|
DATETIME: {
|
||||||
name: "Date/Time",
|
name: "Date / time",
|
||||||
type: FieldType.DATETIME,
|
type: FieldType.DATETIME,
|
||||||
icon: TypeIconMap[FieldType.DATETIME],
|
icon: TypeIconMap[FieldType.DATETIME],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -111,7 +111,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ATTACHMENT_SINGLE: {
|
ATTACHMENT_SINGLE: {
|
||||||
name: "Attachment",
|
name: "Single attachment",
|
||||||
type: FieldType.ATTACHMENT_SINGLE,
|
type: FieldType.ATTACHMENT_SINGLE,
|
||||||
icon: TypeIconMap[FieldType.ATTACHMENT_SINGLE],
|
icon: TypeIconMap[FieldType.ATTACHMENT_SINGLE],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -119,7 +119,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ATTACHMENTS: {
|
ATTACHMENTS: {
|
||||||
name: "Attachment List",
|
name: "Multi attachment",
|
||||||
type: FieldType.ATTACHMENTS,
|
type: FieldType.ATTACHMENTS,
|
||||||
icon: TypeIconMap[FieldType.ATTACHMENTS],
|
icon: TypeIconMap[FieldType.ATTACHMENTS],
|
||||||
constraints: {
|
constraints: {
|
||||||
|
@ -137,7 +137,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
AUTO: {
|
AUTO: {
|
||||||
name: "Auto Column",
|
name: "Auto column",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
icon: TypeIconMap[FieldType.AUTO],
|
icon: TypeIconMap[FieldType.AUTO],
|
||||||
constraints: {},
|
constraints: {},
|
||||||
|
@ -158,7 +158,7 @@ export const FIELDS = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
USER: {
|
USER: {
|
||||||
name: "User",
|
name: "Single user",
|
||||||
type: FieldType.BB_REFERENCE_SINGLE,
|
type: FieldType.BB_REFERENCE_SINGLE,
|
||||||
subtype: BBReferenceFieldSubType.USER,
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
icon: TypeIconMap[FieldType.BB_REFERENCE_SINGLE][
|
icon: TypeIconMap[FieldType.BB_REFERENCE_SINGLE][
|
||||||
|
@ -166,7 +166,7 @@ export const FIELDS = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
USERS: {
|
USERS: {
|
||||||
name: "User List",
|
name: "Multi user",
|
||||||
type: FieldType.BB_REFERENCE,
|
type: FieldType.BB_REFERENCE,
|
||||||
subtype: BBReferenceFieldSubType.USER,
|
subtype: BBReferenceFieldSubType.USER,
|
||||||
icon: TypeIconMap[FieldType.BB_REFERENCE][BBReferenceFieldSubType.USER],
|
icon: TypeIconMap[FieldType.BB_REFERENCE][BBReferenceFieldSubType.USER],
|
||||||
|
|
|
@ -830,7 +830,7 @@ export const getActionBindings = (actions, actionId) => {
|
||||||
* @return {{schema: Object, table: Object}}
|
* @return {{schema: Object, table: Object}}
|
||||||
*/
|
*/
|
||||||
export const getSchemaForDatasourcePlus = (resourceId, options) => {
|
export const getSchemaForDatasourcePlus = (resourceId, options) => {
|
||||||
const isViewV2 = resourceId?.includes("view_")
|
const isViewV2 = resourceId?.startsWith("view_")
|
||||||
const datasource = isViewV2
|
const datasource = isViewV2
|
||||||
? {
|
? {
|
||||||
type: "viewV2",
|
type: "viewV2",
|
||||||
|
|
|
@ -19,11 +19,10 @@ export const name = (validation, { apps, currentApp } = { apps: [] }) => {
|
||||||
// exit early, above validator will fail
|
// exit early, above validator will fail
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (currentApp) {
|
|
||||||
// filter out the current app if present
|
|
||||||
apps = apps.filter(app => app.appId !== currentApp.appId)
|
|
||||||
}
|
|
||||||
return !apps
|
return !apps
|
||||||
|
.filter(app => {
|
||||||
|
return app.appId !== currentApp?.appId
|
||||||
|
})
|
||||||
.map(app => app.name)
|
.map(app => app.name)
|
||||||
.some(appName => appName.toLowerCase() === value.toLowerCase())
|
.some(appName => appName.toLowerCase() === value.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
import { TOUR_KEYS } from "components/portal/onboarding/tours.js"
|
import { TOUR_KEYS } from "components/portal/onboarding/tours.js"
|
||||||
import PreviewOverlay from "./_components/PreviewOverlay.svelte"
|
import PreviewOverlay from "./_components/PreviewOverlay.svelte"
|
||||||
import EnterpriseBasicTrialModal from "components/portal/onboarding/EnterpriseBasicTrialModal.svelte"
|
import EnterpriseBasicTrialModal from "components/portal/onboarding/EnterpriseBasicTrialModal.svelte"
|
||||||
|
import UpdateAppTopNav from "components/common/UpdateAppTopNav.svelte"
|
||||||
|
|
||||||
export let application
|
export let application
|
||||||
|
|
||||||
|
@ -164,7 +165,11 @@
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
<div class="topcenternav">
|
<div class="topcenternav">
|
||||||
<Heading size="XS">{$appStore.name}</Heading>
|
<div class="app-name">
|
||||||
|
<UpdateAppTopNav {application}>
|
||||||
|
<Heading noPadding size="XS">{$appStore.name}</Heading>
|
||||||
|
</UpdateAppTopNav>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toprightnav">
|
<div class="toprightnav">
|
||||||
<span>
|
<span>
|
||||||
|
@ -253,7 +258,6 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
padding: 0px var(--spacing-m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.topleftnav {
|
.topleftnav {
|
||||||
|
|
|
@ -1,30 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import {
|
import { Layout, Divider, Heading, Body } from "@budibase/bbui"
|
||||||
Layout,
|
import UpdateAppForm from "components/common/UpdateAppForm.svelte"
|
||||||
Divider,
|
|
||||||
Heading,
|
|
||||||
Body,
|
|
||||||
Button,
|
|
||||||
Label,
|
|
||||||
Modal,
|
|
||||||
Icon,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import { AppStatus } from "constants"
|
|
||||||
import { appStore, initialise } from "stores/builder"
|
|
||||||
import { appsStore } from "stores/portal"
|
|
||||||
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
|
|
||||||
import { API } from "api"
|
|
||||||
|
|
||||||
let updatingModal
|
|
||||||
|
|
||||||
$: filteredApps = $appsStore.apps.filter(app => app.devId == $appStore.appId)
|
|
||||||
$: app = filteredApps.length ? filteredApps[0] : {}
|
|
||||||
$: appDeployed = app?.status === AppStatus.DEPLOYED
|
|
||||||
|
|
||||||
const initialiseApp = async () => {
|
|
||||||
const applicationPkg = await API.fetchAppPackage($appStore.appId)
|
|
||||||
await initialise(applicationPkg)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
|
@ -33,61 +9,5 @@
|
||||||
<Body>Edit your app's name and URL</Body>
|
<Body>Edit your app's name and URL</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
<UpdateAppForm />
|
||||||
<Layout noPadding gap="XXS">
|
|
||||||
<Label size="L">Name</Label>
|
|
||||||
<Body>{$appStore?.name}</Body>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<Layout noPadding gap="XS">
|
|
||||||
<Label size="L">Icon</Label>
|
|
||||||
<div class="icon">
|
|
||||||
<Icon
|
|
||||||
size="L"
|
|
||||||
name={$appStore?.icon?.name || "Apps"}
|
|
||||||
color={$appStore?.icon?.color}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<Layout noPadding gap="XXS">
|
|
||||||
<Label size="L">URL</Label>
|
|
||||||
<Body>{$appStore.url}</Body>
|
|
||||||
</Layout>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
cta
|
|
||||||
on:click={() => {
|
|
||||||
updatingModal.show()
|
|
||||||
}}
|
|
||||||
disabled={appDeployed}
|
|
||||||
tooltip={appDeployed
|
|
||||||
? "You must unpublish your app to make changes"
|
|
||||||
: null}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<Modal bind:this={updatingModal} padding={false} width="600px">
|
|
||||||
<UpdateAppModal
|
|
||||||
app={{
|
|
||||||
name: $appStore.name,
|
|
||||||
url: $appStore.url,
|
|
||||||
icon: $appStore.icon,
|
|
||||||
appId: $appStore.appId,
|
|
||||||
}}
|
|
||||||
onUpdateComplete={async () => {
|
|
||||||
await initialiseApp()
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import { isActive, redirect, goto, url } from "@roxi/routify"
|
import { isActive, redirect, goto, url } from "@roxi/routify"
|
||||||
import { Icon, notifications, Tabs, Tab } from "@budibase/bbui"
|
import { Icon, notifications, Tabs, Tab } from "@budibase/bbui"
|
||||||
import { organisation, auth, menu, appsStore, licensing } from "stores/portal"
|
import {
|
||||||
|
organisation,
|
||||||
|
auth,
|
||||||
|
menu,
|
||||||
|
appsStore,
|
||||||
|
licensing,
|
||||||
|
admin,
|
||||||
|
} from "stores/portal"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import UpgradeButton from "./_components/UpgradeButton.svelte"
|
import UpgradeButton from "./_components/UpgradeButton.svelte"
|
||||||
import MobileMenu from "./_components/MobileMenu.svelte"
|
import MobileMenu from "./_components/MobileMenu.svelte"
|
||||||
|
@ -20,6 +27,7 @@
|
||||||
$: $url(), updateActiveTab($menu)
|
$: $url(), updateActiveTab($menu)
|
||||||
$: isOnboarding =
|
$: isOnboarding =
|
||||||
!$appsStore.apps.length && sdk.users.hasBuilderPermissions($auth.user)
|
!$appsStore.apps.length && sdk.users.hasBuilderPermissions($auth.user)
|
||||||
|
$: isOwner = $auth.accountPortalAccess && $admin.cloud
|
||||||
|
|
||||||
const updateActiveTab = menu => {
|
const updateActiveTab = menu => {
|
||||||
for (let entry of menu) {
|
for (let entry of menu) {
|
||||||
|
@ -38,8 +46,7 @@
|
||||||
const showFreeTrialBanner = () => {
|
const showFreeTrialBanner = () => {
|
||||||
return (
|
return (
|
||||||
$licensing.license?.plan?.type ===
|
$licensing.license?.plan?.type ===
|
||||||
Constants.PlanType.ENTERPRISE_BASIC_TRIAL &&
|
Constants.PlanType.ENTERPRISE_BASIC_TRIAL && isOwner
|
||||||
sdk.users.isAdmin($auth.user)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,7 @@ export class AppsStore extends BudiStore {
|
||||||
if (updatedAppIndex !== -1) {
|
if (updatedAppIndex !== -1) {
|
||||||
let updatedApp = state.apps[updatedAppIndex]
|
let updatedApp = state.apps[updatedAppIndex]
|
||||||
updatedApp = { ...updatedApp, ...value }
|
updatedApp = { ...updatedApp, ...value }
|
||||||
state.apps = state.apps.splice(updatedAppIndex, 1, updatedApp)
|
state.apps.splice(updatedAppIndex, 1, updatedApp)
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
const { environmentStore } = getContext("sdk")
|
||||||
const {
|
const {
|
||||||
styleable,
|
styleable,
|
||||||
API,
|
API,
|
||||||
|
@ -161,6 +162,7 @@
|
||||||
notifySuccess={notificationStore.actions.success}
|
notifySuccess={notificationStore.actions.success}
|
||||||
notifyError={notificationStore.actions.error}
|
notifyError={notificationStore.actions.error}
|
||||||
buttons={enrichedButtons}
|
buttons={enrichedButtons}
|
||||||
|
isCloud={$environmentStore.cloud}
|
||||||
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
on:rowclick={e => onRowClick?.({ row: e.detail })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
|
||||||
const { API, notificationStore } = getContext("sdk")
|
const { API, notificationStore, environmentStore } = getContext("sdk")
|
||||||
const formContext = getContext("form")
|
const formContext = getContext("form")
|
||||||
const BYTES_IN_MB = 1000000
|
const BYTES_IN_MB = 1000000
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@
|
||||||
error={fieldState.error}
|
error={fieldState.error}
|
||||||
on:change={handleChange}
|
on:change={handleChange}
|
||||||
{processFiles}
|
{processFiles}
|
||||||
{handleFileTooLarge}
|
handleFileTooLarge={$environmentStore.cloud ? handleFileTooLarge : null}
|
||||||
{handleTooManyFiles}
|
{handleTooManyFiles}
|
||||||
{maximum}
|
{maximum}
|
||||||
{extensions}
|
{extensions}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
export let schema
|
export let schema
|
||||||
export let maximum
|
export let maximum
|
||||||
|
|
||||||
const { API, notifications } = getContext("grid")
|
const { API, notifications, props } = getContext("grid")
|
||||||
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
|
const imageExtensions = ["png", "tiff", "gif", "raw", "jpg", "jpeg"]
|
||||||
|
|
||||||
let isOpen = false
|
let isOpen = false
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
on:change={e => onChange(e.detail)}
|
on:change={e => onChange(e.detail)}
|
||||||
maximum={maximum || schema.constraints?.length?.maximum}
|
maximum={maximum || schema.constraints?.length?.maximum}
|
||||||
{processFiles}
|
{processFiles}
|
||||||
{handleFileTooLarge}
|
handleFileTooLarge={$props.isCloud ? handleFileTooLarge : null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</GridPopover>
|
</GridPopover>
|
||||||
|
|
|
@ -386,7 +386,7 @@
|
||||||
>
|
>
|
||||||
Hide column
|
Hide column
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{#if $config.canEditColumns && column.schema.type === "link" && column.schema.tableId === TableNames.USERS}
|
{#if $config.canEditColumns && column.schema.type === "link" && column.schema.tableId === TableNames.USERS && !column.schema.autocolumn}
|
||||||
<MenuItem icon="User" on:click={openMigrationModal}>
|
<MenuItem icon="User" on:click={openMigrationModal}>
|
||||||
Migrate to user column
|
Migrate to user column
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
export let notifySuccess = null
|
export let notifySuccess = null
|
||||||
export let notifyError = null
|
export let notifyError = null
|
||||||
export let buttons = null
|
export let buttons = null
|
||||||
|
export let isCloud = null
|
||||||
|
|
||||||
// Unique identifier for DOM nodes inside this instance
|
// Unique identifier for DOM nodes inside this instance
|
||||||
const gridID = `grid-${Math.random().toString().slice(2)}`
|
const gridID = `grid-${Math.random().toString().slice(2)}`
|
||||||
|
@ -108,6 +109,7 @@
|
||||||
notifySuccess,
|
notifySuccess,
|
||||||
notifyError,
|
notifyError,
|
||||||
buttons,
|
buttons,
|
||||||
|
isCloud,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Derive min height and make available in context
|
// Derive min height and make available in context
|
||||||
|
|
|
@ -17,8 +17,10 @@ module FetchMock {
|
||||||
raw: () => {
|
raw: () => {
|
||||||
return { "content-type": ["application/json"] }
|
return { "content-type": ["application/json"] }
|
||||||
},
|
},
|
||||||
get: () => {
|
get: (name: string) => {
|
||||||
return ["application/json"]
|
if (name.toLowerCase() === "content-type") {
|
||||||
|
return ["application/json"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
json: async () => {
|
json: async () => {
|
||||||
|
|
|
@ -79,8 +79,7 @@
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"global-agent": "3.0.0",
|
"global-agent": "3.0.0",
|
||||||
"google-auth-library": "7.12.0",
|
"google-spreadsheet": "4.1.2",
|
||||||
"google-spreadsheet": "3.2.0",
|
|
||||||
"ioredis": "5.3.2",
|
"ioredis": "5.3.2",
|
||||||
"isolated-vm": "^4.7.2",
|
"isolated-vm": "^4.7.2",
|
||||||
"jimp": "0.22.10",
|
"jimp": "0.22.10",
|
||||||
|
@ -125,7 +124,6 @@
|
||||||
"@swc/jest": "0.2.27",
|
"@swc/jest": "0.2.27",
|
||||||
"@types/archiver": "6.0.2",
|
"@types/archiver": "6.0.2",
|
||||||
"@types/global-agent": "2.1.1",
|
"@types/global-agent": "2.1.1",
|
||||||
"@types/google-spreadsheet": "3.1.5",
|
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/koa": "2.13.4",
|
"@types/koa": "2.13.4",
|
||||||
"@types/koa-send": "^4.1.6",
|
"@types/koa-send": "^4.1.6",
|
||||||
|
|
|
@ -265,7 +265,10 @@ export class ExternalRequest<T extends Operation> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputProcessing(row: Row | undefined, table: Table) {
|
inputProcessing<T extends Row | undefined>(
|
||||||
|
row: T,
|
||||||
|
table: Table
|
||||||
|
): { row: T; manyRelationships: ManyRelationship[] } {
|
||||||
if (!row) {
|
if (!row) {
|
||||||
return { row, manyRelationships: [] }
|
return { row, manyRelationships: [] }
|
||||||
}
|
}
|
||||||
|
@ -346,7 +349,7 @@ export class ExternalRequest<T extends Operation> {
|
||||||
// we return the relationships that may need to be created in the through table
|
// we return the relationships that may need to be created in the through table
|
||||||
// we do this so that if the ID is generated by the DB it can be inserted
|
// we do this so that if the ID is generated by the DB it can be inserted
|
||||||
// after the fact
|
// after the fact
|
||||||
return { row: newRow, manyRelationships }
|
return { row: newRow as T, manyRelationships }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -598,6 +601,18 @@ export class ExternalRequest<T extends Operation> {
|
||||||
// clean up row on ingress using schema
|
// clean up row on ingress using schema
|
||||||
const processed = this.inputProcessing(row, table)
|
const processed = this.inputProcessing(row, table)
|
||||||
row = processed.row
|
row = processed.row
|
||||||
|
let manyRelationships = processed.manyRelationships
|
||||||
|
|
||||||
|
if (!row && rows) {
|
||||||
|
manyRelationships = []
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
const processed = this.inputProcessing(rows[i], table)
|
||||||
|
rows[i] = processed.row
|
||||||
|
if (processed.manyRelationships.length) {
|
||||||
|
manyRelationships.push(...processed.manyRelationships)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
operation === Operation.DELETE &&
|
operation === Operation.DELETE &&
|
||||||
(filters == null || Object.keys(filters).length === 0)
|
(filters == null || Object.keys(filters).length === 0)
|
||||||
|
|
|
@ -292,11 +292,6 @@ export const getSignedUploadURL = async function (ctx: Ctx) {
|
||||||
ctx.throw(400, "The specified datasource could not be found")
|
ctx.throw(400, "The specified datasource could not be found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we aren't using a custom endpoint
|
|
||||||
if (datasource?.config?.endpoint) {
|
|
||||||
ctx.throw(400, "S3 datasources with custom endpoints are not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine type of datasource and generate signed URL
|
// Determine type of datasource and generate signed URL
|
||||||
let signedUrl
|
let signedUrl
|
||||||
let publicUrl
|
let publicUrl
|
||||||
|
@ -309,6 +304,7 @@ export const getSignedUploadURL = async function (ctx: Ctx) {
|
||||||
try {
|
try {
|
||||||
const s3 = new AWS.S3({
|
const s3 = new AWS.S3({
|
||||||
region: awsRegion,
|
region: awsRegion,
|
||||||
|
endpoint: datasource?.config?.endpoint as string,
|
||||||
accessKeyId: datasource?.config?.accessKeyId as string,
|
accessKeyId: datasource?.config?.accessKeyId as string,
|
||||||
secretAccessKey: datasource?.config?.secretAccessKey as string,
|
secretAccessKey: datasource?.config?.secretAccessKey as string,
|
||||||
apiVersion: "2006-03-01",
|
apiVersion: "2006-03-01",
|
||||||
|
@ -316,7 +312,11 @@ export const getSignedUploadURL = async function (ctx: Ctx) {
|
||||||
})
|
})
|
||||||
const params = { Bucket: bucket, Key: key }
|
const params = { Bucket: bucket, Key: key }
|
||||||
signedUrl = s3.getSignedUrl("putObject", params)
|
signedUrl = s3.getSignedUrl("putObject", params)
|
||||||
publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`
|
if (datasource?.config?.endpoint) {
|
||||||
|
publicUrl = `${datasource.config.endpoint}/${bucket}/${key}`
|
||||||
|
} else {
|
||||||
|
publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
ctx.throw(400, error)
|
ctx.throw(400, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import { builderSocket } from "../../../websockets"
|
import { builderSocket } from "../../../websockets"
|
||||||
|
import { inputProcessing } from "../../../utilities/rowProcessor"
|
||||||
|
|
||||||
function getDatasourceId(table: Table) {
|
function getDatasourceId(table: Table) {
|
||||||
if (!table) {
|
if (!table) {
|
||||||
|
@ -80,7 +81,7 @@ export async function destroy(ctx: UserCtx) {
|
||||||
export async function bulkImport(
|
export async function bulkImport(
|
||||||
ctx: UserCtx<BulkImportRequest, BulkImportResponse>
|
ctx: UserCtx<BulkImportRequest, BulkImportResponse>
|
||||||
) {
|
) {
|
||||||
const table = await sdk.tables.getTable(ctx.params.tableId)
|
let table = await sdk.tables.getTable(ctx.params.tableId)
|
||||||
const { rows } = ctx.request.body
|
const { rows } = ctx.request.body
|
||||||
const schema = table.schema
|
const schema = table.schema
|
||||||
|
|
||||||
|
@ -88,7 +89,15 @@ export async function bulkImport(
|
||||||
ctx.throw(400, "Provided data import information is invalid.")
|
ctx.throw(400, "Provided data import information is invalid.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedRows = parse(rows, schema)
|
const parsedRows = []
|
||||||
|
for (const row of parse(rows, schema)) {
|
||||||
|
const processed = await inputProcessing(ctx.user?._id, table, row, {
|
||||||
|
noAutoRelationships: true,
|
||||||
|
})
|
||||||
|
parsedRows.push(processed.row)
|
||||||
|
table = processed.table
|
||||||
|
}
|
||||||
|
|
||||||
await handleRequest(Operation.BULK_CREATE, table._id!, {
|
await handleRequest(Operation.BULK_CREATE, table._id!, {
|
||||||
rows: parsedRows,
|
rows: parsedRows,
|
||||||
})
|
})
|
||||||
|
|
|
@ -163,7 +163,10 @@ async function generateAttachmentRow(attachment: AutomationAttachment) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { filename } = attachment
|
const { filename } = attachment
|
||||||
const extension = path.extname(filename)
|
let extension = path.extname(filename)
|
||||||
|
if (extension.startsWith(".")) {
|
||||||
|
extension = extension.substring(1, extension.length)
|
||||||
|
}
|
||||||
const attachmentResult = await objectStore.processAutomationAttachment(
|
const attachmentResult = await objectStore.processAutomationAttachment(
|
||||||
attachment
|
attachment
|
||||||
)
|
)
|
||||||
|
@ -182,8 +185,8 @@ async function generateAttachmentRow(attachment: AutomationAttachment) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
size,
|
size,
|
||||||
name: filename,
|
|
||||||
extension,
|
extension,
|
||||||
|
name: filename,
|
||||||
key: s3Key,
|
key: s3Key,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -94,18 +94,6 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// have to clean up the row, remove the table from it
|
|
||||||
const ctx: any = buildCtx(appId, emitter, {
|
|
||||||
body: {
|
|
||||||
...inputs.row,
|
|
||||||
_id: inputs.rowId,
|
|
||||||
},
|
|
||||||
params: {
|
|
||||||
rowId: inputs.rowId,
|
|
||||||
tableId: tableId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (tableId) {
|
if (tableId) {
|
||||||
inputs.row = await automationUtils.cleanUpRow(
|
inputs.row = await automationUtils.cleanUpRow(
|
||||||
|
@ -118,6 +106,17 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) {
|
||||||
inputs.row
|
inputs.row
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// have to clean up the row, remove the table from it
|
||||||
|
const ctx: any = buildCtx(appId, emitter, {
|
||||||
|
body: {
|
||||||
|
...inputs.row,
|
||||||
|
_id: inputs.rowId,
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
rowId: inputs.rowId,
|
||||||
|
tableId: tableId,
|
||||||
|
},
|
||||||
|
})
|
||||||
await rowController.patch(ctx)
|
await rowController.patch(ctx)
|
||||||
return {
|
return {
|
||||||
row: ctx.body,
|
row: ctx.body,
|
||||||
|
|
|
@ -88,7 +88,7 @@ describe.each(
|
||||||
let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, {
|
let res = await setup.runStep(setup.actions.EXECUTE_QUERY.stepId, {
|
||||||
query: { queryId: "wrong_id" },
|
query: { queryId: "wrong_id" },
|
||||||
})
|
})
|
||||||
expect(res.response).toEqual("Error: CouchDB error: missing")
|
expect(res.response).toBeDefined()
|
||||||
expect(res.success).toEqual(false)
|
expect(res.success).toEqual(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -158,12 +158,12 @@ const SCHEMA: Integration = {
|
||||||
|
|
||||||
class GoogleSheetsIntegration implements DatasourcePlus {
|
class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
private readonly config: GoogleSheetsConfig
|
private readonly config: GoogleSheetsConfig
|
||||||
private client: GoogleSpreadsheet
|
private readonly spreadsheetId: string
|
||||||
|
private client: GoogleSpreadsheet = undefined!
|
||||||
|
|
||||||
constructor(config: GoogleSheetsConfig) {
|
constructor(config: GoogleSheetsConfig) {
|
||||||
this.config = config
|
this.config = config
|
||||||
const spreadsheetId = this.cleanSpreadsheetUrl(this.config.spreadsheetId)
|
this.spreadsheetId = this.cleanSpreadsheetUrl(this.config.spreadsheetId)
|
||||||
this.client = new GoogleSpreadsheet(spreadsheetId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async testConnection(): Promise<ConnectionInfo> {
|
async testConnection(): Promise<ConnectionInfo> {
|
||||||
|
@ -191,7 +191,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
* @param spreadsheetId - the URL or standard spreadsheetId of the google sheet
|
* @param spreadsheetId - the URL or standard spreadsheetId of the google sheet
|
||||||
* @returns spreadsheet Id of the google sheet
|
* @returns spreadsheet Id of the google sheet
|
||||||
*/
|
*/
|
||||||
cleanSpreadsheetUrl(spreadsheetId: string) {
|
private cleanSpreadsheetUrl(spreadsheetId: string) {
|
||||||
if (!spreadsheetId) {
|
if (!spreadsheetId) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"You must set a spreadsheet ID in your configuration to fetch tables."
|
"You must set a spreadsheet ID in your configuration to fetch tables."
|
||||||
|
@ -201,7 +201,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
return parts.length > 5 ? parts[5] : spreadsheetId
|
return parts.length > 5 ? parts[5] : spreadsheetId
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchAccessToken(
|
private async fetchAccessToken(
|
||||||
payload: AuthTokenRequest
|
payload: AuthTokenRequest
|
||||||
): Promise<AuthTokenResponse> {
|
): Promise<AuthTokenResponse> {
|
||||||
const response = await fetch("https://www.googleapis.com/oauth2/v4/token", {
|
const response = await fetch("https://www.googleapis.com/oauth2/v4/token", {
|
||||||
|
@ -226,7 +226,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect() {
|
private async connect() {
|
||||||
try {
|
try {
|
||||||
await setupCreationAuth(this.config)
|
await setupCreationAuth(this.config)
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
access_token: tokenResponse.access_token,
|
access_token: tokenResponse.access_token,
|
||||||
})
|
})
|
||||||
|
|
||||||
this.client.useOAuth2Client(oauthClient)
|
this.client = new GoogleSpreadsheet(this.spreadsheetId, oauthClient)
|
||||||
await this.client.loadInfo()
|
await this.client.loadInfo()
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// this happens for xlsx imports
|
// this happens for xlsx imports
|
||||||
|
@ -271,7 +271,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
return sheets.map(s => s.title)
|
return sheets.map(s => s.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTableSchema(
|
private getTableSchema(
|
||||||
title: string,
|
title: string,
|
||||||
headerValues: string[],
|
headerValues: string[],
|
||||||
datasourceId: string,
|
datasourceId: string,
|
||||||
|
@ -385,18 +385,22 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRowObject(headers: string[], values: string[], rowNumber: number) {
|
private buildRowObject(
|
||||||
|
headers: string[],
|
||||||
|
values: Record<string, string>,
|
||||||
|
rowNumber: number
|
||||||
|
) {
|
||||||
const rowObject: { rowNumber: number } & Row = {
|
const rowObject: { rowNumber: number } & Row = {
|
||||||
rowNumber,
|
rowNumber,
|
||||||
_id: rowNumber.toString(),
|
_id: rowNumber.toString(),
|
||||||
}
|
}
|
||||||
for (let i = 0; i < headers.length; i++) {
|
for (let i = 0; i < headers.length; i++) {
|
||||||
rowObject[headers[i]] = values[i]
|
rowObject[headers[i]] = values[headers[i]]
|
||||||
}
|
}
|
||||||
return rowObject
|
return rowObject
|
||||||
}
|
}
|
||||||
|
|
||||||
async createTable(name?: string) {
|
private async createTable(name?: string) {
|
||||||
if (!name) {
|
if (!name) {
|
||||||
throw new Error("Must provide name for new sheet.")
|
throw new Error("Must provide name for new sheet.")
|
||||||
}
|
}
|
||||||
|
@ -409,7 +413,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTable(table: TableRequest) {
|
private async updateTable(table: TableRequest) {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const sheet = this.client.sheetsByTitle[table.name]
|
const sheet = this.client.sheetsByTitle[table.name]
|
||||||
await sheet.loadHeaderRow()
|
await sheet.loadHeaderRow()
|
||||||
|
@ -456,7 +460,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteTable(sheet: any) {
|
private async deleteTable(sheet: any) {
|
||||||
try {
|
try {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const sheetToDelete = this.client.sheetsByTitle[sheet]
|
const sheetToDelete = this.client.sheetsByTitle[sheet]
|
||||||
|
@ -475,7 +479,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
||||||
const row = await sheet.addRow(rowToInsert)
|
const row = await sheet.addRow(rowToInsert)
|
||||||
return [
|
return [
|
||||||
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber),
|
this.buildRowObject(sheet.headerValues, row.toObject(), row.rowNumber),
|
||||||
]
|
]
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error writing to google sheets", err)
|
console.error("Error writing to google sheets", err)
|
||||||
|
@ -483,7 +487,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBulk(query: { sheet: string; rows: Row[] }) {
|
private async createBulk(query: { sheet: string; rows: Row[] }) {
|
||||||
try {
|
try {
|
||||||
await this.connect()
|
await this.connect()
|
||||||
const sheet = this.client.sheetsByTitle[query.sheet]
|
const sheet = this.client.sheetsByTitle[query.sheet]
|
||||||
|
@ -493,7 +497,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
}
|
}
|
||||||
const rows = await sheet.addRows(rowsToInsert)
|
const rows = await sheet.addRows(rowsToInsert)
|
||||||
return rows.map(row =>
|
return rows.map(row =>
|
||||||
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber)
|
this.buildRowObject(sheet.headerValues, row.toObject(), row.rowNumber)
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error bulk writing to google sheets", err)
|
console.error("Error bulk writing to google sheets", err)
|
||||||
|
@ -548,7 +552,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
let response = []
|
let response = []
|
||||||
for (let row of filtered) {
|
for (let row of filtered) {
|
||||||
response.push(
|
response.push(
|
||||||
this.buildRowObject(headerValues, row._rawData, row._rowNumber)
|
this.buildRowObject(headerValues, row.toObject(), row._rowNumber)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,10 +602,10 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
const updateValues =
|
const updateValues =
|
||||||
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
typeof query.row === "string" ? JSON.parse(query.row) : query.row
|
||||||
for (let key in updateValues) {
|
for (let key in updateValues) {
|
||||||
row[key] = updateValues[key]
|
row.set(key, updateValues[key])
|
||||||
|
|
||||||
if (row[key] === null) {
|
if (row.get(key) === null) {
|
||||||
row[key] = ""
|
row.set(key, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
const { type, subtype, constraints } = query.table.schema[key]
|
const { type, subtype, constraints } = query.table.schema[key]
|
||||||
|
@ -609,13 +613,17 @@ class GoogleSheetsIntegration implements DatasourcePlus {
|
||||||
type === FieldType.BB_REFERENCE &&
|
type === FieldType.BB_REFERENCE &&
|
||||||
subtype === BBReferenceFieldSubType.USER &&
|
subtype === BBReferenceFieldSubType.USER &&
|
||||||
constraints?.type !== "array"
|
constraints?.type !== "array"
|
||||||
if (isDeprecatedSingleUser && Array.isArray(row[key])) {
|
if (isDeprecatedSingleUser && Array.isArray(row.get(key))) {
|
||||||
row[key] = row[key][0]
|
row.set(key, row.get(key)[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await row.save()
|
await row.save()
|
||||||
return [
|
return [
|
||||||
this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber),
|
this.buildRowObject(
|
||||||
|
sheet.headerValues,
|
||||||
|
row.toObject(),
|
||||||
|
row.rowNumber
|
||||||
|
),
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Row does not exist.")
|
throw new Error("Row does not exist.")
|
||||||
|
|
|
@ -16,6 +16,7 @@ import get from "lodash/get"
|
||||||
import * as https from "https"
|
import * as https from "https"
|
||||||
import qs from "querystring"
|
import qs from "querystring"
|
||||||
import fetch from "node-fetch"
|
import fetch from "node-fetch"
|
||||||
|
import type { Response } from "node-fetch"
|
||||||
import { formatBytes } from "../utilities"
|
import { formatBytes } from "../utilities"
|
||||||
import { performance } from "perf_hooks"
|
import { performance } from "perf_hooks"
|
||||||
import FormData from "form-data"
|
import FormData from "form-data"
|
||||||
|
@ -25,6 +26,7 @@ import { handleFileResponse, handleXml } from "./utils"
|
||||||
import { parse } from "content-disposition"
|
import { parse } from "content-disposition"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Builder as XmlBuilder } from "xml2js"
|
import { Builder as XmlBuilder } from "xml2js"
|
||||||
|
import { getAttachmentHeaders } from "./utils/restUtils"
|
||||||
|
|
||||||
enum BodyType {
|
enum BodyType {
|
||||||
NONE = "none",
|
NONE = "none",
|
||||||
|
@ -130,14 +132,15 @@ class RestIntegration implements IntegrationBase {
|
||||||
this.config = config
|
this.config = config
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseResponse(response: any, pagination: PaginationConfig | null) {
|
async parseResponse(response: Response, pagination: PaginationConfig | null) {
|
||||||
let data: any[] | string | undefined,
|
let data: any[] | string | undefined,
|
||||||
raw: string | undefined,
|
raw: string | undefined,
|
||||||
headers: Record<string, string> = {},
|
headers: Record<string, string[] | string> = {},
|
||||||
filename: string | undefined
|
filename: string | undefined
|
||||||
|
|
||||||
const contentType = response.headers.get("content-type") || ""
|
const { contentType, contentDisposition } = getAttachmentHeaders(
|
||||||
const contentDisposition = response.headers.get("content-disposition") || ""
|
response.headers
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
contentDisposition.includes("filename") ||
|
contentDisposition.includes("filename") ||
|
||||||
contentDisposition.includes("attachment") ||
|
contentDisposition.includes("attachment") ||
|
||||||
|
@ -172,7 +175,7 @@ class RestIntegration implements IntegrationBase {
|
||||||
throw `Failed to parse response body: ${err}`
|
throw `Failed to parse response body: ${err}`
|
||||||
}
|
}
|
||||||
|
|
||||||
let contentLength: string = response.headers.get("content-length")
|
let contentLength = response.headers.get("content-length")
|
||||||
if (!contentLength && raw) {
|
if (!contentLength && raw) {
|
||||||
contentLength = Buffer.byteLength(raw, "utf8").toString()
|
contentLength = Buffer.byteLength(raw, "utf8").toString()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,11 @@ jest.mock("node-fetch", () => {
|
||||||
raw: () => {
|
raw: () => {
|
||||||
return { "content-type": ["application/json"] }
|
return { "content-type": ["application/json"] }
|
||||||
},
|
},
|
||||||
get: () => ["application/json"],
|
get: (name: string) => {
|
||||||
|
if (name.toLowerCase() === "content-type") {
|
||||||
|
return ["application/json"]
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
json: jest.fn(() => ({
|
json: jest.fn(() => ({
|
||||||
my_next_cursor: 123,
|
my_next_cursor: 123,
|
||||||
|
@ -211,7 +215,16 @@ describe("REST Integration", () => {
|
||||||
json: json ? async () => json : undefined,
|
json: json ? async () => json : undefined,
|
||||||
text: text ? async () => text : undefined,
|
text: text ? async () => text : undefined,
|
||||||
headers: {
|
headers: {
|
||||||
get: (key: any) => (key === "content-length" ? 100 : header),
|
get: (key: string) => {
|
||||||
|
switch (key.toLowerCase()) {
|
||||||
|
case "content-length":
|
||||||
|
return 100
|
||||||
|
case "content-type":
|
||||||
|
return header
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
},
|
||||||
raw: () => ({ "content-type": header }),
|
raw: () => ({ "content-type": header }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
38
packages/server/src/integrations/tests/restUtils.spec.ts
Normal file
38
packages/server/src/integrations/tests/restUtils.spec.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { getAttachmentHeaders } from "../utils/restUtils"
|
||||||
|
import type { Headers } from "node-fetch"
|
||||||
|
|
||||||
|
function headers(dispositionValue: string) {
|
||||||
|
return {
|
||||||
|
get: (name: string) => {
|
||||||
|
if (name.toLowerCase() === "content-disposition") {
|
||||||
|
return dispositionValue
|
||||||
|
} else {
|
||||||
|
return "application/pdf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set: () => {},
|
||||||
|
} as unknown as Headers
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("getAttachmentHeaders", () => {
|
||||||
|
it("should be able to correctly handle a broken content-disposition", () => {
|
||||||
|
const { contentDisposition } = getAttachmentHeaders(
|
||||||
|
headers(`filename="report.pdf"`)
|
||||||
|
)
|
||||||
|
expect(contentDisposition).toBe(`attachment; filename="report.pdf"`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to correctly with a filename that could cause problems", () => {
|
||||||
|
const { contentDisposition } = getAttachmentHeaders(
|
||||||
|
headers(`filename="report;.pdf"`)
|
||||||
|
)
|
||||||
|
expect(contentDisposition).toBe(`attachment; filename="report;.pdf"`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not touch a valid content-disposition", () => {
|
||||||
|
const { contentDisposition } = getAttachmentHeaders(
|
||||||
|
headers(`inline; filename="report.pdf"`)
|
||||||
|
)
|
||||||
|
expect(contentDisposition).toBe(`inline; filename="report.pdf"`)
|
||||||
|
})
|
||||||
|
})
|
28
packages/server/src/integrations/utils/restUtils.ts
Normal file
28
packages/server/src/integrations/utils/restUtils.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import type { Headers } from "node-fetch"
|
||||||
|
|
||||||
|
export function getAttachmentHeaders(headers: Headers) {
|
||||||
|
const contentType = headers.get("content-type") || ""
|
||||||
|
let contentDisposition = headers.get("content-disposition") || ""
|
||||||
|
|
||||||
|
// the API does not follow the requirements of https://www.ietf.org/rfc/rfc2183.txt
|
||||||
|
// all content-disposition headers should be format disposition-type; parameters
|
||||||
|
// but some APIs do not provide a type, causing the parse below to fail - add one to fix this
|
||||||
|
if (contentDisposition) {
|
||||||
|
const quotesRegex = /"(?:[^"\\]|\\.)*"|;/g
|
||||||
|
let match: RegExpMatchArray | null = null,
|
||||||
|
found = false
|
||||||
|
while ((match = quotesRegex.exec(contentDisposition)) !== null) {
|
||||||
|
if (match[0] === ";") {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
return {
|
||||||
|
contentDisposition: `attachment; ${contentDisposition}`,
|
||||||
|
contentType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { contentDisposition, contentType }
|
||||||
|
}
|
|
@ -79,9 +79,7 @@ export async function search(
|
||||||
}
|
}
|
||||||
|
|
||||||
const table = await sdk.tables.getTable(options.tableId)
|
const table = await sdk.tables.getTable(options.tableId)
|
||||||
options = searchInputMapping(table, options, {
|
options = searchInputMapping(table, options)
|
||||||
isSql: !!table.sql || !!env.SQS_SEARCH_ENABLE,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (isExternalTable) {
|
if (isExternalTable) {
|
||||||
return external.search(options, table)
|
return external.search(options, table)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
RowSearchParams,
|
RowSearchParams,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { db as dbCore, context } from "@budibase/backend-core"
|
import { db as dbCore, context } from "@budibase/backend-core"
|
||||||
import { helpers, utils } from "@budibase/shared-core"
|
import { utils } from "@budibase/shared-core"
|
||||||
|
|
||||||
export async function paginatedSearch(
|
export async function paginatedSearch(
|
||||||
query: SearchFilters,
|
query: SearchFilters,
|
||||||
|
@ -49,12 +49,7 @@ function findColumnInQueries(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function userColumnMapping(
|
function userColumnMapping(column: string, options: RowSearchParams) {
|
||||||
column: string,
|
|
||||||
options: RowSearchParams,
|
|
||||||
isDeprecatedSingleUserColumn: boolean = false,
|
|
||||||
isSql: boolean = false
|
|
||||||
) {
|
|
||||||
findColumnInQueries(column, options, (filterValue: any): any => {
|
findColumnInQueries(column, options, (filterValue: any): any => {
|
||||||
const isArray = Array.isArray(filterValue),
|
const isArray = Array.isArray(filterValue),
|
||||||
isString = typeof filterValue === "string"
|
isString = typeof filterValue === "string"
|
||||||
|
@ -71,33 +66,23 @@ function userColumnMapping(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let wrapper = (s: string) => s
|
|
||||||
if (isDeprecatedSingleUserColumn && filterValue && isSql) {
|
|
||||||
// Decreated single users are stored as stringified arrays of a single value
|
|
||||||
wrapper = (s: string) => JSON.stringify([s])
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
return filterValue.map(el => {
|
return filterValue.map(el => {
|
||||||
if (typeof el === "string") {
|
if (typeof el === "string") {
|
||||||
return wrapper(processString(el))
|
return processString(el)
|
||||||
} else {
|
} else {
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return wrapper(processString(filterValue))
|
return processString(filterValue)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// maps through the search parameters to check if any of the inputs are invalid
|
// maps through the search parameters to check if any of the inputs are invalid
|
||||||
// based on the table schema, converts them to something that is valid.
|
// based on the table schema, converts them to something that is valid.
|
||||||
export function searchInputMapping(
|
export function searchInputMapping(table: Table, options: RowSearchParams) {
|
||||||
table: Table,
|
|
||||||
options: RowSearchParams,
|
|
||||||
datasourceOptions: { isSql?: boolean } = {}
|
|
||||||
) {
|
|
||||||
if (!table?.schema) {
|
if (!table?.schema) {
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
@ -116,12 +101,7 @@ export function searchInputMapping(
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case FieldType.BB_REFERENCE: {
|
case FieldType.BB_REFERENCE: {
|
||||||
userColumnMapping(
|
userColumnMapping(key, options)
|
||||||
key,
|
|
||||||
options,
|
|
||||||
helpers.schema.isDeprecatedSingleUserColumn(column),
|
|
||||||
datasourceOptions.isSql
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { ObjectStoreBuckets } from "../../constants"
|
import { ObjectStoreBuckets } from "../../constants"
|
||||||
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
import { context, db as dbCore, objectStore } from "@budibase/backend-core"
|
||||||
import { FieldType, RenameColumn, Row, Table } from "@budibase/types"
|
import {
|
||||||
|
FieldType,
|
||||||
|
RenameColumn,
|
||||||
|
Row,
|
||||||
|
RowAttachment,
|
||||||
|
Table,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
export class AttachmentCleanup {
|
export class AttachmentCleanup {
|
||||||
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
static async coreCleanup(fileListFn: () => string[]): Promise<void> {
|
||||||
|
@ -21,7 +27,7 @@ export class AttachmentCleanup {
|
||||||
|
|
||||||
private static extractAttachmentKeys(
|
private static extractAttachmentKeys(
|
||||||
type: FieldType,
|
type: FieldType,
|
||||||
rowData: any
|
rowData: RowAttachment[] | RowAttachment
|
||||||
): string[] {
|
): string[] {
|
||||||
if (
|
if (
|
||||||
type !== FieldType.ATTACHMENTS &&
|
type !== FieldType.ATTACHMENTS &&
|
||||||
|
@ -34,10 +40,15 @@ export class AttachmentCleanup {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === FieldType.ATTACHMENTS) {
|
if (type === FieldType.ATTACHMENTS && Array.isArray(rowData)) {
|
||||||
return rowData.map((attachment: any) => attachment.key)
|
return rowData
|
||||||
|
.filter(attachment => attachment.key)
|
||||||
|
.map(attachment => attachment.key)
|
||||||
|
} else if ("key" in rowData) {
|
||||||
|
return [rowData.key]
|
||||||
}
|
}
|
||||||
return [rowData.key]
|
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async tableChange(
|
private static async tableChange(
|
||||||
|
|
|
@ -14,7 +14,13 @@ export async function processInputBBReference(
|
||||||
subtype: BBReferenceFieldSubType.USER
|
subtype: BBReferenceFieldSubType.USER
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
if (value && Array.isArray(value)) {
|
if (value && Array.isArray(value)) {
|
||||||
throw "BB_REFERENCE_SINGLE cannot be an array"
|
if (value.length > 1) {
|
||||||
|
throw new InvalidBBRefError(
|
||||||
|
JSON.stringify(value),
|
||||||
|
BBReferenceFieldSubType.USER
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value = value[0]
|
||||||
}
|
}
|
||||||
let id = typeof value === "string" ? value : value?._id
|
let id = typeof value === "string" ? value : value?._id
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import * as linkRows from "../../db/linkedRows"
|
import * as linkRows from "../../db/linkedRows"
|
||||||
import { processFormulas, fixAutoColumnSubType } from "./utils"
|
import { fixAutoColumnSubType, processFormulas } from "./utils"
|
||||||
import { objectStore, utils } from "@budibase/backend-core"
|
import { objectStore, utils } from "@budibase/backend-core"
|
||||||
import { InternalTables } from "../../db/utils"
|
import { InternalTables } from "../../db/utils"
|
||||||
import { TYPE_TRANSFORM_MAP } from "./map"
|
import { TYPE_TRANSFORM_MAP } from "./map"
|
||||||
import {
|
import {
|
||||||
FieldType,
|
|
||||||
AutoFieldSubType,
|
AutoFieldSubType,
|
||||||
|
FieldType,
|
||||||
Row,
|
Row,
|
||||||
RowAttachment,
|
RowAttachment,
|
||||||
Table,
|
Table,
|
||||||
|
@ -18,6 +18,7 @@ import {
|
||||||
processOutputBBReferences,
|
processOutputBBReferences,
|
||||||
} from "./bbReferenceProcessor"
|
} from "./bbReferenceProcessor"
|
||||||
import { isExternalTableID } from "../../integrations/utils"
|
import { isExternalTableID } from "../../integrations/utils"
|
||||||
|
import { helpers } from "@budibase/shared-core"
|
||||||
|
|
||||||
export * from "./utils"
|
export * from "./utils"
|
||||||
export * from "./attachments"
|
export * from "./attachments"
|
||||||
|
@ -162,10 +163,14 @@ export async function inputProcessing(
|
||||||
if (attachment?.url) {
|
if (attachment?.url) {
|
||||||
delete clonedRow[key].url
|
delete clonedRow[key].url
|
||||||
}
|
}
|
||||||
} else if (field.type === FieldType.BB_REFERENCE && value) {
|
} else if (
|
||||||
clonedRow[key] = await processInputBBReferences(value, field.subtype)
|
value &&
|
||||||
} else if (field.type === FieldType.BB_REFERENCE_SINGLE && value) {
|
(field.type === FieldType.BB_REFERENCE_SINGLE ||
|
||||||
|
helpers.schema.isDeprecatedSingleUserColumn(field))
|
||||||
|
) {
|
||||||
clonedRow[key] = await processInputBBReference(value, field.subtype)
|
clonedRow[key] = await processInputBBReference(value, field.subtype)
|
||||||
|
} else if (value && field.type === FieldType.BB_REFERENCE) {
|
||||||
|
clonedRow[key] = await processInputBBReferences(value, field.subtype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,27 +226,31 @@ export async function outputProcessing<T extends Row[] | Row>(
|
||||||
opts.squash = true
|
opts.squash = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// process complex types: attachements, bb references...
|
// process complex types: attachments, bb references...
|
||||||
for (let [property, column] of Object.entries(table.schema)) {
|
for (let [property, column] of Object.entries(table.schema)) {
|
||||||
if (column.type === FieldType.ATTACHMENTS) {
|
if (
|
||||||
|
column.type === FieldType.ATTACHMENTS ||
|
||||||
|
column.type === FieldType.ATTACHMENT_SINGLE
|
||||||
|
) {
|
||||||
for (let row of enriched) {
|
for (let row of enriched) {
|
||||||
if (row[property] == null || !Array.isArray(row[property])) {
|
if (row[property] == null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
row[property].forEach((attachment: RowAttachment) => {
|
const process = (attachment: RowAttachment) => {
|
||||||
if (!attachment.url) {
|
if (!attachment.url && attachment.key) {
|
||||||
attachment.url = objectStore.getAppFileUrl(attachment.key)
|
attachment.url = objectStore.getAppFileUrl(attachment.key)
|
||||||
}
|
}
|
||||||
})
|
return attachment
|
||||||
}
|
|
||||||
} else if (column.type === FieldType.ATTACHMENT_SINGLE) {
|
|
||||||
for (let row of enriched) {
|
|
||||||
if (!row[property] || Object.keys(row[property]).length === 0) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
if (typeof row[property] === "string" && row[property].length) {
|
||||||
if (!row[property].url) {
|
row[property] = JSON.parse(row[property])
|
||||||
row[property].url = objectStore.getAppFileUrl(row[property].key)
|
}
|
||||||
|
if (Array.isArray(row[property])) {
|
||||||
|
row[property].forEach((attachment: RowAttachment) => {
|
||||||
|
process(attachment)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
process(row[property])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
|
|
@ -102,7 +102,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
name: "user",
|
name: "user",
|
||||||
constraints: {
|
constraints: {
|
||||||
presence: true,
|
presence: true,
|
||||||
type: "string",
|
type: "array",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -154,7 +154,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
name: "user",
|
name: "user",
|
||||||
constraints: {
|
constraints: {
|
||||||
presence: false,
|
presence: false,
|
||||||
type: "string",
|
type: "array",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -196,7 +196,7 @@ describe("rowProcessor - inputProcessing", () => {
|
||||||
name: "user",
|
name: "user",
|
||||||
constraints: {
|
constraints: {
|
||||||
presence: false,
|
presence: false,
|
||||||
type: "string",
|
type: "array",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,10 @@ import {
|
||||||
|
|
||||||
export function isDeprecatedSingleUserColumn(
|
export function isDeprecatedSingleUserColumn(
|
||||||
schema: Pick<FieldSchema, "type" | "subtype" | "constraints">
|
schema: Pick<FieldSchema, "type" | "subtype" | "constraints">
|
||||||
) {
|
): schema is {
|
||||||
|
type: FieldType.BB_REFERENCE
|
||||||
|
subtype: BBReferenceFieldSubType.USER
|
||||||
|
} {
|
||||||
const result =
|
const result =
|
||||||
schema.type === FieldType.BB_REFERENCE &&
|
schema.type === FieldType.BB_REFERENCE &&
|
||||||
schema.subtype === BBReferenceFieldSubType.USER &&
|
schema.subtype === BBReferenceFieldSubType.USER &&
|
||||||
|
|
|
@ -459,10 +459,11 @@ describe("scim", () => {
|
||||||
it("should return 404 when requesting unexisting user id", async () => {
|
it("should return 404 when requesting unexisting user id", async () => {
|
||||||
const response = await findScimUser(structures.uuid(), { expect: 404 })
|
const response = await findScimUser(structures.uuid(), { expect: 404 })
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(response).toEqual(
|
||||||
message: "missing",
|
expect.objectContaining({
|
||||||
status: 404,
|
status: 404,
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -861,10 +862,11 @@ describe("scim", () => {
|
||||||
it("should return 404 when requesting unexisting group id", async () => {
|
it("should return 404 when requesting unexisting group id", async () => {
|
||||||
const response = await findScimGroup(structures.uuid(), { expect: 404 })
|
const response = await findScimGroup(structures.uuid(), { expect: 404 })
|
||||||
|
|
||||||
expect(response).toEqual({
|
expect(response).toEqual(
|
||||||
message: "missing",
|
expect.objectContaining({
|
||||||
status: 404,
|
status: 404,
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should allow excluding members", async () => {
|
it("should allow excluding members", async () => {
|
||||||
|
|
196
yarn.lock
196
yarn.lock
|
@ -5138,16 +5138,16 @@
|
||||||
integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==
|
integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==
|
||||||
|
|
||||||
"@types/chai-subset@^1.3.3":
|
"@types/chai-subset@^1.3.3":
|
||||||
version "1.3.3"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94"
|
resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.5.tgz#3fc044451f26985f45625230a7f22284808b0a9a"
|
||||||
integrity sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==
|
integrity sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/chai" "*"
|
"@types/chai" "*"
|
||||||
|
|
||||||
"@types/chai@*", "@types/chai@^4.3.4":
|
"@types/chai@*", "@types/chai@^4.3.4":
|
||||||
version "4.3.9"
|
version "4.3.16"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.9.tgz#144d762491967db8c6dea38e03d2206c2623feec"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82"
|
||||||
integrity sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==
|
integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==
|
||||||
|
|
||||||
"@types/chance@1.1.3":
|
"@types/chance@1.1.3":
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
|
@ -5272,11 +5272,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/global-agent/-/global-agent-2.1.1.tgz#3f93185e48a3a36e377a52a8301320cd162a831b"
|
resolved "https://registry.yarnpkg.com/@types/global-agent/-/global-agent-2.1.1.tgz#3f93185e48a3a36e377a52a8301320cd162a831b"
|
||||||
integrity sha512-sVox8Phk1UKgP6LQPAdeRxfww6vHKt7Bf59dXzYLsQBUEMEn8S10a+ESp/yO0i4fJ3WS4+CIuz42hgJcuA+3mA==
|
integrity sha512-sVox8Phk1UKgP6LQPAdeRxfww6vHKt7Bf59dXzYLsQBUEMEn8S10a+ESp/yO0i4fJ3WS4+CIuz42hgJcuA+3mA==
|
||||||
|
|
||||||
"@types/google-spreadsheet@3.1.5":
|
|
||||||
version "3.1.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/google-spreadsheet/-/google-spreadsheet-3.1.5.tgz#2bdc6f9f5372551e0506cb6ef3f562adcf44fc2e"
|
|
||||||
integrity sha512-7N+mDtZ1pmya2RRFPPl4KYc2TRgiqCNBLUZfyrKfER+u751JgCO+C24/LzF70UmUm/zhHUbzRZ5mtfaxekQ1ZQ==
|
|
||||||
|
|
||||||
"@types/graceful-fs@^4.1.3":
|
"@types/graceful-fs@^4.1.3":
|
||||||
version "4.1.6"
|
version "4.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae"
|
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae"
|
||||||
|
@ -6418,11 +6413,16 @@ acorn-walk@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
||||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||||
|
|
||||||
acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0:
|
acorn-walk@^8.0.2, acorn-walk@^8.1.1:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||||
|
|
||||||
|
acorn-walk@^8.2.0:
|
||||||
|
version "8.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa"
|
||||||
|
integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==
|
||||||
|
|
||||||
acorn@^5.2.1, acorn@^5.7.3:
|
acorn@^5.2.1, acorn@^5.7.3:
|
||||||
version "5.7.4"
|
version "5.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
|
||||||
|
@ -6433,11 +6433,16 @@ acorn@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
acorn@^8.1.0, acorn@^8.10.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0:
|
acorn@^8.1.0, acorn@^8.10.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.8.2, acorn@^8.9.0:
|
||||||
version "8.11.2"
|
version "8.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
|
||||||
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
|
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
|
||||||
|
|
||||||
|
acorn@^8.11.3, acorn@^8.8.1:
|
||||||
|
version "8.11.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a"
|
||||||
|
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
|
||||||
|
|
||||||
add-stream@^1.0.0:
|
add-stream@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
|
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
|
||||||
|
@ -6983,7 +6988,7 @@ axios-retry@^3.1.9:
|
||||||
"@babel/runtime" "^7.15.4"
|
"@babel/runtime" "^7.15.4"
|
||||||
is-retry-allowed "^2.2.0"
|
is-retry-allowed "^2.2.0"
|
||||||
|
|
||||||
axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.21.4, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.5.0:
|
axios@0.24.0, axios@1.1.3, axios@1.6.3, axios@^0.21.1, axios@^0.26.0, axios@^1.0.0, axios@^1.1.3, axios@^1.4.0, axios@^1.5.0:
|
||||||
version "1.6.3"
|
version "1.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4"
|
||||||
integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==
|
integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==
|
||||||
|
@ -7817,9 +7822,9 @@ catharsis@^0.9.0:
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
|
|
||||||
chai@^4.3.7:
|
chai@^4.3.7:
|
||||||
version "4.3.10"
|
version "4.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384"
|
resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1"
|
||||||
integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==
|
integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==
|
||||||
dependencies:
|
dependencies:
|
||||||
assertion-error "^1.1.0"
|
assertion-error "^1.1.0"
|
||||||
check-error "^1.0.3"
|
check-error "^1.0.3"
|
||||||
|
@ -8332,6 +8337,11 @@ condense-newlines@^0.2.1:
|
||||||
is-whitespace "^0.3.0"
|
is-whitespace "^0.3.0"
|
||||||
kind-of "^3.0.2"
|
kind-of "^3.0.2"
|
||||||
|
|
||||||
|
confbox@^0.1.7:
|
||||||
|
version "0.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579"
|
||||||
|
integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==
|
||||||
|
|
||||||
config-chain@^1.1.13:
|
config-chain@^1.1.13:
|
||||||
version "1.1.13"
|
version "1.1.13"
|
||||||
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
|
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
|
||||||
|
@ -9434,9 +9444,9 @@ diff@^4.0.1:
|
||||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||||
|
|
||||||
diff@^5.1.0:
|
diff@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
|
||||||
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
|
integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
|
||||||
|
|
||||||
diffie-hellman@^5.0.0:
|
diffie-hellman@^5.0.0:
|
||||||
version "5.0.3"
|
version "5.0.3"
|
||||||
|
@ -11071,17 +11081,6 @@ gauge@^4.0.3:
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
wide-align "^1.1.5"
|
wide-align "^1.1.5"
|
||||||
|
|
||||||
gaxios@^4.0.0:
|
|
||||||
version "4.3.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22"
|
|
||||||
integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==
|
|
||||||
dependencies:
|
|
||||||
abort-controller "^3.0.0"
|
|
||||||
extend "^3.0.2"
|
|
||||||
https-proxy-agent "^5.0.0"
|
|
||||||
is-stream "^2.0.0"
|
|
||||||
node-fetch "^2.6.7"
|
|
||||||
|
|
||||||
gaxios@^5.0.0, gaxios@^5.0.1:
|
gaxios@^5.0.0, gaxios@^5.0.1:
|
||||||
version "5.1.3"
|
version "5.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.3.tgz#f7fa92da0fe197c846441e5ead2573d4979e9013"
|
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.1.3.tgz#f7fa92da0fe197c846441e5ead2573d4979e9013"
|
||||||
|
@ -11092,14 +11091,6 @@ gaxios@^5.0.0, gaxios@^5.0.1:
|
||||||
is-stream "^2.0.0"
|
is-stream "^2.0.0"
|
||||||
node-fetch "^2.6.9"
|
node-fetch "^2.6.9"
|
||||||
|
|
||||||
gcp-metadata@^4.2.0:
|
|
||||||
version "4.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.3.1.tgz#fb205fe6a90fef2fd9c85e6ba06e5559ee1eefa9"
|
|
||||||
integrity sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==
|
|
||||||
dependencies:
|
|
||||||
gaxios "^4.0.0"
|
|
||||||
json-bigint "^1.0.0"
|
|
||||||
|
|
||||||
gcp-metadata@^5.3.0:
|
gcp-metadata@^5.3.0:
|
||||||
version "5.3.0"
|
version "5.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.3.0.tgz#6f45eb473d0cb47d15001476b48b663744d25408"
|
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.3.0.tgz#6f45eb473d0cb47d15001476b48b663744d25408"
|
||||||
|
@ -11506,36 +11497,6 @@ gonzales-pe@^4.2.3, gonzales-pe@^4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
google-auth-library@7.12.0:
|
|
||||||
version "7.12.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.12.0.tgz#7965db6bc20cb31f2df05a08a296bbed6af69426"
|
|
||||||
integrity sha512-RS/whvFPMoF1hQNxnoVET3DWKPBt1Xgqe2rY0k+Jn7TNhoHlwdnSe7Rlcbo2Nub3Mt2lUVz26X65aDQrWp6x8w==
|
|
||||||
dependencies:
|
|
||||||
arrify "^2.0.0"
|
|
||||||
base64-js "^1.3.0"
|
|
||||||
ecdsa-sig-formatter "^1.0.11"
|
|
||||||
fast-text-encoding "^1.0.0"
|
|
||||||
gaxios "^4.0.0"
|
|
||||||
gcp-metadata "^4.2.0"
|
|
||||||
gtoken "^5.0.4"
|
|
||||||
jws "^4.0.0"
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
google-auth-library@^6.1.3:
|
|
||||||
version "6.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572"
|
|
||||||
integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ==
|
|
||||||
dependencies:
|
|
||||||
arrify "^2.0.0"
|
|
||||||
base64-js "^1.3.0"
|
|
||||||
ecdsa-sig-formatter "^1.0.11"
|
|
||||||
fast-text-encoding "^1.0.0"
|
|
||||||
gaxios "^4.0.0"
|
|
||||||
gcp-metadata "^4.2.0"
|
|
||||||
gtoken "^5.0.4"
|
|
||||||
jws "^4.0.0"
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
google-auth-library@^8.0.1, google-auth-library@^8.0.2:
|
google-auth-library@^8.0.1, google-auth-library@^8.0.2:
|
||||||
version "8.9.0"
|
version "8.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.9.0.tgz#15a271eb2ec35d43b81deb72211bd61b1ef14dd0"
|
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.9.0.tgz#15a271eb2ec35d43b81deb72211bd61b1ef14dd0"
|
||||||
|
@ -11572,13 +11533,6 @@ google-gax@^3.5.7:
|
||||||
protobufjs-cli "1.1.1"
|
protobufjs-cli "1.1.1"
|
||||||
retry-request "^5.0.0"
|
retry-request "^5.0.0"
|
||||||
|
|
||||||
google-p12-pem@^3.1.3:
|
|
||||||
version "3.1.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b"
|
|
||||||
integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==
|
|
||||||
dependencies:
|
|
||||||
node-forge "^1.3.1"
|
|
||||||
|
|
||||||
google-p12-pem@^4.0.0:
|
google-p12-pem@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-4.0.1.tgz#82841798253c65b7dc2a4e5fe9df141db670172a"
|
resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-4.0.1.tgz#82841798253c65b7dc2a4e5fe9df141db670172a"
|
||||||
|
@ -11586,13 +11540,12 @@ google-p12-pem@^4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
node-forge "^1.3.1"
|
node-forge "^1.3.1"
|
||||||
|
|
||||||
google-spreadsheet@3.2.0:
|
google-spreadsheet@4.1.2:
|
||||||
version "3.2.0"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-3.2.0.tgz#ce8aa75c15705aa950ad52b091a6fc4d33dcb329"
|
resolved "https://registry.yarnpkg.com/google-spreadsheet/-/google-spreadsheet-4.1.2.tgz#92e30fdba7e0d78c55d50731528df7835d58bfee"
|
||||||
integrity sha512-z7XMaqb+26rdo8p51r5O03u8aPLAPzn5YhOXYJPcf2hdMVr0dUbIARgdkRdmGiBeoV/QoU/7VNhq1MMCLZv3kQ==
|
integrity sha512-HFBweDAkOcyC2qO9kmWESKbNuOcn+R7UzZN/tj5LLNxVv8FHmg113u0Ow+yaKwwIOt/NnDtPLuptAhaxTs0FYw==
|
||||||
dependencies:
|
dependencies:
|
||||||
axios "^0.21.4"
|
axios "^1.4.0"
|
||||||
google-auth-library "^6.1.3"
|
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
gopd@^1.0.1:
|
gopd@^1.0.1:
|
||||||
|
@ -11678,15 +11631,6 @@ graphemer@^1.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
|
||||||
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
|
||||||
|
|
||||||
gtoken@^5.0.4:
|
|
||||||
version "5.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f"
|
|
||||||
integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==
|
|
||||||
dependencies:
|
|
||||||
gaxios "^4.0.0"
|
|
||||||
google-p12-pem "^3.1.3"
|
|
||||||
jws "^4.0.0"
|
|
||||||
|
|
||||||
gtoken@^6.1.0:
|
gtoken@^6.1.0:
|
||||||
version "6.1.2"
|
version "6.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-6.1.2.tgz#aeb7bdb019ff4c3ba3ac100bbe7b6e74dce0e8bc"
|
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-6.1.2.tgz#aeb7bdb019ff4c3ba3ac100bbe7b6e74dce0e8bc"
|
||||||
|
@ -15341,15 +15285,15 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||||
|
|
||||||
mlly@^1.1.0, mlly@^1.2.0:
|
mlly@^1.1.0, mlly@^1.7.0:
|
||||||
version "1.4.2"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.4.2.tgz#7cf406aa319ff6563d25da6b36610a93f2a8007e"
|
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.0.tgz#587383ae40dda23cadb11c3c3cc972b277724271"
|
||||||
integrity sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==
|
integrity sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^8.10.0"
|
acorn "^8.11.3"
|
||||||
pathe "^1.1.1"
|
pathe "^1.1.2"
|
||||||
pkg-types "^1.0.3"
|
pkg-types "^1.1.0"
|
||||||
ufo "^1.3.0"
|
ufo "^1.5.3"
|
||||||
|
|
||||||
modify-values@^1.0.1:
|
modify-values@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
@ -16850,11 +16794,16 @@ path-type@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||||
|
|
||||||
pathe@^1.1.0, pathe@^1.1.1:
|
pathe@^1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
|
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
|
||||||
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
|
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
|
||||||
|
|
||||||
|
pathe@^1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
||||||
|
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
|
||||||
|
|
||||||
pathval@^1.1.1:
|
pathval@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
|
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
|
||||||
|
@ -17122,14 +17071,14 @@ pkg-dir@^4.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
find-up "^4.0.0"
|
find-up "^4.0.0"
|
||||||
|
|
||||||
pkg-types@^1.0.3:
|
pkg-types@^1.1.0:
|
||||||
version "1.0.3"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
|
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.1.1.tgz#07b626880749beb607b0c817af63aac1845a73f2"
|
||||||
integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==
|
integrity sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
jsonc-parser "^3.2.0"
|
confbox "^0.1.7"
|
||||||
mlly "^1.2.0"
|
mlly "^1.7.0"
|
||||||
pathe "^1.1.0"
|
pathe "^1.1.2"
|
||||||
|
|
||||||
pkginfo@0.4.x:
|
pkginfo@0.4.x:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
|
@ -19693,9 +19642,9 @@ statuses@2.0.1, statuses@^2.0.0:
|
||||||
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
|
integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
|
||||||
|
|
||||||
std-env@^3.3.1:
|
std-env@^3.3.1:
|
||||||
version "3.4.3"
|
version "3.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.4.3.tgz#326f11db518db751c83fd58574f449b7c3060910"
|
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
|
||||||
integrity sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==
|
integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
|
||||||
|
|
||||||
step@0.0.x:
|
step@0.0.x:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
|
@ -20539,9 +20488,9 @@ tiny-queue@^0.2.0:
|
||||||
integrity sha512-EijGsv7kzd9I9g0ByCl6h42BWNGUZrlCSejfrb3AKeHC33SGbASu1VDf5O3rRiiUOhAC9CHdZxFPbZu0HmR70A==
|
integrity sha512-EijGsv7kzd9I9g0ByCl6h42BWNGUZrlCSejfrb3AKeHC33SGbASu1VDf5O3rRiiUOhAC9CHdZxFPbZu0HmR70A==
|
||||||
|
|
||||||
tinybench@^2.3.1:
|
tinybench@^2.3.1:
|
||||||
version "2.5.1"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.1.tgz#3408f6552125e53a5a48adee31261686fd71587e"
|
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.8.0.tgz#30e19ae3a27508ee18273ffed9ac7018949acd7b"
|
||||||
integrity sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==
|
integrity sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==
|
||||||
|
|
||||||
tinycolor2@^1.6.0:
|
tinycolor2@^1.6.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
|
@ -20979,10 +20928,10 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||||
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
||||||
|
|
||||||
ufo@^1.3.0:
|
ufo@^1.5.3:
|
||||||
version "1.3.1"
|
version "1.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.1.tgz#e085842f4627c41d4c1b60ebea1f75cdab4ce86b"
|
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344"
|
||||||
integrity sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==
|
integrity sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==
|
||||||
|
|
||||||
uglify-js@^3.1.4, uglify-js@^3.7.7:
|
uglify-js@^3.1.4, uglify-js@^3.7.7:
|
||||||
version "3.17.4"
|
version "3.17.4"
|
||||||
|
@ -21369,7 +21318,18 @@ vite-plugin-static-copy@^0.17.0:
|
||||||
fs-extra "^11.1.0"
|
fs-extra "^11.1.0"
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
|
|
||||||
"vite@^3.0.0 || ^4.0.0", vite@^4.5.0:
|
"vite@^3.0.0 || ^4.0.0":
|
||||||
|
version "4.5.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.3.tgz#d88a4529ea58bae97294c7e2e6f0eab39a50fb1a"
|
||||||
|
integrity sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.18.10"
|
||||||
|
postcss "^8.4.27"
|
||||||
|
rollup "^3.27.1"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
vite@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26"
|
||||||
integrity sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==
|
integrity sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==
|
||||||
|
|
Loading…
Reference in a new issue