Merge pull request #15363 from Budibase/ts/type-fetchData-usages

Type fetch data usages
This commit is contained in:
Adria Navarro 2025-01-16 11:11:22 +01:00 committed by GitHub
commit 1b2a7a6dc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 363 additions and 198 deletions

View File

@ -1,22 +1,39 @@
<script> <script lang="ts">
import { getContext } from "svelte" import { getContext } from "svelte"
import { Pagination, ProgressCircle } from "@budibase/bbui" import { Pagination, ProgressCircle } from "@budibase/bbui"
import { fetchData, QueryUtils } from "@budibase/frontend-core" import { fetchData, QueryUtils } from "@budibase/frontend-core"
import { LogicalOperator, EmptyFilterOption } from "@budibase/types" import {
LogicalOperator,
EmptyFilterOption,
TableSchema,
SortOrder,
SearchFilters,
UISearchFilter,
DataFetchDatasource,
UserDatasource,
GroupUserDatasource,
DataFetchOptions,
} from "@budibase/types"
import { SDK, Component } from "../../index"
export let dataSource type ProviderDatasource = Exclude<
export let filter DataFetchDatasource,
export let sortColumn UserDatasource | GroupUserDatasource
export let sortOrder >
export let limit
export let paginate
export let autoRefresh
const { styleable, Provider, ActionTypes, API } = getContext("sdk") export let dataSource: ProviderDatasource
const component = getContext("component") export let filter: UISearchFilter
export let sortColumn: string
export let sortOrder: SortOrder
export let limit: number
export let paginate: boolean
export let autoRefresh: number
let interval const { styleable, Provider, ActionTypes, API } = getContext<SDK>("sdk")
let queryExtensions = {} const component = getContext<Component>("component")
let interval: ReturnType<typeof setInterval>
let queryExtensions: Record<string, any> = {}
$: defaultQuery = QueryUtils.buildQuery(filter) $: defaultQuery = QueryUtils.buildQuery(filter)
@ -49,8 +66,14 @@
}, },
{ {
type: ActionTypes.SetDataProviderSorting, type: ActionTypes.SetDataProviderSorting,
callback: ({ column, order }) => { callback: ({
let newOptions = {} column,
order,
}: {
column: string
order: SortOrder | undefined
}) => {
let newOptions: Partial<DataFetchOptions> = {}
if (column) { if (column) {
newOptions.sortColumn = column newOptions.sortColumn = column
} }
@ -63,6 +86,7 @@
}, },
}, },
] ]
$: dataContext = { $: dataContext = {
rows: $fetch.rows, rows: $fetch.rows,
info: $fetch.info, info: $fetch.info,
@ -75,14 +99,12 @@
id: $component?.id, id: $component?.id,
state: { state: {
query: $fetch.query, query: $fetch.query,
sortColumn: $fetch.sortColumn,
sortOrder: $fetch.sortOrder,
}, },
limit, limit,
primaryDisplay: $fetch.definition?.primaryDisplay, primaryDisplay: ($fetch.definition as any)?.primaryDisplay,
} }
const createFetch = datasource => { const createFetch = (datasource: ProviderDatasource) => {
return fetchData({ return fetchData({
API, API,
datasource, datasource,
@ -96,7 +118,7 @@
}) })
} }
const sanitizeSchema = schema => { const sanitizeSchema = (schema: TableSchema | null) => {
if (!schema) { if (!schema) {
return schema return schema
} }
@ -109,14 +131,14 @@
return cloned return cloned
} }
const addQueryExtension = (key, extension) => { const addQueryExtension = (key: string, extension: any) => {
if (!key || !extension) { if (!key || !extension) {
return return
} }
queryExtensions = { ...queryExtensions, [key]: extension } queryExtensions = { ...queryExtensions, [key]: extension }
} }
const removeQueryExtension = key => { const removeQueryExtension = (key: string) => {
if (!key) { if (!key) {
return return
} }
@ -125,11 +147,14 @@
queryExtensions = newQueryExtensions queryExtensions = newQueryExtensions
} }
const extendQuery = (defaultQuery, extensions) => { const extendQuery = (
defaultQuery: SearchFilters,
extensions: Record<string, any>
): SearchFilters => {
if (!Object.keys(extensions).length) { if (!Object.keys(extensions).length) {
return defaultQuery return defaultQuery
} }
const extended = { const extended: SearchFilters = {
[LogicalOperator.AND]: { [LogicalOperator.AND]: {
conditions: [ conditions: [
...(defaultQuery ? [defaultQuery] : []), ...(defaultQuery ? [defaultQuery] : []),
@ -140,12 +165,12 @@
} }
// If there are no conditions applied at all, clear the request. // If there are no conditions applied at all, clear the request.
return extended[LogicalOperator.AND]?.conditions?.length > 0 return (extended[LogicalOperator.AND]?.conditions?.length ?? 0) > 0
? extended ? extended
: null : {}
} }
const setUpAutoRefresh = autoRefresh => { const setUpAutoRefresh = (autoRefresh: number) => {
clearInterval(interval) clearInterval(interval)
if (autoRefresh) { if (autoRefresh) {
interval = setInterval(fetch.refresh, Math.max(10000, autoRefresh * 1000)) interval = setInterval(fetch.refresh, Math.max(10000, autoRefresh * 1000))

View File

@ -0,0 +1,15 @@
import { APIClient } from "@budibase/frontend-core"
import type { ActionTypes } from "./constants"
import { Readable } from "svelte/store"
export interface SDK {
API: APIClient
styleable: any
Provider: any
ActionTypes: typeof ActionTypes
}
export type Component = Readable<{
id: string
styles: any
}>

View File

@ -6,7 +6,7 @@ import { screenStore } from "./screens"
import { builderStore } from "./builder" import { builderStore } from "./builder"
import Router from "../components/Router.svelte" import Router from "../components/Router.svelte"
import * as AppComponents from "../components/app/index.js" import * as AppComponents from "../components/app/index.js"
import { ScreenslotType } from "../constants.js" import { ScreenslotType } from "../constants"
export const BudibasePrefix = "@budibase/standard-components/" export const BudibasePrefix = "@budibase/standard-components/"

View File

@ -1,7 +1,11 @@
// TODO: datasource and defitions are unions of the different implementations. At this point, the datasource does not know what type is being used, and the assignations will cause TS exceptions. Casting it "as any" for now. This should be fixed improving the type usages. // TODO: datasource and defitions are unions of the different implementations. At this point, the datasource does not know what type is being used, and the assignations will cause TS exceptions. Casting it "as any" for now. This should be fixed improving the type usages.
import { derived, get, Readable, Writable } from "svelte/store" import { derived, get, Readable, Writable } from "svelte/store"
import { getDatasourceDefinition, getDatasourceSchema } from "../../../fetch" import {
DataFetchDefinition,
getDatasourceDefinition,
getDatasourceSchema,
} from "../../../fetch"
import { enrichSchemaWithRelColumns, memo } from "../../../utils" import { enrichSchemaWithRelColumns, memo } from "../../../utils"
import { cloneDeep } from "lodash" import { cloneDeep } from "lodash"
import { import {
@ -18,7 +22,7 @@ import { Store as StoreContext, BaseStoreProps } from "."
import { DatasourceActions } from "./datasources" import { DatasourceActions } from "./datasources"
interface DatasourceStore { interface DatasourceStore {
definition: Writable<UIDatasource | null> definition: Writable<DataFetchDefinition | null>
schemaMutations: Writable<Record<string, UIFieldMutation>> schemaMutations: Writable<Record<string, UIFieldMutation>>
subSchemaMutations: Writable<Record<string, Record<string, UIFieldMutation>>> subSchemaMutations: Writable<Record<string, Record<string, UIFieldMutation>>>
} }
@ -131,11 +135,17 @@ export const deriveStores = (context: StoreContext): DerivedDatasourceStore => {
[datasource, definition], [datasource, definition],
([$datasource, $definition]) => { ([$datasource, $definition]) => {
let type = $datasource?.type let type = $datasource?.type
// @ts-expect-error
if (type === "provider") { if (type === "provider") {
type = ($datasource as any).value?.datasource?.type // TODO: see line 1 type = ($datasource as any).value?.datasource?.type // TODO: see line 1
} }
// Handle calculation views // Handle calculation views
if (type === "viewV2" && $definition?.type === ViewV2Type.CALCULATION) { if (
type === "viewV2" &&
$definition &&
"type" in $definition &&
$definition.type === ViewV2Type.CALCULATION
) {
return false return false
} }
return !!type && ["table", "viewV2", "link"].includes(type) return !!type && ["table", "viewV2", "link"].includes(type)
@ -197,7 +207,7 @@ export const createActions = (context: StoreContext): ActionDatasourceStore => {
) => { ) => {
// Update local state // Update local state
const originalDefinition = get(definition) const originalDefinition = get(definition)
definition.set(newDefinition as UIDatasource) definition.set(newDefinition)
// Update server // Update server
if (get(config).canSaveSchema) { if (get(config).canSaveSchema) {
@ -225,13 +235,15 @@ export const createActions = (context: StoreContext): ActionDatasourceStore => {
// Update primary display // Update primary display
newDefinition.primaryDisplay = column newDefinition.primaryDisplay = column
// Sanitise schema to ensure field is required and has no default value if (newDefinition.schema) {
if (!newDefinition.schema[column].constraints) { // Sanitise schema to ensure field is required and has no default value
newDefinition.schema[column].constraints = {} if (!newDefinition.schema[column].constraints) {
} newDefinition.schema[column].constraints = {}
newDefinition.schema[column].constraints.presence = { allowEmpty: false } }
if ("default" in newDefinition.schema[column]) { newDefinition.schema[column].constraints.presence = { allowEmpty: false }
delete newDefinition.schema[column].default if ("default" in newDefinition.schema[column]) {
delete newDefinition.schema[column].default
}
} }
return await saveDefinition(newDefinition as any) // TODO: see line 1 return await saveDefinition(newDefinition as any) // TODO: see line 1
} }

View File

@ -8,6 +8,7 @@ import {
import { get } from "svelte/store" import { get } from "svelte/store"
import { Store as StoreContext } from ".." import { Store as StoreContext } from ".."
import { DatasourceTableActions } from "." import { DatasourceTableActions } from "."
import TableFetch from "../../../../fetch/TableFetch"
const SuppressErrors = true const SuppressErrors = true
@ -119,7 +120,7 @@ export const initialise = (context: StoreContext) => {
unsubscribers.push( unsubscribers.push(
allFilters.subscribe($allFilters => { allFilters.subscribe($allFilters => {
// Ensure we're updating the correct fetch // Ensure we're updating the correct fetch
const $fetch = get(fetch) const $fetch = get(fetch) as TableFetch | null
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) { if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
return return
} }
@ -133,7 +134,7 @@ export const initialise = (context: StoreContext) => {
unsubscribers.push( unsubscribers.push(
sort.subscribe($sort => { sort.subscribe($sort => {
// Ensure we're updating the correct fetch // Ensure we're updating the correct fetch
const $fetch = get(fetch) const $fetch = get(fetch) as TableFetch | null
if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) { if ($fetch?.options?.datasource?.tableId !== $datasource.tableId) {
return return
} }

View File

@ -4,11 +4,11 @@ import {
SaveRowRequest, SaveRowRequest,
SortOrder, SortOrder,
UIDatasource, UIDatasource,
UIView,
UpdateViewRequest, UpdateViewRequest,
} from "@budibase/types" } from "@budibase/types"
import { Store as StoreContext } from ".." import { Store as StoreContext } from ".."
import { DatasourceViewActions } from "." import { DatasourceViewActions } from "."
import ViewV2Fetch from "../../../../fetch/ViewV2Fetch"
const SuppressErrors = true const SuppressErrors = true
@ -134,6 +134,9 @@ export const initialise = (context: StoreContext) => {
if (!get(config).canSaveSchema) { if (!get(config).canSaveSchema) {
return return
} }
if (!$definition || !("id" in $definition)) {
return
}
if ($definition?.id !== $datasource.id) { if ($definition?.id !== $datasource.id) {
return return
} }
@ -184,7 +187,10 @@ export const initialise = (context: StoreContext) => {
unsubscribers.push( unsubscribers.push(
sort.subscribe(async $sort => { sort.subscribe(async $sort => {
// Ensure we're updating the correct view // Ensure we're updating the correct view
const $view = get(definition) as UIView const $view = get(definition)
if (!$view || !("id" in $view)) {
return
}
if ($view?.id !== $datasource.id) { if ($view?.id !== $datasource.id) {
return return
} }
@ -207,7 +213,7 @@ export const initialise = (context: StoreContext) => {
// Also update the fetch to ensure the new sort is respected. // Also update the fetch to ensure the new sort is respected.
// Ensure we're updating the correct fetch. // Ensure we're updating the correct fetch.
const $fetch = get(fetch) const $fetch = get(fetch) as ViewV2Fetch | null
if ($fetch?.options?.datasource?.id !== $datasource.id) { if ($fetch?.options?.datasource?.id !== $datasource.id) {
return return
} }
@ -225,6 +231,9 @@ export const initialise = (context: StoreContext) => {
return return
} }
const $view = get(definition) const $view = get(definition)
if (!$view || !("id" in $view)) {
return
}
if ($view?.id !== $datasource.id) { if ($view?.id !== $datasource.id) {
return return
} }
@ -246,7 +255,7 @@ export const initialise = (context: StoreContext) => {
if (!get(config).canSaveSchema) { if (!get(config).canSaveSchema) {
return return
} }
const $fetch = get(fetch) const $fetch = get(fetch) as ViewV2Fetch | null
if ($fetch?.options?.datasource?.id !== $datasource.id) { if ($fetch?.options?.datasource?.id !== $datasource.id) {
return return
} }
@ -262,7 +271,7 @@ export const initialise = (context: StoreContext) => {
if (get(config).canSaveSchema) { if (get(config).canSaveSchema) {
return return
} }
const $fetch = get(fetch) const $fetch = get(fetch) as ViewV2Fetch | null
if ($fetch?.options?.datasource?.id !== $datasource.id) { if ($fetch?.options?.datasource?.id !== $datasource.id) {
return return
} }

View File

@ -1,5 +1,5 @@
import { writable, derived, get, Writable, Readable } from "svelte/store" import { writable, derived, get, Writable, Readable } from "svelte/store"
import { fetchData } from "../../../fetch" import { DataFetch, fetchData } from "../../../fetch"
import { NewRowID, RowPageSize } from "../lib/constants" import { NewRowID, RowPageSize } from "../lib/constants"
import { import {
generateRowID, generateRowID,
@ -13,7 +13,6 @@ import { sleep } from "../../../utils/utils"
import { FieldType, Row, UIRow } from "@budibase/types" import { FieldType, Row, UIRow } from "@budibase/types"
import { getRelatedTableValues } from "../../../utils" import { getRelatedTableValues } from "../../../utils"
import { Store as StoreContext } from "." import { Store as StoreContext } from "."
import DataFetch from "../../../fetch/DataFetch"
interface IndexedUIRow extends UIRow { interface IndexedUIRow extends UIRow {
__idx: number __idx: number
@ -21,7 +20,7 @@ interface IndexedUIRow extends UIRow {
interface RowStore { interface RowStore {
rows: Writable<UIRow[]> rows: Writable<UIRow[]>
fetch: Writable<DataFetch<any, any, any> | null> // TODO: type this properly, having a union of all the possible options fetch: Writable<DataFetch | null>
loaded: Writable<boolean> loaded: Writable<boolean>
refreshing: Writable<boolean> refreshing: Writable<boolean>
loading: Writable<boolean> loading: Writable<boolean>
@ -254,7 +253,7 @@ export const createActions = (context: StoreContext): RowActionStore => {
// Reset state properties when dataset changes // Reset state properties when dataset changes
if (!$instanceLoaded || resetRows) { if (!$instanceLoaded || resetRows) {
definition.set($fetch.definition as any) // TODO: datasource and defitions are unions of the different implementations. At this point, the datasource does not know what type is being used, and the assignations will cause TS exceptions. Casting it "as any" for now. This should be fixed improving the type usages. definition.set($fetch.definition ?? null)
} }
// Reset scroll state when data changes // Reset scroll state when data changes

View File

@ -1,13 +1,9 @@
import DataFetch from "./DataFetch" import { CustomDatasource } from "@budibase/types"
import BaseDataFetch from "./DataFetch"
interface CustomDatasource {
type: "custom"
data: any
}
type CustomDefinition = Record<string, any> type CustomDefinition = Record<string, any>
export default class CustomFetch extends DataFetch< export default class CustomFetch extends BaseDataFetch<
CustomDatasource, CustomDatasource,
CustomDefinition CustomDefinition
> { > {

View File

@ -3,14 +3,13 @@ import { cloneDeep } from "lodash/fp"
import { QueryUtils } from "../utils" import { QueryUtils } from "../utils"
import { convertJSONSchemaToTableSchema } from "../utils/json" import { convertJSONSchemaToTableSchema } from "../utils/json"
import { import {
DataFetchOptions,
FieldType, FieldType,
LegacyFilter,
Row, Row,
SearchFilters, SearchFilters,
SortOrder, SortOrder,
SortType, SortType,
TableSchema, TableSchema,
UISearchFilter,
} from "@budibase/types" } from "@budibase/types"
import { APIClient } from "../api/types" import { APIClient } from "../api/types"
import { DataFetchType } from "." import { DataFetchType } from "."
@ -44,14 +43,11 @@ interface DataFetchDerivedStore<TDefinition, TQuery>
supportsPagination: boolean supportsPagination: boolean
} }
export interface DataFetchParams< export interface DataFetchParams<TDatasource, TQuery = SearchFilters> {
TDatasource,
TQuery = SearchFilters | undefined
> {
API: APIClient API: APIClient
datasource: TDatasource datasource: TDatasource
query: TQuery query: TQuery
options?: {} options?: Partial<DataFetchOptions<TQuery>>
} }
/** /**
@ -59,7 +55,7 @@ export interface DataFetchParams<
* internal table or datasource plus. * internal table or datasource plus.
* For other types of datasource, this class is overridden and extended. * For other types of datasource, this class is overridden and extended.
*/ */
export default abstract class DataFetch< export default abstract class BaseDataFetch<
TDatasource extends { type: DataFetchType }, TDatasource extends { type: DataFetchType },
TDefinition extends { TDefinition extends {
schema?: Record<string, any> | null schema?: Record<string, any> | null
@ -73,18 +69,11 @@ export default abstract class DataFetch<
supportsSort: boolean supportsSort: boolean
supportsPagination: boolean supportsPagination: boolean
} }
options: { options: DataFetchOptions<TQuery> & {
datasource: TDatasource datasource: TDatasource
limit: number
// Search config
filter: UISearchFilter | LegacyFilter[] | null
query: TQuery
// Sorting config
sortColumn: string | null
sortOrder: SortOrder
sortType: SortType | null sortType: SortType | null
// Pagination config
paginate: boolean
// Client side feature customisation // Client side feature customisation
clientSideSearching: boolean clientSideSearching: boolean
clientSideSorting: boolean clientSideSorting: boolean
@ -267,6 +256,7 @@ export default abstract class DataFetch<
// Build the query // Build the query
let query = this.options.query let query = this.options.query
if (!query) { if (!query) {
query = buildQuery(filter ?? undefined) as TQuery query = buildQuery(filter ?? undefined) as TQuery
} }
@ -430,7 +420,7 @@ export default abstract class DataFetch<
* Resets the data set and updates options * Resets the data set and updates options
* @param newOptions any new options * @param newOptions any new options
*/ */
async update(newOptions: any) { async update(newOptions: Partial<DataFetchOptions<TQuery>>) {
// Check if any settings have actually changed // Check if any settings have actually changed
let refresh = false let refresh = false
for (const [key, value] of Object.entries(newOptions || {})) { for (const [key, value] of Object.entries(newOptions || {})) {

View File

@ -1,14 +1,10 @@
import { Row } from "@budibase/types" import {
import DataFetch from "./DataFetch" FieldDatasource,
JSONArrayFieldDatasource,
type Types = "field" | "queryarray" | "jsonarray" QueryArrayFieldDatasource,
Row,
export interface FieldDatasource<TType extends Types> { } from "@budibase/types"
type: TType import BaseDataFetch from "./DataFetch"
tableId: string
fieldType: "attachment" | "array"
value: string[] | Row[]
}
export interface FieldDefinition { export interface FieldDefinition {
schema?: Record<string, { type: string }> | null schema?: Record<string, { type: string }> | null
@ -18,10 +14,12 @@ function isArrayOfStrings(value: string[] | Row[]): value is string[] {
return Array.isArray(value) && !!value[0] && typeof value[0] !== "object" return Array.isArray(value) && !!value[0] && typeof value[0] !== "object"
} }
export default class FieldFetch<TType extends Types> extends DataFetch< export default class FieldFetch<
FieldDatasource<TType>, TDatasource extends
FieldDefinition | FieldDatasource
> { | QueryArrayFieldDatasource
| JSONArrayFieldDatasource = FieldDatasource
> extends BaseDataFetch<TDatasource, FieldDefinition> {
async getDefinition(): Promise<FieldDefinition | null> { async getDefinition(): Promise<FieldDefinition | null> {
const { datasource } = this.options const { datasource } = this.options

View File

@ -1,20 +1,20 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import DataFetch, { DataFetchParams } from "./DataFetch" import BaseDataFetch, { DataFetchParams } from "./DataFetch"
import { TableNames } from "../constants" import { GroupUserDatasource, InternalTable } from "@budibase/types"
interface GroupUserQuery { interface GroupUserQuery {
groupId: string groupId: string
emailSearch: string emailSearch: string
} }
interface GroupUserDatasource { interface GroupUserDefinition {
type: "groupUser" schema?: Record<string, any> | null
tableId: TableNames.USERS primaryDisplay?: string
} }
export default class GroupUserFetch extends DataFetch< export default class GroupUserFetch extends BaseDataFetch<
GroupUserDatasource, GroupUserDatasource,
{}, GroupUserDefinition,
GroupUserQuery GroupUserQuery
> { > {
constructor(opts: DataFetchParams<GroupUserDatasource, GroupUserQuery>) { constructor(opts: DataFetchParams<GroupUserDatasource, GroupUserQuery>) {
@ -22,7 +22,7 @@ export default class GroupUserFetch extends DataFetch<
...opts, ...opts,
datasource: { datasource: {
type: "groupUser", type: "groupUser",
tableId: TableNames.USERS, tableId: InternalTable.USER_METADATA,
}, },
}) })
} }

View File

@ -1,7 +1,8 @@
import FieldFetch from "./FieldFetch" import FieldFetch from "./FieldFetch"
import { getJSONArrayDatasourceSchema } from "../utils/json" import { getJSONArrayDatasourceSchema } from "../utils/json"
import { JSONArrayFieldDatasource } from "@budibase/types"
export default class JSONArrayFetch extends FieldFetch<"jsonarray"> { export default class JSONArrayFetch extends FieldFetch<JSONArrayFieldDatasource> {
async getDefinition() { async getDefinition() {
const { datasource } = this.options const { datasource } = this.options

View File

@ -1,20 +1,11 @@
import { Row, TableSchema } from "@budibase/types" import { NestedProviderDatasource, TableSchema } from "@budibase/types"
import DataFetch from "./DataFetch" import BaseDataFetch from "./DataFetch"
interface NestedProviderDatasource {
type: "provider"
value?: {
schema: TableSchema
primaryDisplay: string
rows: Row[]
}
}
interface NestedProviderDefinition { interface NestedProviderDefinition {
schema?: TableSchema schema?: TableSchema
primaryDisplay?: string primaryDisplay?: string
} }
export default class NestedProviderFetch extends DataFetch< export default class NestedProviderFetch extends BaseDataFetch<
NestedProviderDatasource, NestedProviderDatasource,
NestedProviderDefinition NestedProviderDefinition
> { > {

View File

@ -3,8 +3,9 @@ import {
getJSONArrayDatasourceSchema, getJSONArrayDatasourceSchema,
generateQueryArraySchemas, generateQueryArraySchemas,
} from "../utils/json" } from "../utils/json"
import { QueryArrayFieldDatasource } from "@budibase/types"
export default class QueryArrayFetch extends FieldFetch<"queryarray"> { export default class QueryArrayFetch extends FieldFetch<QueryArrayFieldDatasource> {
async getDefinition() { async getDefinition() {
const { datasource } = this.options const { datasource } = this.options

View File

@ -1,23 +1,9 @@
import DataFetch from "./DataFetch" import BaseDataFetch from "./DataFetch"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import { ExecuteQueryRequest, Query } from "@budibase/types" import { ExecuteQueryRequest, Query, QueryDatasource } from "@budibase/types"
import { get } from "svelte/store" import { get } from "svelte/store"
interface QueryDatasource { export default class QueryFetch extends BaseDataFetch<QueryDatasource, Query> {
type: "query"
_id: string
fields: Record<string, any> & {
pagination?: {
type: string
location: string
pageParam: string
}
}
queryParams?: Record<string, string>
parameters: { name: string; default: string }[]
}
export default class QueryFetch extends DataFetch<QueryDatasource, Query> {
async determineFeatureFlags() { async determineFeatureFlags() {
const definition = await this.getDefinition() const definition = await this.getDefinition()
const supportsPagination = const supportsPagination =

View File

@ -1,15 +1,7 @@
import { Table } from "@budibase/types" import { RelationshipDatasource, Table } from "@budibase/types"
import DataFetch from "./DataFetch" import BaseDataFetch from "./DataFetch"
interface RelationshipDatasource { export default class RelationshipFetch extends BaseDataFetch<
type: "link"
tableId: string
rowId: string
rowTableId: string
fieldName: string
}
export default class RelationshipFetch extends DataFetch<
RelationshipDatasource, RelationshipDatasource,
Table Table
> { > {

View File

@ -1,13 +1,8 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import DataFetch from "./DataFetch" import BaseDataFetch from "./DataFetch"
import { SortOrder, Table } from "@budibase/types" import { SortOrder, Table, TableDatasource } from "@budibase/types"
interface TableDatasource { export default class TableFetch extends BaseDataFetch<TableDatasource, Table> {
type: "table"
tableId: string
}
export default class TableFetch extends DataFetch<TableDatasource, Table> {
async determineFeatureFlags() { async determineFeatureFlags() {
return { return {
supportsSearch: true, supportsSearch: true,

View File

@ -1,22 +1,24 @@
import { get } from "svelte/store" import { get } from "svelte/store"
import DataFetch, { DataFetchParams } from "./DataFetch" import BaseDataFetch, { DataFetchParams } from "./DataFetch"
import { TableNames } from "../constants"
import { utils } from "@budibase/shared-core" import { utils } from "@budibase/shared-core"
import { SearchFilters, SearchUsersRequest } from "@budibase/types" import {
InternalTable,
SearchFilters,
SearchUsersRequest,
UserDatasource,
} from "@budibase/types"
interface UserFetchQuery { interface UserFetchQuery {
appId: string appId: string
paginated: boolean paginated: boolean
} }
interface UserDatasource { interface UserDefinition {
type: "user" schema?: Record<string, any> | null
tableId: TableNames.USERS primaryDisplay?: string
} }
interface UserDefinition {} export default class UserFetch extends BaseDataFetch<
export default class UserFetch extends DataFetch<
UserDatasource, UserDatasource,
UserDefinition, UserDefinition,
UserFetchQuery UserFetchQuery
@ -26,7 +28,7 @@ export default class UserFetch extends DataFetch<
...opts, ...opts,
datasource: { datasource: {
type: "user", type: "user",
tableId: TableNames.USERS, tableId: InternalTable.USER_METADATA,
}, },
}) })
} }

View File

@ -1,16 +1,7 @@
import { Table } from "@budibase/types" import { Table, ViewV1Datasource } from "@budibase/types"
import DataFetch from "./DataFetch" import BaseDataFetch from "./DataFetch"
type ViewV1Datasource = { export default class ViewFetch extends BaseDataFetch<ViewV1Datasource, Table> {
type: "view"
name: string
tableId: string
calculation: string
field: string
groupBy: string
}
export default class ViewFetch extends DataFetch<ViewV1Datasource, Table> {
async getDefinition() { async getDefinition() {
const { datasource } = this.options const { datasource } = this.options

View File

@ -1,14 +1,14 @@
import { SortOrder, ViewV2Enriched, ViewV2Type } from "@budibase/types" import {
import DataFetch from "./DataFetch" SortOrder,
ViewDatasource,
ViewV2Enriched,
ViewV2Type,
} from "@budibase/types"
import BaseDataFetch from "./DataFetch"
import { get } from "svelte/store" import { get } from "svelte/store"
import { helpers } from "@budibase/shared-core" import { helpers } from "@budibase/shared-core"
interface ViewDatasource { export default class ViewV2Fetch extends BaseDataFetch<
type: "viewV2"
id: string
}
export default class ViewV2Fetch extends DataFetch<
ViewDatasource, ViewDatasource,
ViewV2Enriched ViewV2Enriched
> { > {

View File

@ -11,6 +11,7 @@ import GroupUserFetch from "./GroupUserFetch"
import CustomFetch from "./CustomFetch" import CustomFetch from "./CustomFetch"
import QueryArrayFetch from "./QueryArrayFetch" import QueryArrayFetch from "./QueryArrayFetch"
import { APIClient } from "../api/types" import { APIClient } from "../api/types"
import { DataFetchDatasource, Table, ViewV2Enriched } from "@budibase/types"
export type DataFetchType = keyof typeof DataFetchMap export type DataFetchType = keyof typeof DataFetchMap
@ -26,32 +27,88 @@ export const DataFetchMap = {
// Client specific datasource types // Client specific datasource types
provider: NestedProviderFetch, provider: NestedProviderFetch,
field: FieldFetch<"field">, field: FieldFetch,
jsonarray: JSONArrayFetch, jsonarray: JSONArrayFetch,
queryarray: QueryArrayFetch, queryarray: QueryArrayFetch,
} }
export interface DataFetchClassMap {
table: TableFetch
view: ViewFetch
viewV2: ViewV2Fetch
query: QueryFetch
link: RelationshipFetch
user: UserFetch
groupUser: GroupUserFetch
custom: CustomFetch
// Client specific datasource types
provider: NestedProviderFetch
field: FieldFetch
jsonarray: JSONArrayFetch
queryarray: QueryArrayFetch
}
export type DataFetch =
| TableFetch
| ViewFetch
| ViewV2Fetch
| QueryFetch
| RelationshipFetch
| UserFetch
| GroupUserFetch
| CustomFetch
| NestedProviderFetch
| FieldFetch
| JSONArrayFetch
| QueryArrayFetch
export type DataFetchDefinition =
| Table
| ViewV2Enriched
| {
// These fields are added to allow checking these fields on definition usages without requiring constant castings
schema?: Record<string, any> | null
primaryDisplay?: string
rowHeight?: number
type?: string
name?: string
}
// Constructs a new fetch model for a certain datasource // Constructs a new fetch model for a certain datasource
export const fetchData = ({ API, datasource, options }: any) => { export const fetchData = <
const Fetch = DataFetchMap[datasource?.type as DataFetchType] || TableFetch T extends DataFetchDatasource,
Type extends T["type"] = T["type"]
>({
API,
datasource,
options,
}: {
API: APIClient
datasource: T
options: any
}): Type extends keyof DataFetchClassMap
? DataFetchClassMap[Type]
: TableFetch => {
const Fetch = DataFetchMap[datasource?.type] || TableFetch
const fetch = new Fetch({ API, datasource, ...options }) const fetch = new Fetch({ API, datasource, ...options })
// Initially fetch data but don't bother waiting for the result // Initially fetch data but don't bother waiting for the result
fetch.getInitialData() fetch.getInitialData()
return fetch return fetch as any
} }
// Creates an empty fetch instance with no datasource configured, so no data // Creates an empty fetch instance with no datasource configured, so no data
// will initially be loaded // will initially be loaded
const createEmptyFetchInstance = <TDatasource extends { type: DataFetchType }>({ const createEmptyFetchInstance = ({
API, API,
datasource, datasource,
}: { }: {
API: APIClient API: APIClient
datasource: TDatasource datasource: DataFetchDatasource
}) => { }) => {
const handler = DataFetchMap[datasource?.type as DataFetchType] const handler = DataFetchMap[datasource?.type]
if (!handler) { if (!handler) {
return null return null
} }
@ -63,29 +120,25 @@ const createEmptyFetchInstance = <TDatasource extends { type: DataFetchType }>({
} }
// Fetches the definition of any type of datasource // Fetches the definition of any type of datasource
export const getDatasourceDefinition = async < export const getDatasourceDefinition = async ({
TDatasource extends { type: DataFetchType }
>({
API, API,
datasource, datasource,
}: { }: {
API: APIClient API: APIClient
datasource: TDatasource datasource: DataFetchDatasource
}) => { }) => {
const instance = createEmptyFetchInstance({ API, datasource }) const instance = createEmptyFetchInstance({ API, datasource })
return await instance?.getDefinition() return await instance?.getDefinition()
} }
// Fetches the schema of any type of datasource // Fetches the schema of any type of datasource
export const getDatasourceSchema = < export const getDatasourceSchema = ({
TDatasource extends { type: DataFetchType }
>({
API, API,
datasource, datasource,
definition, definition,
}: { }: {
API: APIClient API: APIClient
datasource: TDatasource datasource: DataFetchDatasource
definition?: any definition?: any
}) => { }) => {
const instance = createEmptyFetchInstance({ API, datasource }) const instance = createEmptyFetchInstance({ API, datasource })

View File

@ -1,7 +1,6 @@
export { createAPIClient } from "./api" export { createAPIClient } from "./api"
export type { APIClient } from "./api" export type { APIClient } from "./api"
export { fetchData, DataFetchMap } from "./fetch" export { fetchData, DataFetchMap } from "./fetch"
export type { DataFetchType } from "./fetch"
export * as Constants from "./constants" export * as Constants from "./constants"
export * from "./stores" export * from "./stores"
export * from "./utils" export * from "./utils"

View File

@ -0,0 +1,93 @@
import { InternalTable, Row, TableSchema } from "../../documents"
export type DataFetchDatasource =
| TableDatasource
| ViewV1Datasource
| ViewDatasource
| QueryDatasource
| RelationshipDatasource
| UserDatasource
| GroupUserDatasource
| CustomDatasource
| NestedProviderDatasource
| FieldDatasource
| QueryArrayFieldDatasource
| JSONArrayFieldDatasource
export interface TableDatasource {
type: "table"
tableId: string
}
export type ViewV1Datasource = {
type: "view"
name: string
tableId: string
calculation: string
field: string
groupBy: string
}
export interface ViewDatasource {
type: "viewV2"
id: string
}
export interface QueryDatasource {
type: "query"
_id: string
fields: Record<string, any> & {
pagination?: {
type: string
location: string
pageParam: string
}
}
queryParams?: Record<string, string>
parameters: { name: string; default: string }[]
}
export interface RelationshipDatasource {
type: "link"
tableId: string
rowId: string
rowTableId: string
fieldName: string
}
export interface UserDatasource {
type: "user"
tableId: InternalTable.USER_METADATA
}
export interface GroupUserDatasource {
type: "groupUser"
tableId: InternalTable.USER_METADATA
}
export interface CustomDatasource {
type: "custom"
data: any
}
export interface NestedProviderDatasource {
type: "provider"
value?: {
schema: TableSchema
primaryDisplay: string
rows: Row[]
}
}
interface BaseFieldDatasource<
TType extends "field" | "queryarray" | "jsonarray"
> {
type: TType
tableId: string
fieldType: "attachment" | "array"
value: string[] | Row[]
}
export type FieldDatasource = BaseFieldDatasource<"field">
export type QueryArrayFieldDatasource = BaseFieldDatasource<"queryarray">
export type JSONArrayFieldDatasource = BaseFieldDatasource<"jsonarray">

View File

@ -0,0 +1,16 @@
import { LegacyFilter, SortOrder, UISearchFilter } from "../../api"
import { SearchFilters } from "../../sdk"
export * from "./datasources"
export interface DataFetchOptions<TQuery = SearchFilters> {
// Search config
filter: UISearchFilter | LegacyFilter[] | null
query: TQuery
// Sorting config
sortColumn: string | null
sortOrder: SortOrder
// Pagination config
limit: number
paginate: boolean
}

View File

@ -1 +1,2 @@
export * from "./stores" export * from "./stores"
export * from "./dataFetch"

View File

@ -1,6 +1,6 @@
import { UITable, UIView } from "@budibase/types" import { UITable, UIView } from "@budibase/types"
export type UIDatasource = UITable | UIView export type UIDatasource = UITable | (Omit<UIView, "type"> & { type: "viewV2" })
export interface UIFieldMutation { export interface UIFieldMutation {
visible?: boolean visible?: boolean

View File

@ -8,10 +8,9 @@ import {
UISearchFilter, UISearchFilter,
} from "@budibase/types" } from "@budibase/types"
export interface UITable extends Omit<Table, "type"> { export interface UITable extends Table {
name: string name: string
id: string id: string
type: string
tableId: string tableId: string
primaryDisplay?: string primaryDisplay?: string
sort?: { sort?: {