Add majority of API interactions to SDK
This commit is contained in:
parent
e24a89af56
commit
aa70eab935
|
@ -1,9 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { buildStyle } from "../../helpers.js"
|
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let text = ""
|
export let text = ""
|
||||||
export let icon = ""
|
export let icon = ""
|
||||||
export let onClick = value => {}
|
export let onClick = () => {}
|
||||||
export let selected = false
|
export let selected = false
|
||||||
|
|
||||||
$: useIcon = !!icon
|
$: useIcon = !!icon
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import regexparam from "regexparam"
|
import regexparam from "regexparam"
|
||||||
import appStore from "../state/store"
|
import appStore from "../state/store"
|
||||||
import { getAppId } from "./getAppId"
|
import { getAppId } from "../../../component-sdk/src/utils"
|
||||||
|
|
||||||
export const screenRouter = ({ screens, onScreenSelected, window }) => {
|
export const screenRouter = ({ screens, onScreenSelected, window }) => {
|
||||||
function sanitize(url) {
|
function sanitize(url) {
|
||||||
|
|
|
@ -2,22 +2,36 @@ import { get } from "svelte/store"
|
||||||
import { getAppId } from "../utils"
|
import { getAppId } from "../utils"
|
||||||
import { configStore } from "../store"
|
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
|
const { proto, domain, port } = get(configStore).config
|
||||||
let url = `/${path}`.replace("//", "/")
|
let url = `/${path}`.replace("//", "/")
|
||||||
return domain ? `${proto}://${domain}:${port}${url}` : url
|
return domain ? `${proto}://${domain}:${port}${url}` : url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for API errors.
|
||||||
|
*/
|
||||||
const handleError = error => {
|
const handleError = error => {
|
||||||
store.actions.handleError(error)
|
configStore.actions.handleError(error)
|
||||||
return { 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 {
|
try {
|
||||||
const fullURL = makeURL(url)
|
const response = await fetch(url, {
|
||||||
const response = await fetch(fullURL, {
|
method,
|
||||||
method: method,
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"x-budibase-app-id": getAppId(window.document.cookie),
|
"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 {
|
export default {
|
||||||
post: apiCall("POST"),
|
post: requestApiCall("POST"),
|
||||||
get: apiCall("GET"),
|
get: requestApiCall("GET"),
|
||||||
patch: apiCall("PATCH"),
|
patch: requestApiCall("PATCH"),
|
||||||
del: apiCall("DELETE"),
|
del: requestApiCall("DELETE"),
|
||||||
error: handleError,
|
error: handleError,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,6 @@ import api from "./api"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a log in request.
|
* Performs a log in request.
|
||||||
*
|
|
||||||
* @param username
|
|
||||||
* @param password
|
|
||||||
* @returns {Promise<{error: *}|any|{error: *}>}
|
|
||||||
*/
|
*/
|
||||||
export const logIn = async ({ username, password }) => {
|
export const logIn = async ({ username, password }) => {
|
||||||
if (!username) {
|
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.
|
* Creates a row in a table.
|
||||||
*
|
|
||||||
* @param params
|
|
||||||
* @param state
|
|
||||||
* @returns {Promise<any|{error: *}>}
|
|
||||||
*/
|
*/
|
||||||
export const saveRow = async (params, state) => {
|
export const saveRow = async (params, state) => {
|
||||||
return await api.post({
|
return await api.post({
|
||||||
|
@ -16,10 +12,6 @@ export const saveRow = async (params, state) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a row in a table.
|
* Updates a row in a table.
|
||||||
*
|
|
||||||
* @param params
|
|
||||||
* @param state
|
|
||||||
* @returns {Promise<any|{error: *}>}
|
|
||||||
*/
|
*/
|
||||||
export const updateRow = async (params, state) => {
|
export const updateRow = async (params, state) => {
|
||||||
const row = makeRowRequestBody(params, state)
|
const row = makeRowRequestBody(params, state)
|
||||||
|
@ -32,11 +24,6 @@ export const updateRow = async (params, state) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a row from a table.
|
* Deletes a row from a table.
|
||||||
*
|
|
||||||
* @param tableId
|
|
||||||
* @param rowId
|
|
||||||
* @param revId
|
|
||||||
* @returns {Promise<any|{error: *}>}
|
|
||||||
*/
|
*/
|
||||||
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
||||||
return await api.del({
|
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) => {
|
const makeRowRequestBody = (parameters, state) => {
|
||||||
// start with the row thats currently in context
|
// start with the row thats currently in context
|
||||||
const body = { ...(state.data || {}) }
|
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.
|
* Logs a user in.
|
||||||
*
|
|
||||||
* @param username
|
|
||||||
* @param password
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
*/
|
||||||
const logIn = async ({ username, password }) => {
|
const logIn = async ({ username, password }) => {
|
||||||
const user = await api.logIn({ username, password })
|
const user = await api.logIn({ username, password })
|
||||||
|
|
|
@ -12,8 +12,6 @@ export const createConfigStore = () => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the SDK configuration.
|
* Sets the SDK configuration.
|
||||||
*
|
|
||||||
* @param config
|
|
||||||
*/
|
*/
|
||||||
const initialise = config => {
|
const initialise = config => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
|
@ -33,8 +31,6 @@ export const createConfigStore = () => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store handler for errors which triggers the user defined error handler.
|
* Store handler for errors which triggers the user defined error handler.
|
||||||
*
|
|
||||||
* @param error
|
|
||||||
*/
|
*/
|
||||||
const handleError = error => {
|
const handleError = error => {
|
||||||
const handler = get(store).onError
|
const handler = get(store).onError
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { cssVars, createClasses } from "./cssVars"
|
import { cssVars } from "./helpers"
|
||||||
|
|
||||||
export const className = ""
|
export const className = ""
|
||||||
export let imageUrl = ""
|
export let imageUrl = ""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { cssVars, createClasses } from "./cssVars"
|
import { cssVars } from "./helpers"
|
||||||
|
|
||||||
export const className = ""
|
export const className = ""
|
||||||
export let imageUrl = ""
|
export let imageUrl = ""
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import { cssVars, createClasses } from "./cssVars"
|
|
||||||
|
|
||||||
export let className = ""
|
export let className = ""
|
||||||
export let onLoad
|
export let onLoad
|
||||||
export let type = "div"
|
export let type = "div"
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import { cssVars, createClasses } from "./cssVars"
|
|
||||||
|
|
||||||
export let url = ""
|
export let url = ""
|
||||||
export let text = ""
|
export let text = ""
|
||||||
export let openInNewTab = false
|
export let openInNewTab = false
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import Button from "./Button.svelte"
|
|
||||||
|
|
||||||
export let buttonText = "Log In"
|
export let buttonText = "Log In"
|
||||||
export let logo = ""
|
export let logo = ""
|
||||||
export let title = ""
|
export let title = ""
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import cssVars from "./cssVars"
|
import { cssVars } from "./helpers"
|
||||||
|
|
||||||
export let navBarBackground = ""
|
export let navBarBackground = ""
|
||||||
export let navBarBorder = ""
|
export let navBarBorder = ""
|
||||||
|
|
Loading…
Reference in New Issue