Merge branch 'master' into fix/user-fetch
This commit is contained in:
commit
da5f49bacc
|
@ -1,130 +0,0 @@
|
||||||
import { writable, get, derived } from "svelte/store"
|
|
||||||
import { datasources } from "./datasources"
|
|
||||||
import { integrations } from "./integrations"
|
|
||||||
import { API } from "@/api"
|
|
||||||
import { duplicateName } from "@/helpers/duplicate"
|
|
||||||
|
|
||||||
const sortQueries = queryList => {
|
|
||||||
queryList.sort((q1, q2) => {
|
|
||||||
return q1.name.localeCompare(q2.name)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createQueriesStore() {
|
|
||||||
const store = writable({
|
|
||||||
list: [],
|
|
||||||
selectedQueryId: null,
|
|
||||||
})
|
|
||||||
const derivedStore = derived(store, $store => ({
|
|
||||||
...$store,
|
|
||||||
selected: $store.list?.find(q => q._id === $store.selectedQueryId),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const fetch = async () => {
|
|
||||||
const queries = await API.getQueries()
|
|
||||||
sortQueries(queries)
|
|
||||||
store.update(state => ({
|
|
||||||
...state,
|
|
||||||
list: queries,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const save = async (datasourceId, query) => {
|
|
||||||
const _integrations = get(integrations)
|
|
||||||
const dataSource = get(datasources).list.filter(
|
|
||||||
ds => ds._id === datasourceId
|
|
||||||
)
|
|
||||||
// Check if readable attribute is found
|
|
||||||
if (dataSource.length !== 0) {
|
|
||||||
const integration = _integrations[dataSource[0].source]
|
|
||||||
const readable = integration.query[query.queryVerb].readable
|
|
||||||
if (readable) {
|
|
||||||
query.readable = readable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
query.datasourceId = datasourceId
|
|
||||||
const savedQuery = await API.saveQuery(query)
|
|
||||||
store.update(state => {
|
|
||||||
const idx = state.list.findIndex(query => query._id === savedQuery._id)
|
|
||||||
const queries = state.list
|
|
||||||
if (idx >= 0) {
|
|
||||||
queries.splice(idx, 1, savedQuery)
|
|
||||||
} else {
|
|
||||||
queries.push(savedQuery)
|
|
||||||
}
|
|
||||||
sortQueries(queries)
|
|
||||||
return {
|
|
||||||
list: queries,
|
|
||||||
selectedQueryId: savedQuery._id,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return savedQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
const importQueries = async ({ data, datasourceId }) => {
|
|
||||||
return await API.importQueries(datasourceId, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
const select = id => {
|
|
||||||
store.update(state => ({
|
|
||||||
...state,
|
|
||||||
selectedQueryId: id,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
const preview = async query => {
|
|
||||||
const result = await API.previewQuery(query)
|
|
||||||
// Assume all the fields are strings and create a basic schema from the
|
|
||||||
// unique fields returned by the server
|
|
||||||
const schema = {}
|
|
||||||
for (let [field, metadata] of Object.entries(result.schema)) {
|
|
||||||
schema[field] = metadata || { type: "string" }
|
|
||||||
}
|
|
||||||
return { ...result, schema, rows: result.rows || [] }
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteQuery = async query => {
|
|
||||||
await API.deleteQuery(query._id, query._rev)
|
|
||||||
store.update(state => {
|
|
||||||
state.list = state.list.filter(existing => existing._id !== query._id)
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const duplicate = async query => {
|
|
||||||
let list = get(store).list
|
|
||||||
const newQuery = { ...query }
|
|
||||||
const datasourceId = query.datasourceId
|
|
||||||
|
|
||||||
delete newQuery._id
|
|
||||||
delete newQuery._rev
|
|
||||||
newQuery.name = duplicateName(
|
|
||||||
query.name,
|
|
||||||
list.map(q => q.name)
|
|
||||||
)
|
|
||||||
|
|
||||||
return await save(datasourceId, newQuery)
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeDatasourceQueries = datasourceId => {
|
|
||||||
store.update(state => ({
|
|
||||||
...state,
|
|
||||||
list: state.list.filter(table => table.datasourceId !== datasourceId),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe: derivedStore.subscribe,
|
|
||||||
fetch,
|
|
||||||
init: fetch,
|
|
||||||
select,
|
|
||||||
save,
|
|
||||||
import: importQueries,
|
|
||||||
delete: deleteQuery,
|
|
||||||
preview,
|
|
||||||
duplicate,
|
|
||||||
removeDatasourceQueries,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const queries = createQueriesStore()
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { derived, get, Writable } from "svelte/store"
|
||||||
|
import { datasources } from "./datasources"
|
||||||
|
import { integrations } from "./integrations"
|
||||||
|
import { API } from "@/api"
|
||||||
|
import { duplicateName } from "@/helpers/duplicate"
|
||||||
|
import { DerivedBudiStore } from "@/stores/BudiStore"
|
||||||
|
import {
|
||||||
|
Query,
|
||||||
|
QueryPreview,
|
||||||
|
PreviewQueryResponse,
|
||||||
|
SaveQueryRequest,
|
||||||
|
ImportRestQueryRequest,
|
||||||
|
QuerySchema,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
const sortQueries = (queryList: Query[]) => {
|
||||||
|
queryList.sort((q1, q2) => {
|
||||||
|
return q1.name.localeCompare(q2.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BuilderQueryStore {
|
||||||
|
list: Query[]
|
||||||
|
selectedQueryId: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DerivedQueryStore extends BuilderQueryStore {
|
||||||
|
selected?: Query
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryStore extends DerivedBudiStore<
|
||||||
|
BuilderQueryStore,
|
||||||
|
DerivedQueryStore
|
||||||
|
> {
|
||||||
|
constructor() {
|
||||||
|
const makeDerivedStore = (store: Writable<BuilderQueryStore>) => {
|
||||||
|
return derived(store, ($store): DerivedQueryStore => {
|
||||||
|
return {
|
||||||
|
list: $store.list,
|
||||||
|
selectedQueryId: $store.selectedQueryId,
|
||||||
|
selected: $store.list?.find(q => q._id === $store.selectedQueryId),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
super(
|
||||||
|
{
|
||||||
|
list: [],
|
||||||
|
selectedQueryId: null,
|
||||||
|
},
|
||||||
|
makeDerivedStore
|
||||||
|
)
|
||||||
|
|
||||||
|
this.select = this.select.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch() {
|
||||||
|
const queries = await API.getQueries()
|
||||||
|
sortQueries(queries)
|
||||||
|
this.store.update(state => ({
|
||||||
|
...state,
|
||||||
|
list: queries,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(datasourceId: string, query: SaveQueryRequest) {
|
||||||
|
const _integrations = get(integrations)
|
||||||
|
const dataSource = get(datasources).list.filter(
|
||||||
|
ds => ds._id === datasourceId
|
||||||
|
)
|
||||||
|
// Check if readable attribute is found
|
||||||
|
if (dataSource.length !== 0) {
|
||||||
|
const integration = _integrations[dataSource[0].source]
|
||||||
|
const readable = integration.query[query.queryVerb].readable
|
||||||
|
if (readable) {
|
||||||
|
query.readable = readable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query.datasourceId = datasourceId
|
||||||
|
const savedQuery = await API.saveQuery(query)
|
||||||
|
this.store.update(state => {
|
||||||
|
const idx = state.list.findIndex(query => query._id === savedQuery._id)
|
||||||
|
const queries = state.list
|
||||||
|
if (idx >= 0) {
|
||||||
|
queries.splice(idx, 1, savedQuery)
|
||||||
|
} else {
|
||||||
|
queries.push(savedQuery)
|
||||||
|
}
|
||||||
|
sortQueries(queries)
|
||||||
|
return {
|
||||||
|
list: queries,
|
||||||
|
selectedQueryId: savedQuery._id || null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return savedQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
async importQueries(data: ImportRestQueryRequest) {
|
||||||
|
return await API.importQueries(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
select(id: string | null) {
|
||||||
|
this.store.update(state => ({
|
||||||
|
...state,
|
||||||
|
selectedQueryId: id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async preview(query: QueryPreview): Promise<PreviewQueryResponse> {
|
||||||
|
const result = await API.previewQuery(query)
|
||||||
|
// Assume all the fields are strings and create a basic schema from the
|
||||||
|
// unique fields returned by the server
|
||||||
|
const schema: Record<string, QuerySchema> = {}
|
||||||
|
for (let [field, metadata] of Object.entries(result.schema)) {
|
||||||
|
schema[field] = (metadata as QuerySchema) || { type: "string" }
|
||||||
|
}
|
||||||
|
return { ...result, schema, rows: result.rows || [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(query: Query) {
|
||||||
|
if (!query._id || !query._rev) {
|
||||||
|
throw new Error("Query ID or Revision is missing")
|
||||||
|
}
|
||||||
|
await API.deleteQuery(query._id, query._rev)
|
||||||
|
this.store.update(state => ({
|
||||||
|
...state,
|
||||||
|
list: state.list.filter(existing => existing._id !== query._id),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async duplicate(query: Query) {
|
||||||
|
let list = get(this.store).list
|
||||||
|
const newQuery = { ...query }
|
||||||
|
const datasourceId = query.datasourceId
|
||||||
|
|
||||||
|
delete newQuery._id
|
||||||
|
delete newQuery._rev
|
||||||
|
newQuery.name = duplicateName(
|
||||||
|
query.name,
|
||||||
|
list.map(q => q.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.save(datasourceId, newQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDatasourceQueries(datasourceId: string) {
|
||||||
|
this.store.update(state => ({
|
||||||
|
...state,
|
||||||
|
list: state.list.filter(table => table.datasourceId !== datasourceId),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
init = this.fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queries = new QueryStore()
|
|
@ -29,7 +29,7 @@ import { ActionTypes } from "./constants"
|
||||||
import {
|
import {
|
||||||
fetchDatasourceSchema,
|
fetchDatasourceSchema,
|
||||||
fetchDatasourceDefinition,
|
fetchDatasourceDefinition,
|
||||||
} from "./utils/schema.js"
|
} from "./utils/schema"
|
||||||
import { getAPIKey } from "./utils/api.js"
|
import { getAPIKey } from "./utils/api.js"
|
||||||
import { enrichButtonActions } from "./utils/buttonActions.js"
|
import { enrichButtonActions } from "./utils/buttonActions.js"
|
||||||
import { processStringSync, makePropSafe } from "@budibase/string-templates"
|
import { processStringSync, makePropSafe } from "@budibase/string-templates"
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import TableFetch from "@budibase/frontend-core/src/fetch/TableFetch"
|
import { DataFetchMap, DataFetchType } from "@budibase/frontend-core"
|
||||||
import ViewFetch from "@budibase/frontend-core/src/fetch/ViewFetch"
|
|
||||||
import QueryFetch from "@budibase/frontend-core/src/fetch/QueryFetch"
|
|
||||||
import RelationshipFetch from "@budibase/frontend-core/src/fetch/RelationshipFetch"
|
|
||||||
import NestedProviderFetch from "@budibase/frontend-core/src/fetch/NestedProviderFetch"
|
|
||||||
import FieldFetch from "@budibase/frontend-core/src/fetch/FieldFetch"
|
|
||||||
import JSONArrayFetch from "@budibase/frontend-core/src/fetch/JSONArrayFetch"
|
|
||||||
import ViewV2Fetch from "@budibase/frontend-core/src/fetch/ViewV2Fetch"
|
|
||||||
import QueryArrayFetch from "@budibase/frontend-core/src/fetch/QueryArrayFetch"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a fetch instance for a given datasource.
|
* Constructs a fetch instance for a given datasource.
|
||||||
|
@ -16,22 +8,20 @@ import QueryArrayFetch from "@budibase/frontend-core/src/fetch/QueryArrayFetch"
|
||||||
* @param datasource the datasource
|
* @param datasource the datasource
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getDatasourceFetchInstance = datasource => {
|
const getDatasourceFetchInstance = <
|
||||||
const handler = {
|
TDatasource extends { type: DataFetchType }
|
||||||
table: TableFetch,
|
>(
|
||||||
view: ViewFetch,
|
datasource: TDatasource
|
||||||
viewV2: ViewV2Fetch,
|
) => {
|
||||||
query: QueryFetch,
|
const handler = DataFetchMap[datasource?.type]
|
||||||
link: RelationshipFetch,
|
|
||||||
provider: NestedProviderFetch,
|
|
||||||
field: FieldFetch,
|
|
||||||
jsonarray: JSONArrayFetch,
|
|
||||||
queryarray: QueryArrayFetch,
|
|
||||||
}[datasource?.type]
|
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return new handler({ API, datasource })
|
return new handler({
|
||||||
|
API,
|
||||||
|
datasource: datasource as never,
|
||||||
|
query: null as any,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,21 +29,23 @@ const getDatasourceFetchInstance = datasource => {
|
||||||
* @param datasource the datasource to fetch the schema for
|
* @param datasource the datasource to fetch the schema for
|
||||||
* @param options options for enriching the schema
|
* @param options options for enriching the schema
|
||||||
*/
|
*/
|
||||||
export const fetchDatasourceSchema = async (
|
export const fetchDatasourceSchema = async <
|
||||||
datasource,
|
TDatasource extends { type: DataFetchType }
|
||||||
|
>(
|
||||||
|
datasource: TDatasource,
|
||||||
options = { enrichRelationships: false, formSchema: false }
|
options = { enrichRelationships: false, formSchema: false }
|
||||||
) => {
|
) => {
|
||||||
const instance = getDatasourceFetchInstance(datasource)
|
const instance = getDatasourceFetchInstance(datasource)
|
||||||
const definition = await instance?.getDefinition(datasource)
|
const definition = await instance?.getDefinition()
|
||||||
if (!definition) {
|
if (!instance || !definition) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the normal schema as long as we aren't wanting a form schema
|
// Get the normal schema as long as we aren't wanting a form schema
|
||||||
let schema
|
let schema: any
|
||||||
if (datasource?.type !== "query" || !options?.formSchema) {
|
if (datasource?.type !== "query" || !options?.formSchema) {
|
||||||
schema = instance.getSchema(definition)
|
schema = instance.getSchema(definition as any)
|
||||||
} else if (definition.parameters?.length) {
|
} else if ("parameters" in definition && definition.parameters?.length) {
|
||||||
schema = {}
|
schema = {}
|
||||||
definition.parameters.forEach(param => {
|
definition.parameters.forEach(param => {
|
||||||
schema[param.name] = { ...param, type: "string" }
|
schema[param.name] = { ...param, type: "string" }
|
||||||
|
@ -73,7 +65,12 @@ export const fetchDatasourceSchema = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enrich schema with relationships if required
|
// Enrich schema with relationships if required
|
||||||
if (definition?.sql && options?.enrichRelationships) {
|
if (
|
||||||
|
definition &&
|
||||||
|
"sql" in definition &&
|
||||||
|
definition.sql &&
|
||||||
|
options?.enrichRelationships
|
||||||
|
) {
|
||||||
const relationshipAdditions = await getRelationshipSchemaAdditions(schema)
|
const relationshipAdditions = await getRelationshipSchemaAdditions(schema)
|
||||||
schema = {
|
schema = {
|
||||||
...schema,
|
...schema,
|
||||||
|
@ -89,20 +86,26 @@ export const fetchDatasourceSchema = async (
|
||||||
* Fetches the definition of any kind of datasource.
|
* Fetches the definition of any kind of datasource.
|
||||||
* @param datasource the datasource to fetch the schema for
|
* @param datasource the datasource to fetch the schema for
|
||||||
*/
|
*/
|
||||||
export const fetchDatasourceDefinition = async datasource => {
|
export const fetchDatasourceDefinition = async <
|
||||||
|
TDatasource extends { type: DataFetchType }
|
||||||
|
>(
|
||||||
|
datasource: TDatasource
|
||||||
|
) => {
|
||||||
const instance = getDatasourceFetchInstance(datasource)
|
const instance = getDatasourceFetchInstance(datasource)
|
||||||
return await instance?.getDefinition(datasource)
|
return await instance?.getDefinition()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the schema of relationship fields for a SQL table schema
|
* Fetches the schema of relationship fields for a SQL table schema
|
||||||
* @param schema the schema to enrich
|
* @param schema the schema to enrich
|
||||||
*/
|
*/
|
||||||
export const getRelationshipSchemaAdditions = async schema => {
|
export const getRelationshipSchemaAdditions = async (
|
||||||
|
schema: Record<string, any>
|
||||||
|
) => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
let relationshipAdditions = {}
|
let relationshipAdditions: Record<string, any> = {}
|
||||||
for (let fieldKey of Object.keys(schema)) {
|
for (let fieldKey of Object.keys(schema)) {
|
||||||
const fieldSchema = schema[fieldKey]
|
const fieldSchema = schema[fieldKey]
|
||||||
if (fieldSchema?.type === "link") {
|
if (fieldSchema?.type === "link") {
|
||||||
|
@ -110,7 +113,10 @@ export const getRelationshipSchemaAdditions = async schema => {
|
||||||
type: "table",
|
type: "table",
|
||||||
tableId: fieldSchema?.tableId,
|
tableId: fieldSchema?.tableId,
|
||||||
})
|
})
|
||||||
Object.keys(linkSchema || {}).forEach(linkKey => {
|
if (!linkSchema) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Object.keys(linkSchema).forEach(linkKey => {
|
||||||
relationshipAdditions[`${fieldKey}.${linkKey}`] = {
|
relationshipAdditions[`${fieldKey}.${linkKey}`] = {
|
||||||
type: linkSchema[linkKey].type,
|
type: linkSchema[linkKey].type,
|
||||||
externalType: linkSchema[linkKey].externalType,
|
externalType: linkSchema[linkKey].externalType,
|
|
@ -1,6 +1,7 @@
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
|
|
||||||
interface CustomDatasource {
|
interface CustomDatasource {
|
||||||
|
type: "custom"
|
||||||
data: any
|
data: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
UISearchFilter,
|
UISearchFilter,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { APIClient } from "../api/types"
|
import { APIClient } from "../api/types"
|
||||||
|
import { DataFetchType } from "."
|
||||||
|
|
||||||
const { buildQuery, limit: queryLimit, runQuery, sort } = QueryUtils
|
const { buildQuery, limit: queryLimit, runQuery, sort } = QueryUtils
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ export interface DataFetchParams<
|
||||||
* 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 DataFetch<
|
||||||
TDatasource extends {},
|
TDatasource extends { type: DataFetchType },
|
||||||
TDefinition extends {
|
TDefinition extends {
|
||||||
schema?: Record<string, any> | null
|
schema?: Record<string, any> | null
|
||||||
primaryDisplay?: string
|
primaryDisplay?: string
|
||||||
|
@ -368,7 +369,7 @@ export default abstract class DataFetch<
|
||||||
* @param schema the datasource schema
|
* @param schema the datasource schema
|
||||||
* @return {object} the enriched datasource schema
|
* @return {object} the enriched datasource schema
|
||||||
*/
|
*/
|
||||||
private enrichSchema(schema: TableSchema): TableSchema {
|
enrichSchema(schema: TableSchema): TableSchema {
|
||||||
// Check for any JSON fields so we can add any top level properties
|
// Check for any JSON fields so we can add any top level properties
|
||||||
let jsonAdditions: Record<string, { type: string; nestedJSON: true }> = {}
|
let jsonAdditions: Record<string, { type: string; nestedJSON: true }> = {}
|
||||||
for (const fieldKey of Object.keys(schema)) {
|
for (const fieldKey of Object.keys(schema)) {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { Row } from "@budibase/types"
|
import { Row } from "@budibase/types"
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
|
|
||||||
export interface FieldDatasource {
|
type Types = "field" | "queryarray" | "jsonarray"
|
||||||
|
|
||||||
|
export interface FieldDatasource<TType extends Types> {
|
||||||
|
type: TType
|
||||||
tableId: string
|
tableId: string
|
||||||
fieldType: "attachment" | "array"
|
fieldType: "attachment" | "array"
|
||||||
value: string[] | Row[]
|
value: string[] | Row[]
|
||||||
|
@ -15,8 +18,8 @@ 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 extends DataFetch<
|
export default class FieldFetch<TType extends Types> extends DataFetch<
|
||||||
FieldDatasource,
|
FieldDatasource<TType>,
|
||||||
FieldDefinition
|
FieldDefinition
|
||||||
> {
|
> {
|
||||||
async getDefinition(): Promise<FieldDefinition | null> {
|
async getDefinition(): Promise<FieldDefinition | null> {
|
||||||
|
|
|
@ -8,6 +8,7 @@ interface GroupUserQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GroupUserDatasource {
|
interface GroupUserDatasource {
|
||||||
|
type: "groupUser"
|
||||||
tableId: TableNames.USERS
|
tableId: TableNames.USERS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ export default class GroupUserFetch extends DataFetch<
|
||||||
super({
|
super({
|
||||||
...opts,
|
...opts,
|
||||||
datasource: {
|
datasource: {
|
||||||
|
type: "groupUser",
|
||||||
tableId: TableNames.USERS,
|
tableId: TableNames.USERS,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import FieldFetch from "./FieldFetch"
|
import FieldFetch from "./FieldFetch"
|
||||||
import { getJSONArrayDatasourceSchema } from "../utils/json"
|
import { getJSONArrayDatasourceSchema } from "../utils/json"
|
||||||
|
|
||||||
export default class JSONArrayFetch extends FieldFetch {
|
export default class JSONArrayFetch extends FieldFetch<"jsonarray"> {
|
||||||
async getDefinition() {
|
async getDefinition() {
|
||||||
const { datasource } = this.options
|
const { datasource } = this.options
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Row, TableSchema } from "@budibase/types"
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
|
|
||||||
interface NestedProviderDatasource {
|
interface NestedProviderDatasource {
|
||||||
|
type: "provider"
|
||||||
value?: {
|
value?: {
|
||||||
schema: TableSchema
|
schema: TableSchema
|
||||||
primaryDisplay: string
|
primaryDisplay: string
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
generateQueryArraySchemas,
|
generateQueryArraySchemas,
|
||||||
} from "../utils/json"
|
} from "../utils/json"
|
||||||
|
|
||||||
export default class QueryArrayFetch extends FieldFetch {
|
export default class QueryArrayFetch extends FieldFetch<"queryarray"> {
|
||||||
async getDefinition() {
|
async getDefinition() {
|
||||||
const { datasource } = this.options
|
const { datasource } = this.options
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { ExecuteQueryRequest, Query } from "@budibase/types"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
interface QueryDatasource {
|
interface QueryDatasource {
|
||||||
|
type: "query"
|
||||||
_id: string
|
_id: string
|
||||||
fields: Record<string, any> & {
|
fields: Record<string, any> & {
|
||||||
pagination?: {
|
pagination?: {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Table } from "@budibase/types"
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
|
|
||||||
interface RelationshipDatasource {
|
interface RelationshipDatasource {
|
||||||
|
type: "link"
|
||||||
tableId: string
|
tableId: string
|
||||||
rowId: string
|
rowId: string
|
||||||
rowTableId: string
|
rowTableId: string
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
import { SortOrder, Table, UITable } from "@budibase/types"
|
import { SortOrder, Table } from "@budibase/types"
|
||||||
|
|
||||||
export default class TableFetch extends DataFetch<UITable, Table> {
|
interface TableDatasource {
|
||||||
|
type: "table"
|
||||||
|
tableId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TableFetch extends DataFetch<TableDatasource, Table> {
|
||||||
async determineFeatureFlags() {
|
async determineFeatureFlags() {
|
||||||
return {
|
return {
|
||||||
supportsSearch: true,
|
supportsSearch: true,
|
||||||
|
|
|
@ -14,18 +14,22 @@ interface UserFetchQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserDatasource {
|
interface UserDatasource {
|
||||||
tableId: string
|
type: "user"
|
||||||
|
tableId: TableNames.USERS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UserDefinition {}
|
||||||
|
|
||||||
export default class UserFetch extends DataFetch<
|
export default class UserFetch extends DataFetch<
|
||||||
UserDatasource,
|
UserDatasource,
|
||||||
{},
|
UserDefinition,
|
||||||
UserFetchQuery
|
UserFetchQuery
|
||||||
> {
|
> {
|
||||||
constructor(opts: DataFetchParams<UserDatasource, UserFetchQuery>) {
|
constructor(opts: DataFetchParams<UserDatasource, UserFetchQuery>) {
|
||||||
super({
|
super({
|
||||||
...opts,
|
...opts,
|
||||||
datasource: {
|
datasource: {
|
||||||
|
type: "user",
|
||||||
tableId: TableNames.USERS,
|
tableId: TableNames.USERS,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
import { Table, View } from "@budibase/types"
|
import { Table } from "@budibase/types"
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
|
|
||||||
type ViewV1 = View & { name: string }
|
type ViewV1Datasource = {
|
||||||
|
type: "view"
|
||||||
|
name: string
|
||||||
|
tableId: string
|
||||||
|
calculation: string
|
||||||
|
field: string
|
||||||
|
groupBy: string
|
||||||
|
}
|
||||||
|
|
||||||
export default class ViewFetch extends DataFetch<ViewV1, Table> {
|
export default class ViewFetch extends DataFetch<ViewV1Datasource, Table> {
|
||||||
async getDefinition() {
|
async getDefinition() {
|
||||||
const { datasource } = this.options
|
const { datasource } = this.options
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
import { SortOrder, UIView, ViewV2, ViewV2Type } from "@budibase/types"
|
import { SortOrder, ViewV2Enriched, ViewV2Type } from "@budibase/types"
|
||||||
import DataFetch from "./DataFetch"
|
import DataFetch from "./DataFetch"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { helpers } from "@budibase/shared-core"
|
import { helpers } from "@budibase/shared-core"
|
||||||
|
|
||||||
export default class ViewV2Fetch extends DataFetch<UIView, ViewV2> {
|
interface ViewDatasource {
|
||||||
|
type: "viewV2"
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ViewV2Fetch extends DataFetch<
|
||||||
|
ViewDatasource,
|
||||||
|
ViewV2Enriched
|
||||||
|
> {
|
||||||
async determineFeatureFlags() {
|
async determineFeatureFlags() {
|
||||||
return {
|
return {
|
||||||
supportsSearch: true,
|
supportsSearch: true,
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
import TableFetch from "./TableFetch.js"
|
import TableFetch from "./TableFetch"
|
||||||
import ViewFetch from "./ViewFetch.js"
|
import ViewFetch from "./ViewFetch"
|
||||||
import ViewV2Fetch from "./ViewV2Fetch.js"
|
import ViewV2Fetch from "./ViewV2Fetch"
|
||||||
import QueryFetch from "./QueryFetch"
|
import QueryFetch from "./QueryFetch"
|
||||||
import RelationshipFetch from "./RelationshipFetch"
|
import RelationshipFetch from "./RelationshipFetch"
|
||||||
import NestedProviderFetch from "./NestedProviderFetch"
|
import NestedProviderFetch from "./NestedProviderFetch"
|
||||||
import FieldFetch from "./FieldFetch"
|
import FieldFetch from "./FieldFetch"
|
||||||
import JSONArrayFetch from "./JSONArrayFetch"
|
import JSONArrayFetch from "./JSONArrayFetch"
|
||||||
import UserFetch from "./UserFetch.js"
|
import UserFetch from "./UserFetch"
|
||||||
import GroupUserFetch from "./GroupUserFetch"
|
import GroupUserFetch from "./GroupUserFetch"
|
||||||
import CustomFetch from "./CustomFetch"
|
import CustomFetch from "./CustomFetch"
|
||||||
import QueryArrayFetch from "./QueryArrayFetch.js"
|
import QueryArrayFetch from "./QueryArrayFetch"
|
||||||
import { APIClient } from "../api/types.js"
|
import { APIClient } from "../api/types"
|
||||||
|
|
||||||
const DataFetchMap = {
|
export type DataFetchType = keyof typeof DataFetchMap
|
||||||
|
|
||||||
|
export const DataFetchMap = {
|
||||||
table: TableFetch,
|
table: TableFetch,
|
||||||
view: ViewFetch,
|
view: ViewFetch,
|
||||||
viewV2: ViewV2Fetch,
|
viewV2: ViewV2Fetch,
|
||||||
|
@ -24,15 +26,14 @@ const DataFetchMap = {
|
||||||
|
|
||||||
// Client specific datasource types
|
// Client specific datasource types
|
||||||
provider: NestedProviderFetch,
|
provider: NestedProviderFetch,
|
||||||
field: FieldFetch,
|
field: FieldFetch<"field">,
|
||||||
jsonarray: JSONArrayFetch,
|
jsonarray: JSONArrayFetch,
|
||||||
queryarray: QueryArrayFetch,
|
queryarray: QueryArrayFetch,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 = ({ API, datasource, options }: any) => {
|
||||||
const Fetch =
|
const Fetch = DataFetchMap[datasource?.type as DataFetchType] || TableFetch
|
||||||
DataFetchMap[datasource?.type as keyof typeof DataFetchMap] || 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
|
||||||
|
@ -43,29 +44,27 @@ export const fetchData = ({ API, datasource, options }: 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 = <
|
const createEmptyFetchInstance = <TDatasource extends { type: DataFetchType }>({
|
||||||
TDatasource extends {
|
|
||||||
type: keyof typeof DataFetchMap
|
|
||||||
}
|
|
||||||
>({
|
|
||||||
API,
|
API,
|
||||||
datasource,
|
datasource,
|
||||||
}: {
|
}: {
|
||||||
API: APIClient
|
API: APIClient
|
||||||
datasource: TDatasource
|
datasource: TDatasource
|
||||||
}) => {
|
}) => {
|
||||||
const handler = DataFetchMap[datasource?.type as keyof typeof DataFetchMap]
|
const handler = DataFetchMap[datasource?.type as DataFetchType]
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return new handler({ API, datasource: null as any, query: null as any })
|
return new handler({
|
||||||
|
API,
|
||||||
|
datasource: null as never,
|
||||||
|
query: null as any,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
TDatasource extends { type: DataFetchType }
|
||||||
type: keyof typeof DataFetchMap
|
|
||||||
}
|
|
||||||
>({
|
>({
|
||||||
API,
|
API,
|
||||||
datasource,
|
datasource,
|
||||||
|
@ -79,9 +78,7 @@ export const getDatasourceDefinition = async <
|
||||||
|
|
||||||
// Fetches the schema of any type of datasource
|
// Fetches the schema of any type of datasource
|
||||||
export const getDatasourceSchema = <
|
export const getDatasourceSchema = <
|
||||||
TDatasource extends {
|
TDatasource extends { type: DataFetchType }
|
||||||
type: keyof typeof DataFetchMap
|
|
||||||
}
|
|
||||||
>({
|
>({
|
||||||
API,
|
API,
|
||||||
datasource,
|
datasource,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export { createAPIClient } from "./api"
|
export { createAPIClient } from "./api"
|
||||||
export { fetchData } 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"
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
export enum FeatureFlag {
|
export enum FeatureFlag {
|
||||||
USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR",
|
USE_ZOD_VALIDATOR = "USE_ZOD_VALIDATOR",
|
||||||
|
|
||||||
|
// Account-portal
|
||||||
|
DIRECT_LOGIN_TO_ACCOUNT_PORTAL = "DIRECT_LOGIN_TO_ACCOUNT_PORTAL",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeatureFlagDefaults = {
|
export const FeatureFlagDefaults = {
|
||||||
[FeatureFlag.USE_ZOD_VALIDATOR]: false,
|
[FeatureFlag.USE_ZOD_VALIDATOR]: false,
|
||||||
|
|
||||||
|
// Account-portal
|
||||||
|
[FeatureFlag.DIRECT_LOGIN_TO_ACCOUNT_PORTAL]: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FeatureFlags = typeof FeatureFlagDefaults
|
export type FeatureFlags = typeof FeatureFlagDefaults
|
||||||
|
|
Loading…
Reference in New Issue