Merge branch 'master' into ts/client-utils
This commit is contained in:
commit
29ea6ebdf4
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||||
"version": "3.2.39",
|
"version": "3.2.40",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"concurrency": 20,
|
"concurrency": 20,
|
||||||
"command": {
|
"command": {
|
||||||
|
|
|
@ -5139,7 +5139,8 @@
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"label": "File name",
|
"label": "File name",
|
||||||
"key": "key"
|
"key": "key",
|
||||||
|
"nested": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "event",
|
"type": "event",
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
import { Constants } from "@budibase/frontend-core"
|
import { Constants, APIClient } from "@budibase/frontend-core"
|
||||||
import { FieldTypes } from "../constants"
|
import { FieldTypes } from "../constants"
|
||||||
|
import { Row, Table } from "@budibase/types"
|
||||||
|
|
||||||
export const patchAPI = API => {
|
export const patchAPI = (API: APIClient) => {
|
||||||
/**
|
/**
|
||||||
* Enriches rows which contain certain field types so that they can
|
* Enriches rows which contain certain field types so that they can
|
||||||
* be properly displayed.
|
* be properly displayed.
|
||||||
* The ability to create these bindings has been removed, but they will still
|
* The ability to create these bindings has been removed, but they will still
|
||||||
* exist in client apps to support backwards compatibility.
|
* exist in client apps to support backwards compatibility.
|
||||||
*/
|
*/
|
||||||
const enrichRows = async (rows, tableId) => {
|
const enrichRows = async (rows: Row[], tableId: string) => {
|
||||||
if (!Array.isArray(rows)) {
|
if (!Array.isArray(rows)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
if (rows.length) {
|
if (rows.length) {
|
||||||
const tables = {}
|
const tables: Record<string, Table> = {}
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
// Fall back to passed in tableId if row doesn't have it specified
|
// Fall back to passed in tableId if row doesn't have it specified
|
||||||
let rowTableId = row.tableId || tableId
|
let rowTableId = row.tableId || tableId
|
||||||
|
@ -54,7 +55,7 @@ export const patchAPI = API => {
|
||||||
const fetchSelf = API.fetchSelf
|
const fetchSelf = API.fetchSelf
|
||||||
API.fetchSelf = async () => {
|
API.fetchSelf = async () => {
|
||||||
const user = await fetchSelf()
|
const user = await fetchSelf()
|
||||||
if (user && user._id) {
|
if (user && "_id" in user && user._id) {
|
||||||
if (user.roleId === "PUBLIC") {
|
if (user.roleId === "PUBLIC") {
|
||||||
// Don't try to enrich a public user as it will 403
|
// Don't try to enrich a public user as it will 403
|
||||||
return user
|
return user
|
||||||
|
@ -90,13 +91,14 @@ export const patchAPI = API => {
|
||||||
return await enrichRows(rows, tableId)
|
return await enrichRows(rows, tableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wipe any HBS formulae from table definitions, as these interfere with
|
// Wipe any HBS formulas from table definitions, as these interfere with
|
||||||
// handlebars enrichment
|
// handlebars enrichment
|
||||||
const fetchTableDefinition = API.fetchTableDefinition
|
const fetchTableDefinition = API.fetchTableDefinition
|
||||||
API.fetchTableDefinition = async tableId => {
|
API.fetchTableDefinition = async tableId => {
|
||||||
const definition = await fetchTableDefinition(tableId)
|
const definition = await fetchTableDefinition(tableId)
|
||||||
Object.keys(definition?.schema || {}).forEach(field => {
|
Object.keys(definition?.schema || {}).forEach(field => {
|
||||||
if (definition.schema[field]?.type === "formula") {
|
if (definition.schema[field]?.type === "formula") {
|
||||||
|
// @ts-expect-error TODO check what use case removing that would break
|
||||||
delete definition.schema[field].formula
|
delete definition.schema[field].formula
|
||||||
}
|
}
|
||||||
})
|
})
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onDestroy, onMount, setContext } from "svelte"
|
import { getContext, onDestroy, onMount, setContext } from "svelte"
|
||||||
import { builderStore } from "stores/builder.js"
|
import { builderStore } from "stores/builder.js"
|
||||||
import { blockStore } from "stores/blocks.js"
|
import { blockStore } from "stores/blocks"
|
||||||
|
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
const { styleable } = getContext("sdk")
|
const { styleable } = getContext("sdk")
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
import Field from "./Field.svelte"
|
import Field from "./Field.svelte"
|
||||||
import { CoreDropzone, ProgressCircle, Helpers } from "@budibase/bbui"
|
import { CoreDropzone, ProgressCircle, Helpers } from "@budibase/bbui"
|
||||||
import { getContext, onMount, onDestroy } from "svelte"
|
import { getContext, onMount, onDestroy } from "svelte"
|
||||||
|
import { builderStore } from "stores/builder.js"
|
||||||
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
|
|
||||||
export let datasourceId
|
export let datasourceId
|
||||||
export let bucket
|
export let bucket
|
||||||
|
@ -12,6 +14,8 @@
|
||||||
export let validation
|
export let validation
|
||||||
export let onChange
|
export let onChange
|
||||||
|
|
||||||
|
const context = getContext("context")
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
let localFiles = []
|
let localFiles = []
|
||||||
|
@ -42,6 +46,9 @@
|
||||||
// Process the file input and return a serializable structure expected by
|
// Process the file input and return a serializable structure expected by
|
||||||
// the dropzone component to display the file
|
// the dropzone component to display the file
|
||||||
const processFiles = async fileList => {
|
const processFiles = async fileList => {
|
||||||
|
if ($builderStore.inBuilder) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
return await new Promise(resolve => {
|
return await new Promise(resolve => {
|
||||||
if (!fileList?.length) {
|
if (!fileList?.length) {
|
||||||
return []
|
return []
|
||||||
|
@ -78,9 +85,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const upload = async () => {
|
const upload = async () => {
|
||||||
|
const processedFileKey = processStringSync(key, $context)
|
||||||
loading = true
|
loading = true
|
||||||
try {
|
try {
|
||||||
const res = await API.externalUpload(datasourceId, bucket, key, data)
|
const res = await API.externalUpload(
|
||||||
|
datasourceId,
|
||||||
|
bucket,
|
||||||
|
processedFileKey,
|
||||||
|
data
|
||||||
|
)
|
||||||
notificationStore.actions.success("File uploaded successfully")
|
notificationStore.actions.success("File uploaded successfully")
|
||||||
loading = false
|
loading = false
|
||||||
return res
|
return res
|
||||||
|
@ -126,7 +139,7 @@
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
defaultValue={[]}
|
defaultValue={[]}
|
||||||
>
|
>
|
||||||
<div class="content">
|
<div class="content" class:builder={$builderStore.inBuilder}>
|
||||||
{#if fieldState}
|
{#if fieldState}
|
||||||
<CoreDropzone
|
<CoreDropzone
|
||||||
value={localFiles}
|
value={localFiles}
|
||||||
|
@ -149,6 +162,9 @@
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.content.builder :global(.spectrum-Dropzone) {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { makePropSafe as safe } from "@budibase/string-templates"
|
import { makePropSafe as safe } from "@budibase/string-templates"
|
||||||
import { API } from "../api/index.js"
|
import { API } from "../api"
|
||||||
import {
|
import {
|
||||||
BasicOperator,
|
BasicOperator,
|
||||||
LegacyFilter,
|
LegacyFilter,
|
||||||
|
|
|
@ -100,6 +100,7 @@ export const buildAttachmentEndpoints = (
|
||||||
body: data,
|
body: data,
|
||||||
json: false,
|
json: false,
|
||||||
external: true,
|
external: true,
|
||||||
|
parseResponse: response => response as any,
|
||||||
})
|
})
|
||||||
return { publicUrl }
|
return { publicUrl }
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,6 +46,8 @@ import { buildLogsEndpoints } from "./logs"
|
||||||
import { buildMigrationEndpoints } from "./migrations"
|
import { buildMigrationEndpoints } from "./migrations"
|
||||||
import { buildRowActionEndpoints } from "./rowActions"
|
import { buildRowActionEndpoints } from "./rowActions"
|
||||||
|
|
||||||
|
export type { APIClient } from "./types"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Random identifier to uniquely identify a session in a tab. This is
|
* Random identifier to uniquely identify a session in a tab. This is
|
||||||
* used to determine the originator of calls to the API, which is in
|
* used to determine the originator of calls to the API, which is in
|
||||||
|
|
|
@ -13,7 +13,7 @@ export interface SelfEndpoints {
|
||||||
generateAPIKey: () => Promise<string | undefined>
|
generateAPIKey: () => Promise<string | undefined>
|
||||||
fetchDeveloperInfo: () => Promise<FetchAPIKeyResponse>
|
fetchDeveloperInfo: () => Promise<FetchAPIKeyResponse>
|
||||||
fetchBuilderSelf: () => Promise<GetGlobalSelfResponse>
|
fetchBuilderSelf: () => Promise<GetGlobalSelfResponse>
|
||||||
fetchSelf: () => Promise<AppSelfResponse>
|
fetchSelf: () => Promise<AppSelfResponse | null>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const buildSelfEndpoints = (API: BaseAPIClient): SelfEndpoints => ({
|
export const buildSelfEndpoints = (API: BaseAPIClient): SelfEndpoints => ({
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export { createAPIClient } from "./api"
|
export { createAPIClient } from "./api"
|
||||||
|
export type { APIClient } from "./api"
|
||||||
export { fetchData, DataFetchMap } from "./fetch"
|
export { fetchData, DataFetchMap } from "./fetch"
|
||||||
export type { DataFetchType } from "./fetch"
|
export type { DataFetchType } from "./fetch"
|
||||||
export * as Constants from "./constants"
|
export * as Constants from "./constants"
|
||||||
|
|
|
@ -97,7 +97,7 @@ export async function run({
|
||||||
const ctx: any = buildCtx(appId, emitter, {
|
const ctx: any = buildCtx(appId, emitter, {
|
||||||
body: inputs.row,
|
body: inputs.row,
|
||||||
params: {
|
params: {
|
||||||
tableId: inputs.row.tableId,
|
tableId: decodeURIComponent(inputs.row.tableId),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -85,7 +85,7 @@ export async function run({
|
||||||
_rev: inputs.revision,
|
_rev: inputs.revision,
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
tableId: inputs.tableId,
|
tableId: decodeURIComponent(inputs.tableId),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -122,9 +122,10 @@ export async function run({
|
||||||
sortType =
|
sortType =
|
||||||
fieldType === FieldType.NUMBER ? FieldType.NUMBER : FieldType.STRING
|
fieldType === FieldType.NUMBER ? FieldType.NUMBER : FieldType.STRING
|
||||||
}
|
}
|
||||||
|
// when passing the tableId in the Ctx it needs to be decoded
|
||||||
const ctx = buildCtx(appId, null, {
|
const ctx = buildCtx(appId, null, {
|
||||||
params: {
|
params: {
|
||||||
tableId,
|
tableId: decodeURIComponent(tableId),
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
sortType,
|
sortType,
|
||||||
|
|
|
@ -90,6 +90,8 @@ export async function run({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const tableId = inputs.row.tableId
|
const tableId = inputs.row.tableId
|
||||||
|
? decodeURIComponent(inputs.row.tableId)
|
||||||
|
: inputs.row.tableId
|
||||||
|
|
||||||
// Base update
|
// Base update
|
||||||
let rowUpdate: Record<string, any>
|
let rowUpdate: Record<string, any>
|
||||||
|
@ -157,7 +159,7 @@ export async function run({
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
rowId: inputs.rowId,
|
rowId: inputs.rowId,
|
||||||
tableId,
|
tableId: tableId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await rowController.patch(ctx)
|
await rowController.patch(ctx)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { EmptyFilterOption, SortOrder, Table } from "@budibase/types"
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
|
import { createAutomationBuilder } from "./utilities/AutomationTestBuilder"
|
||||||
import * as automation from "../index"
|
import * as automation from "../index"
|
||||||
|
import { basicTable } from "../../tests/utilities/structures"
|
||||||
|
|
||||||
const NAME = "Test"
|
const NAME = "Test"
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ describe("Test a query step automation", () => {
|
||||||
await automation.init()
|
await automation.init()
|
||||||
await config.init()
|
await config.init()
|
||||||
table = await config.createTable()
|
table = await config.createTable()
|
||||||
|
|
||||||
const row = {
|
const row = {
|
||||||
name: NAME,
|
name: NAME,
|
||||||
description: "original description",
|
description: "original description",
|
||||||
|
@ -153,4 +155,32 @@ describe("Test a query step automation", () => {
|
||||||
expect(result.steps[0].outputs.rows).toBeDefined()
|
expect(result.steps[0].outputs.rows).toBeDefined()
|
||||||
expect(result.steps[0].outputs.rows.length).toBe(2)
|
expect(result.steps[0].outputs.rows.length).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("return rows when querying a table with a space in the name", async () => {
|
||||||
|
const tableWithSpaces = await config.createTable({
|
||||||
|
...basicTable(),
|
||||||
|
name: "table with spaces",
|
||||||
|
})
|
||||||
|
await config.createRow({
|
||||||
|
name: NAME,
|
||||||
|
tableId: tableWithSpaces._id,
|
||||||
|
})
|
||||||
|
const result = await createAutomationBuilder({
|
||||||
|
name: "Return All Test",
|
||||||
|
config,
|
||||||
|
})
|
||||||
|
.appAction({ fields: {} })
|
||||||
|
.queryRows(
|
||||||
|
{
|
||||||
|
tableId: tableWithSpaces._id!,
|
||||||
|
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||||
|
filters: {},
|
||||||
|
},
|
||||||
|
{ stepName: "Query table with spaces" }
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
expect(result.steps[0].outputs.success).toBe(true)
|
||||||
|
expect(result.steps[0].outputs.rows).toBeDefined()
|
||||||
|
expect(result.steps[0].outputs.rows.length).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue