Update client lib data fetch models to support generic pagination
This commit is contained in:
parent
882067056c
commit
441cc2fccf
|
@ -4,7 +4,7 @@ import API from "./api"
|
||||||
/**
|
/**
|
||||||
* Executes a query against an external data connector.
|
* Executes a query against an external data connector.
|
||||||
*/
|
*/
|
||||||
export const executeQuery = async ({ queryId, parameters }) => {
|
export const executeQuery = async ({ queryId, pagination, parameters }) => {
|
||||||
const query = await fetchQueryDefinition(queryId)
|
const query = await fetchQueryDefinition(queryId)
|
||||||
if (query?.datasourceId == null) {
|
if (query?.datasourceId == null) {
|
||||||
notificationStore.actions.error("That query couldn't be found")
|
notificationStore.actions.error("That query couldn't be found")
|
||||||
|
@ -14,6 +14,7 @@ export const executeQuery = async ({ queryId, parameters }) => {
|
||||||
url: `/api/v2/queries/${queryId}`,
|
url: `/api/v2/queries/${queryId}`,
|
||||||
body: {
|
body: {
|
||||||
parameters,
|
parameters,
|
||||||
|
pagination,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { fetchTableDefinition } from "api"
|
||||||
* 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 class TableFetch {
|
export default class DataFetch {
|
||||||
// Feature flags
|
// Feature flags
|
||||||
supportsSearch = false
|
supportsSearch = false
|
||||||
supportsSort = false
|
supportsSort = false
|
||||||
|
@ -21,7 +21,6 @@ export default class TableFetch {
|
||||||
// Config
|
// Config
|
||||||
options = {
|
options = {
|
||||||
datasource: null,
|
datasource: null,
|
||||||
schema: null,
|
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
|
||||||
// Search config
|
// Search config
|
||||||
|
@ -53,23 +52,20 @@ export default class TableFetch {
|
||||||
/**
|
/**
|
||||||
* Constructs a new DataFetch instance.
|
* Constructs a new DataFetch instance.
|
||||||
* @param opts the fetch options
|
* @param opts the fetch options
|
||||||
* @param flags the datasource feature flags
|
|
||||||
*/
|
*/
|
||||||
constructor(opts, flags) {
|
constructor(opts) {
|
||||||
// Merge options with their default values
|
// Merge options with their default values
|
||||||
this.options = {
|
this.options = {
|
||||||
...this.options,
|
...this.options,
|
||||||
...opts,
|
...opts,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update feature flags
|
|
||||||
this.supportsSearch = flags?.supportsSearch || false
|
|
||||||
this.supportsSort = flags?.supportsSort || false
|
|
||||||
this.supportsPagination = flags?.supportsPagination || false
|
|
||||||
|
|
||||||
// Bind all functions to properly scope "this"
|
// Bind all functions to properly scope "this"
|
||||||
this.getData = this.getData.bind(this)
|
this.getData = this.getData.bind(this)
|
||||||
|
this.getPage = this.getPage.bind(this)
|
||||||
this.getInitialData = this.getInitialData.bind(this)
|
this.getInitialData = this.getInitialData.bind(this)
|
||||||
|
this.determineFeatureFlags = this.determineFeatureFlags.bind(this)
|
||||||
|
this.enrichSchema = this.enrichSchema.bind(this)
|
||||||
this.refresh = this.refresh.bind(this)
|
this.refresh = this.refresh.bind(this)
|
||||||
this.update = this.update.bind(this)
|
this.update = this.update.bind(this)
|
||||||
this.hasNextPage = this.hasNextPage.bind(this)
|
this.hasNextPage = this.hasNextPage.bind(this)
|
||||||
|
@ -116,11 +112,13 @@ export default class TableFetch {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure schema exists and enrich it
|
// Fetch datasource definition and determine feature flags
|
||||||
let schema = this.options.schema
|
const definition = await this.constructor.getDefinition(datasource)
|
||||||
if (!schema) {
|
this.determineFeatureFlags(definition)
|
||||||
schema = await this.constructor.getSchema(datasource)
|
|
||||||
}
|
// Fetch and enrich schema
|
||||||
|
let schema = this.constructor.getSchema(datasource, definition)
|
||||||
|
schema = this.enrichSchema(schema)
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -142,13 +140,16 @@ export default class TableFetch {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update store
|
// Update store
|
||||||
this.store.update($store => ({ ...$store, schema, query, loading: true }))
|
this.store.update($store => ({
|
||||||
|
...$store,
|
||||||
|
definition,
|
||||||
|
schema,
|
||||||
|
query,
|
||||||
|
loading: true,
|
||||||
|
}))
|
||||||
|
|
||||||
// Actually fetch data
|
// Actually fetch data
|
||||||
const page = await this.getPage()
|
const page = await this.getPage()
|
||||||
if (page.info) {
|
|
||||||
console.log("new info", page.info)
|
|
||||||
}
|
|
||||||
this.store.update($store => ({
|
this.store.update($store => ({
|
||||||
...$store,
|
...$store,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -165,7 +166,7 @@ export default class TableFetch {
|
||||||
*/
|
*/
|
||||||
async getPage() {
|
async getPage() {
|
||||||
const { sortColumn, sortOrder, sortType, limit } = this.options
|
const { sortColumn, sortOrder, sortType, limit } = this.options
|
||||||
const { query } = get(this.store)
|
const { query, pageNumber } = get(this.store)
|
||||||
|
|
||||||
// Get the actual data
|
// Get the actual data
|
||||||
let { rows, info, hasNextPage, cursor } = await this.getData()
|
let { rows, info, hasNextPage, cursor } = await this.getData()
|
||||||
|
@ -182,7 +183,8 @@ export default class TableFetch {
|
||||||
|
|
||||||
// If we don't support pagination, do a client-side limit
|
// If we don't support pagination, do a client-side limit
|
||||||
if (!this.supportsPagination) {
|
if (!this.supportsPagination) {
|
||||||
rows = luceneLimit(rows, limit)
|
rows = rows.slice(pageNumber * limit, (pageNumber + 1) * limit)
|
||||||
|
// rows = luceneLimit(rows, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -207,17 +209,27 @@ export default class TableFetch {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the schema definition for a datasource.
|
* Gets the definition for this datasource.
|
||||||
* Defaults to fetching a table definition.
|
* Defaults to fetching a table definition.
|
||||||
* @param datasource the datasource definition
|
* @param datasource
|
||||||
* @return {object} the schema
|
* @return {object} the definition
|
||||||
*/
|
*/
|
||||||
static async getSchema(datasource) {
|
static async getDefinition(datasource) {
|
||||||
if (!datasource?.tableId) {
|
if (!datasource?.tableId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const table = await fetchTableDefinition(datasource.tableId)
|
return await fetchTableDefinition(datasource.tableId)
|
||||||
return this.enrichSchema(table?.schema)
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the schema definition for a datasource.
|
||||||
|
* Defaults to getting the "schema" property of the definition.
|
||||||
|
* @param datasource the datasource
|
||||||
|
* @param definition the datasource definition
|
||||||
|
* @return {object} the schema
|
||||||
|
*/
|
||||||
|
static getSchema(datasource, definition) {
|
||||||
|
return definition?.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,7 +237,7 @@ export default class TableFetch {
|
||||||
* @param schema the datasource schema
|
* @param schema the datasource schema
|
||||||
* @return {object} the enriched datasource schema
|
* @return {object} the enriched datasource schema
|
||||||
*/
|
*/
|
||||||
static enrichSchema(schema) {
|
enrichSchema(schema) {
|
||||||
if (schema == null) {
|
if (schema == null) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -246,6 +258,17 @@ export default class TableFetch {
|
||||||
return enrichedSchema
|
return enrichedSchema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the feature flag for this datasource definition
|
||||||
|
* @param definition
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
determineFeatureFlags(definition) {
|
||||||
|
this.supportsSearch = false
|
||||||
|
this.supportsSort = false
|
||||||
|
this.supportsPagination = false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the data set and updates options
|
* Resets the data set and updates options
|
||||||
* @param newOptions any new options
|
* @param newOptions any new options
|
||||||
|
@ -319,6 +342,7 @@ export default class TableFetch {
|
||||||
...$store,
|
...$store,
|
||||||
loading: true,
|
loading: true,
|
||||||
cursor: nextCursor,
|
cursor: nextCursor,
|
||||||
|
pageNumber: $store.pageNumber + 1,
|
||||||
}))
|
}))
|
||||||
const { rows, info, hasNextPage, cursor } = await this.getPage()
|
const { rows, info, hasNextPage, cursor } = await this.getPage()
|
||||||
|
|
||||||
|
@ -326,11 +350,10 @@ export default class TableFetch {
|
||||||
this.store.update($store => {
|
this.store.update($store => {
|
||||||
let { cursors, pageNumber } = $store
|
let { cursors, pageNumber } = $store
|
||||||
if (hasNextPage) {
|
if (hasNextPage) {
|
||||||
cursors[pageNumber + 2] = cursor
|
cursors[pageNumber + 1] = cursor
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...$store,
|
...$store,
|
||||||
pageNumber: pageNumber + 1,
|
|
||||||
rows,
|
rows,
|
||||||
info,
|
info,
|
||||||
cursors,
|
cursors,
|
||||||
|
@ -354,6 +377,7 @@ export default class TableFetch {
|
||||||
...$store,
|
...$store,
|
||||||
loading: true,
|
loading: true,
|
||||||
cursor: prevCursor,
|
cursor: prevCursor,
|
||||||
|
pageNumber: $store.pageNumber - 1,
|
||||||
}))
|
}))
|
||||||
const { rows, info } = await this.getPage()
|
const { rows, info } = await this.getPage()
|
||||||
|
|
||||||
|
@ -361,7 +385,6 @@ export default class TableFetch {
|
||||||
this.store.update($store => {
|
this.store.update($store => {
|
||||||
return {
|
return {
|
||||||
...$store,
|
...$store,
|
||||||
pageNumber: $store.pageNumber - 1,
|
|
||||||
rows,
|
rows,
|
||||||
info,
|
info,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
import DataFetch from "./DataFetch.js"
|
import DataFetch from "./DataFetch.js"
|
||||||
import { executeQuery, fetchQueryDefinition } from "api"
|
import { executeQuery, fetchQueryDefinition } from "api"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
|
||||||
export default class ViewFetch extends DataFetch {
|
export default class QueryFetch extends DataFetch {
|
||||||
static async getSchema(datasource) {
|
determineFeatureFlags(definition) {
|
||||||
|
console.log("pagination config", definition?.pagination)
|
||||||
|
this.supportsPagination =
|
||||||
|
definition?.fields?.pagination?.type != null &&
|
||||||
|
definition?.fields?.pagination?.pageParam != null
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getDefinition(datasource) {
|
||||||
if (!datasource?._id) {
|
if (!datasource?._id) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const definition = await fetchQueryDefinition(datasource._id)
|
return await fetchQueryDefinition(datasource._id)
|
||||||
return this.enrichSchema(definition?.schema)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
const { datasource } = this.options
|
const { datasource, limit } = this.options
|
||||||
|
|
||||||
// Set the default query params
|
// Set the default query params
|
||||||
let parameters = cloneDeep(datasource?.queryParams || {})
|
let parameters = cloneDeep(datasource?.queryParams || {})
|
||||||
|
@ -22,13 +29,21 @@ export default class ViewFetch extends DataFetch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, ...rest } = await executeQuery({
|
// Add pagination to query if supported
|
||||||
queryId: datasource?._id,
|
let queryPayload = { queryId: datasource?._id, parameters }
|
||||||
parameters,
|
if (this.supportsPagination) {
|
||||||
})
|
const { cursor, definition, pageNumber } = get(this.store)
|
||||||
|
const { type } = definition.fields.pagination.type
|
||||||
|
const page = type === "page" ? pageNumber : cursor
|
||||||
|
queryPayload.pagination = { page, limit }
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, pagination, ...rest } = await executeQuery(queryPayload)
|
||||||
return {
|
return {
|
||||||
rows: data || [],
|
rows: data || [],
|
||||||
info: rest,
|
info: rest,
|
||||||
|
cursor: pagination?.page,
|
||||||
|
hasNextPage: data?.length === limit && limit > 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import DataFetch from "./DataFetch.js"
|
import DataFetch from "./DataFetch.js"
|
||||||
import { fetchRelationshipData } from "api"
|
import { fetchRelationshipData } from "api"
|
||||||
|
|
||||||
export default class ViewFetch extends DataFetch {
|
export default class RelationshipFetch extends DataFetch {
|
||||||
async getData() {
|
async getData() {
|
||||||
const { datasource } = this.options
|
const { datasource } = this.options
|
||||||
const res = await fetchRelationshipData({
|
const res = await fetchRelationshipData({
|
||||||
|
|
|
@ -3,12 +3,10 @@ import DataFetch from "./DataFetch.js"
|
||||||
import { searchTable } from "api"
|
import { searchTable } from "api"
|
||||||
|
|
||||||
export default class TableFetch extends DataFetch {
|
export default class TableFetch extends DataFetch {
|
||||||
constructor(opts) {
|
determineFeatureFlags() {
|
||||||
super(opts, {
|
this.supportsSearch = true
|
||||||
supportsSearch: true,
|
this.supportsSort = true
|
||||||
supportsSort: true,
|
this.supportsPagination = true
|
||||||
supportsPagination: true,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import DataFetch from "./DataFetch.js"
|
import DataFetch from "./DataFetch.js"
|
||||||
import { fetchTableDefinition, fetchViewData } from "api"
|
import { fetchViewData } from "api"
|
||||||
|
|
||||||
export default class ViewFetch extends DataFetch {
|
export default class ViewFetch extends DataFetch {
|
||||||
static async getSchema(datasource) {
|
static async getSchema(datasource, definition) {
|
||||||
if (!datasource?.tableId) {
|
const schema = definition?.views?.[datasource.name]?.schema
|
||||||
return null
|
console.log(schema)
|
||||||
}
|
return schema
|
||||||
const table = await fetchTableDefinition(datasource.tableId)
|
|
||||||
return this.enrichSchema(table?.views?.[datasource.name]?.schema)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
const { datasource } = this.options
|
const { datasource } = this.options
|
||||||
const res = await fetchViewData(datasource)
|
const res = await fetchViewData(datasource)
|
||||||
|
console.log(res)
|
||||||
return {
|
return {
|
||||||
rows: res || [],
|
rows: res || [],
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,14 +35,15 @@ export const fetchDatasourceSchema = async datasource => {
|
||||||
|
|
||||||
// All normal datasource schema can use their corresponsing implementations
|
// All normal datasource schema can use their corresponsing implementations
|
||||||
// in the data fetch classes
|
// in the data fetch classes
|
||||||
if (type === "table" || type === "link") {
|
const handler = {
|
||||||
return TableFetch.getSchema(datasource)
|
table: TableFetch,
|
||||||
}
|
link: TableFetch,
|
||||||
if (type === "view") {
|
view: ViewFetch,
|
||||||
return ViewFetch.getSchema(datasource)
|
query: QueryFetch,
|
||||||
}
|
}[type]
|
||||||
if (type === "query") {
|
if (handler) {
|
||||||
return QueryFetch.getSchema(datasource)
|
const definition = await handler.getDefinition(datasource)
|
||||||
|
return handler.getSchema(datasource, definition)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
Loading…
Reference in New Issue