From 0fb4613b5adc0d58aba79ebcb4cf7f4ccd9b2d14 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 21 Oct 2021 14:15:55 +0100 Subject: [PATCH 1/3] Handle nulls / empty in views and tables --- packages/bbui/src/Table/CellRenderer.svelte | 20 +++++- packages/bbui/src/Table/StringRenderer.svelte | 9 ++- .../DataTable/modals/FilterModal.svelte | 62 ++++++++++++------- .../api/controllers/row/ExternalRequest.ts | 6 +- .../src/api/controllers/view/viewBuilder.js | 20 ++++++ packages/server/src/integrations/base/sql.ts | 8 ++- packages/server/src/integrations/utils.ts | 5 +- 7 files changed, 103 insertions(+), 27 deletions(-) diff --git a/packages/bbui/src/Table/CellRenderer.svelte b/packages/bbui/src/Table/CellRenderer.svelte index 9a53fd0169..844501947f 100644 --- a/packages/bbui/src/Table/CellRenderer.svelte +++ b/packages/bbui/src/Table/CellRenderer.svelte @@ -28,9 +28,27 @@ $: type = schema?.type ?? "string" $: customRenderer = customRenderers?.find(x => x.column === schema?.name) $: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer + + /** + * Don't use falsy here as we want to: + * - include empty arrays + * - exclude 0 and booleans + * + * If updated, the corresponding view expression should be updated in 'server/viewBuilder.js' + */ + const isNotSet = value => { + return ( + value === undefined || + value === null || + value === "" || + (Array.isArray(value) && value.length === 0) + ) + } -{#if renderer && (customRenderer || (value != null && value !== ""))} +{#if !customRenderer && isNotSet(value)} + +{:else if renderer} diff --git a/packages/bbui/src/Table/StringRenderer.svelte b/packages/bbui/src/Table/StringRenderer.svelte index 6fd731f5ec..7219a1a8f1 100644 --- a/packages/bbui/src/Table/StringRenderer.svelte +++ b/packages/bbui/src/Table/StringRenderer.svelte @@ -1,8 +1,11 @@ -
{typeof value === "object" ? JSON.stringify(value) : value}
+
+ {typeof value === "object" ? JSON.stringify(value) : value} +
diff --git a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte index 9c6f4956b0..1b2e92ced2 100644 --- a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte @@ -42,6 +42,14 @@ name: "Contains", key: "CONTAINS", }, + { + name: "Is Set", + key: "SET", + }, + { + name: "Is Not Set", + key: "NOT_SET", + }, ] const CONJUNCTIONS = [ @@ -116,6 +124,10 @@ const getOptionLabel = x => x.name const getOptionValue = x => x.key + + const showValue = filter => { + return !(filter.condition === "SET" || filter.condition === "NOT_SET") + } @@ -144,30 +156,36 @@ {getOptionLabel} {getOptionValue} /> - {#if filter.key && isMultipleChoice(filter.key)} - + {#if showValue(filter)} + {#if filter.key && isMultipleChoice(filter.key)} + + {:else} + + {/if} + removeFilter(idx)} /> {:else} - + removeFilter(idx)} /> + +
{/if} - removeFilter(idx)} /> {/each}
{:else} diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 1129a9eda2..c58364af07 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -37,7 +37,11 @@ interface RunConfig { module External { const { makeExternalQuery } = require("./utils") - const { DataSourceOperation, FieldTypes, RelationshipTypes } = require("../../../constants") + const { + DataSourceOperation, + FieldTypes, + RelationshipTypes, + } = require("../../../constants") const { breakExternalTableId, isSQL } = require("../../../integrations/utils") const { processObjectSync } = require("@budibase/string-templates") const { cloneDeep } = require("lodash/fp") diff --git a/packages/server/src/api/controllers/view/viewBuilder.js b/packages/server/src/api/controllers/view/viewBuilder.js index 068c6ab7b1..9cbd7b83d4 100644 --- a/packages/server/src/api/controllers/view/viewBuilder.js +++ b/packages/server/src/api/controllers/view/viewBuilder.js @@ -10,6 +10,22 @@ const TOKEN_MAP = { OR: "||", } +/** + * Don't use falsy here as we want to: + * - include empty arrays + * - exclude 0 and booleans + * + * If updated, the corresponding rendering condition should be updated in 'bbui/CellRenderer.svelte' + */ +const isNotSetExpression = key => { + return `( + doc["${key}"] === undefined || + doc["${key}"] === null || + doc["${key}"] === "" || + (Array.isArray(doc["${key}"]) && doc["${key}"].length === 0) + )` +} + const GROUP_PROPERTY = { group: { type: "string", @@ -72,6 +88,10 @@ function parseFilterExpression(filters) { expression.push( `doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")` ) + } else if (filter.condition === "NOT_SET") { + expression.push(isNotSetExpression(filter.key)) + } else if (filter.condition === "SET") { + expression.push(`!${isNotSetExpression(filter.key)}`) } else { const value = typeof filter.value == "string" ? `"${filter.value}"` : filter.value diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 5a67f05830..316e20e352 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -201,7 +201,13 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery { [tableName]: query, }).select(selectStatement) // handle joins - return addRelationships(knex, preQuery, selectStatement, tableName, relationships) + return addRelationships( + knex, + preQuery, + selectStatement, + tableName, + relationships + ) } function buildUpdate( diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index 45addef839..07e4e950f8 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -34,7 +34,10 @@ export function generateRowIdField(keyProps: any[] = []) { } export function isRowId(field: any) { - return Array.isArray(field) || (typeof field === "string" && field.match(ROW_ID_REGEX) != null) + return ( + Array.isArray(field) || + (typeof field === "string" && field.match(ROW_ID_REGEX) != null) + ) } export function convertRowId(field: any) { From 99b42c7083105889f1e847ff5fc099e166760aea Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 25 Oct 2021 09:58:05 +0100 Subject: [PATCH 2/3] Feedback: Rename set/not set to not empty/empty, remove cell rendering --- packages/bbui/src/Table/CellRenderer.svelte | 20 +------------------ packages/bbui/src/Table/StringRenderer.svelte | 9 +-------- .../DataTable/modals/FilterModal.svelte | 10 +++++----- .../src/api/controllers/view/viewBuilder.js | 17 +++++----------- 4 files changed, 12 insertions(+), 44 deletions(-) diff --git a/packages/bbui/src/Table/CellRenderer.svelte b/packages/bbui/src/Table/CellRenderer.svelte index 844501947f..9a53fd0169 100644 --- a/packages/bbui/src/Table/CellRenderer.svelte +++ b/packages/bbui/src/Table/CellRenderer.svelte @@ -28,27 +28,9 @@ $: type = schema?.type ?? "string" $: customRenderer = customRenderers?.find(x => x.column === schema?.name) $: renderer = customRenderer?.component ?? typeMap[type] ?? StringRenderer - - /** - * Don't use falsy here as we want to: - * - include empty arrays - * - exclude 0 and booleans - * - * If updated, the corresponding view expression should be updated in 'server/viewBuilder.js' - */ - const isNotSet = value => { - return ( - value === undefined || - value === null || - value === "" || - (Array.isArray(value) && value.length === 0) - ) - } -{#if !customRenderer && isNotSet(value)} - -{:else if renderer} +{#if renderer && (customRenderer || (value != null && value !== ""))} diff --git a/packages/bbui/src/Table/StringRenderer.svelte b/packages/bbui/src/Table/StringRenderer.svelte index 7219a1a8f1..2756839616 100644 --- a/packages/bbui/src/Table/StringRenderer.svelte +++ b/packages/bbui/src/Table/StringRenderer.svelte @@ -1,11 +1,8 @@ -
- {typeof value === "object" ? JSON.stringify(value) : value} -
+
{value}
diff --git a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte index 2b76fd2209..6359c781cc 100644 --- a/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/FilterModal.svelte @@ -43,12 +43,12 @@ key: "CONTAINS", }, { - name: "Is Set", - key: "SET", + name: "Is Not Empty", + key: "NOT_EMPTY", }, { - name: "Is Not Set", - key: "NOT_SET", + name: "Is Empty", + key: "EMPTY", }, ] @@ -122,7 +122,7 @@ const getOptionValue = x => x.key const showValue = filter => { - return !(filter.condition === "SET" || filter.condition === "NOT_SET") + return !(filter.condition === "EMPTY" || filter.condition === "NOT_EMPTY") } diff --git a/packages/server/src/api/controllers/view/viewBuilder.js b/packages/server/src/api/controllers/view/viewBuilder.js index 9cbd7b83d4..b0f39b2f1f 100644 --- a/packages/server/src/api/controllers/view/viewBuilder.js +++ b/packages/server/src/api/controllers/view/viewBuilder.js @@ -10,14 +10,7 @@ const TOKEN_MAP = { OR: "||", } -/** - * Don't use falsy here as we want to: - * - include empty arrays - * - exclude 0 and booleans - * - * If updated, the corresponding rendering condition should be updated in 'bbui/CellRenderer.svelte' - */ -const isNotSetExpression = key => { +const isEmptyExpression = key => { return `( doc["${key}"] === undefined || doc["${key}"] === null || @@ -88,10 +81,10 @@ function parseFilterExpression(filters) { expression.push( `doc["${filter.key}"].${TOKEN_MAP[filter.condition]}("${filter.value}")` ) - } else if (filter.condition === "NOT_SET") { - expression.push(isNotSetExpression(filter.key)) - } else if (filter.condition === "SET") { - expression.push(`!${isNotSetExpression(filter.key)}`) + } else if (filter.condition === "EMPTY") { + expression.push(isEmptyExpression(filter.key)) + } else if (filter.condition === "NOT_EMPTY") { + expression.push(`!${isEmptyExpression(filter.key)}`) } else { const value = typeof filter.value == "string" ? `"${filter.value}"` : filter.value From 2b283d8bab5c5d2a1caeadb4a62972317506c7d6 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 25 Oct 2021 10:02:43 +0100 Subject: [PATCH 3/3] Revert bad revert --- packages/bbui/src/Table/StringRenderer.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bbui/src/Table/StringRenderer.svelte b/packages/bbui/src/Table/StringRenderer.svelte index 2756839616..6fd731f5ec 100644 --- a/packages/bbui/src/Table/StringRenderer.svelte +++ b/packages/bbui/src/Table/StringRenderer.svelte @@ -2,7 +2,7 @@ export let value -
{value}
+
{typeof value === "object" ? JSON.stringify(value) : value}