Merge branch 'master' into feat/automation-naming-ux-updates

This commit is contained in:
Michael Drury 2023-11-03 10:19:23 +00:00 committed by GitHub
commit 87c0145816
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 141 additions and 103 deletions

View File

@ -17,6 +17,6 @@ jobs:
token: ${{ secrets.GH_ACCESS_TOKEN }} token: ${{ secrets.GH_ACCESS_TOKEN }}
client-payload: |- client-payload: |-
{ {
"PAYLOAD_VERSION": "${{ github.sha }}", "VERSION": "${{ github.sha }}",
"REF_NAME": "${{ github.ref_name}}" "REF_NAME": "${{ github.ref_name}}"
} }

View File

@ -170,9 +170,9 @@ jobs:
with: with:
repository: budibase/budibase-deploys repository: budibase/budibase-deploys
event-type: budicloud-qa-deploy event-type: budicloud-qa-deploy
github_pat: ${{ secrets.GH_ACCESS_TOKEN }} token: ${{ secrets.GH_ACCESS_TOKEN }}
client-payload: |- client-payload: |-
{ {
"PAYLOAD_VERSION": "${{ github.ref_name }}", "VERSION": "${{ github.ref_name }}",
"REF_NAME": "${{ github.ref_name}}" "REF_NAME": "${{ github.ref_name}}"
} }

View File

@ -1,5 +1,5 @@
{ {
"version": "2.12.6", "version": "2.12.7",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,27 +1,10 @@
<script context="module">
// We can create a module level cache for all relationship cells to avoid
// having to fetch the table definition one time for each cell
let primaryDisplayCache = {}
const getPrimaryDisplayForTableId = async (API, tableId) => {
if (primaryDisplayCache[tableId]) {
return primaryDisplayCache[tableId]
}
const definition = await API.fetchTableDefinition(tableId)
const primaryDisplay =
definition?.primaryDisplay || definition?.schema?.[0]?.name
primaryDisplayCache[tableId] = primaryDisplay
return primaryDisplay
}
</script>
<script> <script>
import { getColor } from "../lib/utils" import { getColor } from "../lib/utils"
import { onMount, getContext } from "svelte" import { onMount, getContext } from "svelte"
import { Icon, Input, ProgressCircle, clickOutside } from "@budibase/bbui" import { Icon, Input, ProgressCircle, clickOutside } from "@budibase/bbui"
import { debounce } from "../../../utils/utils" import { debounce } from "../../../utils/utils"
const { API, dispatch } = getContext("grid") const { API, dispatch, cache } = getContext("grid")
export let value export let value
export let api export let api
@ -147,7 +130,9 @@
// Find the primary display for the related table // Find the primary display for the related table
if (!primaryDisplay) { if (!primaryDisplay) {
searching = true searching = true
primaryDisplay = await getPrimaryDisplayForTableId(API, schema.tableId) primaryDisplay = await cache.actions.getPrimaryDisplayForTableId(
schema.tableId
)
} }
// Show initial list of results // Show initial list of results
@ -195,7 +180,7 @@
const toggleRow = async row => { const toggleRow = async row => {
if (value?.some(x => x._id === row._id)) { if (value?.some(x => x._id === row._id)) {
// If the row is already included, remove it and update the candidate // If the row is already included, remove it and update the candidate
// row to be the the same position if possible // row to be the same position if possible
if (oneRowOnly) { if (oneRowOnly) {
await onChange([]) await onChange([])
} else { } else {
@ -260,31 +245,29 @@
class:wrap={editable || contentLines > 1} class:wrap={editable || contentLines > 1}
on:wheel={e => (focused ? e.stopPropagation() : null)} on:wheel={e => (focused ? e.stopPropagation() : null)}
> >
{#if Array.isArray(value) && value.length} {#each value || [] as relationship}
{#each value as relationship} {#if relationship[primaryDisplay] || relationship.primaryDisplay}
{#if relationship[primaryDisplay] || relationship.primaryDisplay} <div class="badge">
<div class="badge"> <span
<span on:click={editable
on:click={editable ? () => showRelationship(relationship._id)
? () => showRelationship(relationship._id) : null}
: null} >
> {readable(
{readable( relationship[primaryDisplay] || relationship.primaryDisplay
relationship[primaryDisplay] || relationship.primaryDisplay )}
)} </span>
</span> {#if editable}
{#if editable} <Icon
<Icon name="Close"
name="Close" size="XS"
size="XS" hoverable
hoverable on:click={() => toggleRow(relationship)}
on:click={() => toggleRow(relationship)} />
/> {/if}
{/if} </div>
</div> {/if}
{/if} {/each}
{/each}
{/if}
{#if editable} {#if editable}
<div class="add" on:click={open}> <div class="add" on:click={open}>
<Icon name="Add" size="S" /> <Icon name="Add" size="S" />
@ -320,7 +303,7 @@
<div class="searching"> <div class="searching">
<ProgressCircle size="S" /> <ProgressCircle size="S" />
</div> </div>
{:else if Array.isArray(searchResults) && searchResults.length} {:else if searchResults?.length}
<div class="results"> <div class="results">
{#each searchResults as row, idx} {#each searchResults as row, idx}
<div <div

View File

@ -0,0 +1,47 @@
export const createActions = context => {
const { API } = context
// Cache for the primary display columns of different tables.
// If we ever need to cache table definitions for other purposes then we can
// expand this to be a more generic cache.
let primaryDisplayCache = {}
const resetPrimaryDisplayCache = () => {
primaryDisplayCache = {}
}
const getPrimaryDisplayForTableId = async tableId => {
// If we've never encountered this tableId before then store a promise that
// resolves to the primary display so that subsequent invocations before the
// promise completes can reuse this promise
if (!primaryDisplayCache[tableId]) {
primaryDisplayCache[tableId] = new Promise(resolve => {
API.fetchTableDefinition(tableId).then(def => {
const display = def?.primaryDisplay || def?.schema?.[0]?.name
primaryDisplayCache[tableId] = display
resolve(display)
})
})
}
// We await the result so that we account for both promises and primitives
return await primaryDisplayCache[tableId]
}
return {
cache: {
actions: {
getPrimaryDisplayForTableId,
resetPrimaryDisplayCache,
},
},
}
}
export const initialise = context => {
const { datasource, cache } = context
// Wipe the caches whenever the datasource changes to ensure we aren't
// storing any stale information
datasource.subscribe(cache.actions.resetPrimaryDisplayCache)
}

View File

@ -160,11 +160,6 @@ export const createActions = context => {
return getAPI()?.actions.canUseColumn(name) return getAPI()?.actions.canUseColumn(name)
} }
// Gets the default number of rows for a single page
const getFeatures = () => {
return getAPI()?.actions.getFeatures()
}
return { return {
datasource: { datasource: {
...datasource, ...datasource,
@ -177,7 +172,6 @@ export const createActions = context => {
getRow, getRow,
isDatasourceValid, isDatasourceValid,
canUseColumn, canUseColumn,
getFeatures,
}, },
}, },
} }

View File

@ -35,11 +35,6 @@ export const createActions = context => {
return $columns.some(col => col.name === name) || $sticky?.name === name return $columns.some(col => col.name === name) || $sticky?.name === name
} }
const getFeatures = () => {
// We don't support any features
return {}
}
return { return {
nonPlus: { nonPlus: {
actions: { actions: {
@ -50,7 +45,6 @@ export const createActions = context => {
getRow, getRow,
isDatasourceValid, isDatasourceValid,
canUseColumn, canUseColumn,
getFeatures,
}, },
}, },
} }

View File

@ -1,5 +1,4 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import TableFetch from "../../../../fetch/TableFetch"
const SuppressErrors = true const SuppressErrors = true
@ -46,10 +45,6 @@ export const createActions = context => {
return $columns.some(col => col.name === name) || $sticky?.name === name return $columns.some(col => col.name === name) || $sticky?.name === name
} }
const getFeatures = () => {
return new TableFetch({ API }).determineFeatureFlags()
}
return { return {
table: { table: {
actions: { actions: {
@ -60,7 +55,6 @@ export const createActions = context => {
getRow, getRow,
isDatasourceValid, isDatasourceValid,
canUseColumn, canUseColumn,
getFeatures,
}, },
}, },
} }

View File

@ -1,5 +1,4 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import ViewV2Fetch from "../../../../fetch/ViewV2Fetch"
const SuppressErrors = true const SuppressErrors = true
@ -46,10 +45,6 @@ export const createActions = context => {
) )
} }
const getFeatures = () => {
return new ViewV2Fetch({ API }).determineFeatureFlags()
}
return { return {
viewV2: { viewV2: {
actions: { actions: {
@ -60,7 +55,6 @@ export const createActions = context => {
getRow, getRow,
isDatasourceValid, isDatasourceValid,
canUseColumn, canUseColumn,
getFeatures,
}, },
}, },
} }

View File

@ -19,6 +19,7 @@ import * as Datasource from "./datasource"
import * as Table from "./datasources/table" import * as Table from "./datasources/table"
import * as ViewV2 from "./datasources/viewV2" import * as ViewV2 from "./datasources/viewV2"
import * as NonPlus from "./datasources/nonPlus" import * as NonPlus from "./datasources/nonPlus"
import * as Cache from "./cache"
const DependencyOrderedStores = [ const DependencyOrderedStores = [
Sort, Sort,
@ -42,6 +43,7 @@ const DependencyOrderedStores = [
Clipboard, Clipboard,
Config, Config,
Notifications, Notifications,
Cache,
] ]
export const attachStores = context => { export const attachStores = context => {

View File

@ -114,10 +114,6 @@ export const createActions = context => {
const $allFilters = get(allFilters) const $allFilters = get(allFilters)
const $sort = get(sort) const $sort = get(sort)
// Determine how many rows to fetch per page
const features = datasource.actions.getFeatures()
const limit = features?.supportsPagination ? RowPageSize : null
// Create new fetch model // Create new fetch model
const newFetch = fetchData({ const newFetch = fetchData({
API, API,
@ -126,8 +122,12 @@ export const createActions = context => {
filter: $allFilters, filter: $allFilters,
sortColumn: $sort.column, sortColumn: $sort.column,
sortOrder: $sort.order, sortOrder: $sort.order,
limit, limit: RowPageSize,
paginate: true, paginate: true,
// Disable client side limiting, so that for queries and custom data
// sources we don't impose fake row limits. We want all the data.
clientSideLimiting: false,
}, },
}) })

View File

@ -43,6 +43,11 @@ export default class DataFetch {
// Pagination config // Pagination config
paginate: true, paginate: true,
// Client side feature customisation
clientSideSearching: true,
clientSideSorting: true,
clientSideLimiting: true,
} }
// State of the fetch // State of the fetch
@ -208,24 +213,32 @@ export default class DataFetch {
* Fetches some filtered, sorted and paginated data * Fetches some filtered, sorted and paginated data
*/ */
async getPage() { async getPage() {
const { sortColumn, sortOrder, sortType, limit } = this.options const {
sortColumn,
sortOrder,
sortType,
limit,
clientSideSearching,
clientSideSorting,
clientSideLimiting,
} = this.options
const { query } = get(this.store) const { query } = get(this.store)
// Get the actual data // Get the actual data
let { rows, info, hasNextPage, cursor, error } = await this.getData() let { rows, info, hasNextPage, cursor, error } = await this.getData()
// If we don't support searching, do a client search // If we don't support searching, do a client search
if (!this.features.supportsSearch) { if (!this.features.supportsSearch && clientSideSearching) {
rows = runLuceneQuery(rows, query) rows = runLuceneQuery(rows, query)
} }
// If we don't support sorting, do a client-side sort // If we don't support sorting, do a client-side sort
if (!this.features.supportsSort) { if (!this.features.supportsSort && clientSideSorting) {
rows = luceneSort(rows, sortColumn, sortOrder, sortType) rows = luceneSort(rows, sortColumn, sortOrder, sortType)
} }
// If we don't support pagination, do a client-side limit // If we don't support pagination, do a client-side limit
if (!this.features.supportsPagination) { if (!this.features.supportsPagination && clientSideLimiting) {
rows = luceneLimit(rows, limit) rows = luceneLimit(rows, limit)
} }

View File

@ -7,6 +7,7 @@ import {
isBBReferenceField, isBBReferenceField,
isRelationshipField, isRelationshipField,
LinkDocument, LinkDocument,
LinkInfo,
RelationshipFieldMetadata, RelationshipFieldMetadata,
RelationshipType, RelationshipType,
Row, Row,
@ -125,7 +126,23 @@ abstract class UserColumnMigrator implements ColumnMigrator {
protected newColumn: BBReferenceFieldMetadata protected newColumn: BBReferenceFieldMetadata
) {} ) {}
abstract updateRow(row: Row, link: LinkDocument): void abstract updateRow(row: Row, linkInfo: LinkInfo): void
pickUserTableLinkSide(link: LinkDocument): LinkInfo {
if (link.doc1.tableId === InternalTable.USER_METADATA) {
return link.doc1
} else {
return link.doc2
}
}
pickOtherTableLinkSide(link: LinkDocument): LinkInfo {
if (link.doc1.tableId === InternalTable.USER_METADATA) {
return link.doc2
} else {
return link.doc1
}
}
async doMigration(): Promise<MigrationResult> { async doMigration(): Promise<MigrationResult> {
let oldTable = cloneDeep(this.table) let oldTable = cloneDeep(this.table)
@ -137,15 +154,17 @@ abstract class UserColumnMigrator implements ColumnMigrator {
let links = await sdk.links.fetchWithDocument(this.table._id!) let links = await sdk.links.fetchWithDocument(this.table._id!)
for (let link of links) { for (let link of links) {
const userSide = this.pickUserTableLinkSide(link)
const otherSide = this.pickOtherTableLinkSide(link)
if ( if (
link.doc1.tableId !== this.table._id || otherSide.tableId !== this.table._id ||
link.doc1.fieldName !== this.oldColumn.name || otherSide.fieldName !== this.oldColumn.name ||
link.doc2.tableId !== InternalTable.USER_METADATA userSide.tableId !== InternalTable.USER_METADATA
) { ) {
continue continue
} }
let row = rowsById[link.doc1.rowId] let row = rowsById[otherSide.rowId]
if (!row) { if (!row) {
// This can happen if the row has been deleted but the link hasn't, // This can happen if the row has been deleted but the link hasn't,
// which was a state that was found during the initial testing of this // which was a state that was found during the initial testing of this
@ -153,7 +172,7 @@ abstract class UserColumnMigrator implements ColumnMigrator {
continue continue
} }
this.updateRow(row, link) this.updateRow(row, userSide)
} }
let db = context.getAppDB() let db = context.getAppDB()
@ -175,20 +194,20 @@ abstract class UserColumnMigrator implements ColumnMigrator {
} }
class SingleUserColumnMigrator extends UserColumnMigrator { class SingleUserColumnMigrator extends UserColumnMigrator {
updateRow(row: Row, link: LinkDocument): void { updateRow(row: Row, linkInfo: LinkInfo): void {
row[this.newColumn.name] = dbCore.getGlobalIDFromUserMetadataID( row[this.newColumn.name] = dbCore.getGlobalIDFromUserMetadataID(
link.doc2.rowId linkInfo.rowId
) )
} }
} }
class MultiUserColumnMigrator extends UserColumnMigrator { class MultiUserColumnMigrator extends UserColumnMigrator {
updateRow(row: Row, link: LinkDocument): void { updateRow(row: Row, linkInfo: LinkInfo): void {
if (!row[this.newColumn.name]) { if (!row[this.newColumn.name]) {
row[this.newColumn.name] = [] row[this.newColumn.name] = []
} }
row[this.newColumn.name].push( row[this.newColumn.name].push(
dbCore.getGlobalIDFromUserMetadataID(link.doc2.rowId) dbCore.getGlobalIDFromUserMetadataID(linkInfo.rowId)
) )
} }
} }

View File

@ -1,17 +1,15 @@
import { Document } from "../document" import { Document } from "../document"
export interface LinkInfo {
rowId: string
fieldName: string
tableId: string
}
export interface LinkDocument extends Document { export interface LinkDocument extends Document {
type: string type: string
doc1: { doc1: LinkInfo
rowId: string doc2: LinkInfo
fieldName: string
tableId: string
}
doc2: {
rowId: string
fieldName: string
tableId: string
}
} }
export interface LinkDocumentValue { export interface LinkDocumentValue {