Add majority of API interactions to SDK
This commit is contained in:
parent
921cae0cbc
commit
299dcbab3d
|
@ -1,9 +1,8 @@
|
|||
<script>
|
||||
import { buildStyle } from "../../helpers.js"
|
||||
export let value = ""
|
||||
export let text = ""
|
||||
export let icon = ""
|
||||
export let onClick = value => {}
|
||||
export let onClick = () => {}
|
||||
export let selected = false
|
||||
|
||||
$: useIcon = !!icon
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import regexparam from "regexparam"
|
||||
import appStore from "../state/store"
|
||||
import { getAppId } from "./getAppId"
|
||||
import { getAppId } from "../../../component-sdk/src/utils"
|
||||
|
||||
export const screenRouter = ({ screens, onScreenSelected, window }) => {
|
||||
function sanitize(url) {
|
||||
|
|
|
@ -2,22 +2,36 @@ import { get } from "svelte/store"
|
|||
import { getAppId } from "../utils"
|
||||
import { configStore } from "../store"
|
||||
|
||||
const makeURL = path => {
|
||||
/**
|
||||
* API cache for cached request responses.
|
||||
*/
|
||||
let cache = {}
|
||||
|
||||
/**
|
||||
* Makes a fully formatted URL based on the SDK configuration.
|
||||
*/
|
||||
const makeFullURL = path => {
|
||||
const { proto, domain, port } = get(configStore).config
|
||||
let url = `/${path}`.replace("//", "/")
|
||||
return domain ? `${proto}://${domain}:${port}${url}` : url
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for API errors.
|
||||
*/
|
||||
const handleError = error => {
|
||||
store.actions.handleError(error)
|
||||
configStore.actions.handleError(error)
|
||||
return { error }
|
||||
}
|
||||
|
||||
const apiCall = method => async ({ url, body }) => {
|
||||
/**
|
||||
* Performs an API call to the server.
|
||||
* App ID header is always correctly set.
|
||||
*/
|
||||
const makeApiCall = async ({ method, url, body }) => {
|
||||
try {
|
||||
const fullURL = makeURL(url)
|
||||
const response = await fetch(fullURL, {
|
||||
method: method,
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-budibase-app-id": getAppId(window.document.cookie),
|
||||
|
@ -45,10 +59,36 @@ const apiCall = method => async ({ url, body }) => {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an API call to the server and caches the response.
|
||||
* Future invocation for this URL will return the cached result instead of
|
||||
* hitting the server again.
|
||||
*/
|
||||
const makeCachedApiCall = async params => {
|
||||
const identifier = params.url
|
||||
if (!identifier) {
|
||||
return null
|
||||
}
|
||||
if (!cache[identifier]) {
|
||||
cache[identifier] = makeApiCall(params)
|
||||
cache[identifier] = await cache[identifier]
|
||||
}
|
||||
return await cache[identifier]
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an API call function for a particular HTTP method.
|
||||
*/
|
||||
const requestApiCall = method => async ({ url, body, cache = false }) => {
|
||||
const fullURL = makeFullURL(url)
|
||||
const params = { method, url: fullURL, body }
|
||||
return await (cache ? makeCachedApiCall : makeApiCall)(params)
|
||||
}
|
||||
|
||||
export default {
|
||||
post: apiCall("POST"),
|
||||
get: apiCall("GET"),
|
||||
patch: apiCall("PATCH"),
|
||||
del: apiCall("DELETE"),
|
||||
post: requestApiCall("POST"),
|
||||
get: requestApiCall("GET"),
|
||||
patch: requestApiCall("PATCH"),
|
||||
del: requestApiCall("DELETE"),
|
||||
error: handleError,
|
||||
}
|
||||
|
|
|
@ -2,10 +2,6 @@ import api from "./api"
|
|||
|
||||
/**
|
||||
* Performs a log in request.
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
* @returns {Promise<{error: *}|any|{error: *}>}
|
||||
*/
|
||||
export const logIn = async ({ username, password }) => {
|
||||
if (!username) {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { fetchTableData, fetchTableDefinition } from "./tables"
|
||||
import { fetchViewData } from "./views"
|
||||
import { fetchRelationshipData } from "./relationships"
|
||||
|
||||
/**
|
||||
* Fetches all rows for a particular Budibase data source.
|
||||
*/
|
||||
export const fetchDatasource = async datasource => {
|
||||
if (!datasource || !datasource.name) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Fetch all rows in data source
|
||||
const { type, name, tableId } = datasource
|
||||
let rows = []
|
||||
if (type === "table") {
|
||||
rows = await fetchTableData(name)
|
||||
} else if (type === "view") {
|
||||
rows = await fetchViewData(datasource)
|
||||
} else if (type === "link") {
|
||||
rows = await fetchRelationshipData(tableId)
|
||||
}
|
||||
|
||||
// Enrich rows
|
||||
return await enrichDatasourceRows(rows, tableId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriches data source rows which contain certain field types so that they can
|
||||
* be properly displayed.
|
||||
*/
|
||||
const enrichDatasourceRows = async (rows, tableId) => {
|
||||
if (rows && rows.length && tableId) {
|
||||
// Fetch table schema so we can check column types
|
||||
const tableDefinition = await fetchTableDefinition(tableId)
|
||||
const schema = tableDefinition && tableDefinition.schema
|
||||
if (schema) {
|
||||
const keys = Object.keys(schema)
|
||||
rows.forEach(row => {
|
||||
for (let key of keys) {
|
||||
const type = schema[key].type
|
||||
if (type === "link") {
|
||||
// Enrich row with the count of any relationship fields
|
||||
row[`${key}_count`] = Array.isArray(row[key]) ? row[key].length : 0
|
||||
} else if (type === "attachment") {
|
||||
// Enrich row with the first image URL for any attachment fields
|
||||
let url = null
|
||||
if (Array.isArray(row[key]) && row[key][0] != null) {
|
||||
url = row[key][0].url
|
||||
}
|
||||
row[`${key}_first`] = url
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import api from "./api"
|
||||
|
||||
/**
|
||||
* Fetches related rows for a certain field of a certain row.
|
||||
*/
|
||||
export const fetchRelationshipData = async ({ tableId, rowId, fieldName }) => {
|
||||
if (!tableId || !rowId) {
|
||||
return []
|
||||
}
|
||||
const response = await api.get({ url: `/api/${tableId}/${rowId}/enrich` })
|
||||
return response[fieldName] || []
|
||||
}
|
|
@ -2,10 +2,6 @@ import api from "./api"
|
|||
|
||||
/**
|
||||
* Creates a row in a table.
|
||||
*
|
||||
* @param params
|
||||
* @param state
|
||||
* @returns {Promise<any|{error: *}>}
|
||||
*/
|
||||
export const saveRow = async (params, state) => {
|
||||
return await api.post({
|
||||
|
@ -16,10 +12,6 @@ export const saveRow = async (params, state) => {
|
|||
|
||||
/**
|
||||
* Updates a row in a table.
|
||||
*
|
||||
* @param params
|
||||
* @param state
|
||||
* @returns {Promise<any|{error: *}>}
|
||||
*/
|
||||
export const updateRow = async (params, state) => {
|
||||
const row = makeRowRequestBody(params, state)
|
||||
|
@ -32,11 +24,6 @@ export const updateRow = async (params, state) => {
|
|||
|
||||
/**
|
||||
* Deletes a row from a table.
|
||||
*
|
||||
* @param tableId
|
||||
* @param rowId
|
||||
* @param revId
|
||||
* @returns {Promise<any|{error: *}>}
|
||||
*/
|
||||
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
||||
return await api.del({
|
||||
|
@ -44,6 +31,9 @@ export const deleteRow = async ({ tableId, rowId, revId }) => {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitises and parses column types when saving and updating rows.
|
||||
*/
|
||||
const makeRowRequestBody = (parameters, state) => {
|
||||
// start with the row thats currently in context
|
||||
const body = { ...(state.data || {}) }
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import api from "./api"
|
||||
|
||||
/**
|
||||
* Fetches a table definition.
|
||||
* Since definitions cannot change at runtime, the result is cached.
|
||||
*/
|
||||
export const fetchTableDefinition = async tableId => {
|
||||
return await api.get({ url: `/api/tables/${tableId}`, cache: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all rows from a table.
|
||||
*/
|
||||
export const fetchTableData = async name => {
|
||||
return await api.get({ url: `/api/views/${name}` })
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import api from "./api"
|
||||
|
||||
/**
|
||||
* Fetches all rows in a view.
|
||||
*/
|
||||
export const fetchViewData = async ({ name, field, groupBy, calculation }) => {
|
||||
const params = new URLSearchParams()
|
||||
|
||||
if (calculation) {
|
||||
params.set("field", field)
|
||||
params.set("calculation", calculation)
|
||||
}
|
||||
if (groupBy) {
|
||||
params.set("group", groupBy)
|
||||
}
|
||||
|
||||
const QUERY_VIEW_URL = field
|
||||
? `/api/views/${name}?${params}`
|
||||
: `/api/views/${name}`
|
||||
|
||||
return await api.get({ url: QUERY_VIEW_URL })
|
||||
}
|
|
@ -10,10 +10,6 @@ export const createAuthStore = () => {
|
|||
|
||||
/**
|
||||
* Logs a user in.
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const logIn = async ({ username, password }) => {
|
||||
const user = await api.logIn({ username, password })
|
||||
|
|
|
@ -12,8 +12,6 @@ export const createConfigStore = () => {
|
|||
|
||||
/**
|
||||
* Sets the SDK configuration.
|
||||
*
|
||||
* @param config
|
||||
*/
|
||||
const initialise = config => {
|
||||
store.update(state => {
|
||||
|
@ -33,8 +31,6 @@ export const createConfigStore = () => {
|
|||
|
||||
/**
|
||||
* Store handler for errors which triggers the user defined error handler.
|
||||
*
|
||||
* @param error
|
||||
*/
|
||||
const handleError = error => {
|
||||
const handler = get(store).onError
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cssVars, createClasses } from "./cssVars"
|
||||
import { cssVars } from "./helpers"
|
||||
|
||||
export const className = ""
|
||||
export let imageUrl = ""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cssVars, createClasses } from "./cssVars"
|
||||
import { cssVars } from "./helpers"
|
||||
|
||||
export const className = ""
|
||||
export let imageUrl = ""
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script>
|
||||
import { cssVars, createClasses } from "./cssVars"
|
||||
|
||||
export let className = ""
|
||||
export let onLoad
|
||||
export let type = "div"
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script>
|
||||
import { cssVars, createClasses } from "./cssVars"
|
||||
|
||||
export let url = ""
|
||||
export let text = ""
|
||||
export let openInNewTab = false
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
<script>
|
||||
import Button from "./Button.svelte"
|
||||
|
||||
export let buttonText = "Log In"
|
||||
export let logo = ""
|
||||
export let title = ""
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import cssVars from "./cssVars"
|
||||
import { cssVars } from "./helpers"
|
||||
|
||||
export let navBarBackground = ""
|
||||
export let navBarBorder = ""
|
||||
|
|
Loading…
Reference in New Issue