Add relationships as data source

This commit is contained in:
Andrew Kingston 2020-10-09 12:24:18 +01:00
parent f3d0104f41
commit d1f367ccff
8 changed files with 103 additions and 34 deletions

View File

@ -73,18 +73,32 @@ const componentInstanceToBindable = walkResult => i => {
const contextToBindables = (models, walkResult) => context => {
const contextParentPath = getParentPath(walkResult, context)
const isModel = context.model?.isModel || typeof context.model === "string"
const modelId =
typeof context.model === "string" ? context.model : context.model.modelId
const model = models.find(model => model._id === modelId)
let model, schema
if (typeof context.model === "string" || context.model.type === "model") {
const modelId =
typeof context.model === "string" ? context.model : context.model.modelId
model = models.find(model => model._id === modelId)
schema = model?.schema
} else if (context.model.type === "view") {
const modelId = context.model.modelId
model = models.find(model => model._id === modelId)
schema = model?.views?.[context.model.name]?.schema
} else if (context.model.type === "link") {
console.log(context.model)
const modelId = context.model.modelId
model = models.find(model => model._id === modelId)
schema = model?.schema
}
// Avoid crashing whenever no data source has been selected
if (model == null) {
if (!schema) {
return []
}
const newBindable = key => ({
const newBindable = ([key, fieldSchema]) => ({
type: "context",
fieldSchema,
instance: context.instance,
// how the binding expression persists, and is used in the app at runtime
runtimeBinding: `${contextParentPath}data.${key}`,
@ -92,15 +106,11 @@ const contextToBindables = (models, walkResult) => context => {
readableBinding: `${context.instance._instanceName}.${model.name}.${key}`,
})
// see ModelViewSelect.svelte for the format of context.model
// ... this allows us to bind to Model schemas, or View schemas
const schema = isModel ? model.schema : model.views[context.model.name].schema
return (
Object.keys(schema)
Object.entries(schema)
.map(newBindable)
// add _id and _rev fields - not part of schema, but always valid
.concat([newBindable("_id"), newBindable("_rev")])
.concat([newBindable(["_id", "string"]), newBindable(["_rev", "string"])])
)
}

View File

@ -15,10 +15,12 @@
? models.find(m => m._id === componentInstance.datasource.modelId)
: null
$: type = componentInstance.datasource.type
$: if (model) {
options = componentInstance.datasource.isModel
? Object.keys(model.schema)
: Object.keys(model.views[componentInstance.datasource.name].schema)
options =
type === "model" || type === "link"
? Object.keys(model.schema)
: Object.keys(model.views[componentInstance.datasource.name].schema)
}
</script>

View File

@ -1,7 +1,8 @@
<script>
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { backendUiStore } from "builderStore"
import { store, backendUiStore } from "builderStore"
import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
const dispatch = createEventDispatcher()
let anchorRight, dropdownRight
@ -13,21 +14,39 @@
dropdownRight.hide()
}
const models = $backendUiStore.models.map(m => ({
$: models = $backendUiStore.models.map(m => ({
label: m.name,
name: `all_${m._id}`,
modelId: m._id,
isModel: true,
type: "model",
}))
const views = $backendUiStore.models.reduce((acc, cur) => {
$: views = $backendUiStore.models.reduce((acc, cur) => {
let viewsArr = Object.entries(cur.views).map(([key, value]) => ({
label: key,
name: key,
...value,
type: "view",
}))
return [...acc, ...viewsArr]
}, [])
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.currentComponentInfo._id,
components: $store.components,
screen: $store.currentPreviewItem,
models: $backendUiStore.models,
})
$: links = bindableProperties
.filter(x => x.fieldSchema.type === "link")
.map(property => ({
label: property.readableBinding,
fieldName: property.fieldSchema.name,
name: `all_${property.fieldSchema.modelId}`,
modelId: property.fieldSchema.modelId,
type: "link",
}))
</script>
<div class="dropdownbutton" bind:this={anchorRight}>
@ -63,6 +82,19 @@
</li>
{/each}
</ul>
<hr />
<div class="title">
<Heading extraSmall>Relationships</Heading>
</div>
<ul>
{#each links as link}
<li
class:selected={value === link}
on:click={() => handleSelected(link)}>
{link.label}
</li>
{/each}
</ul>
</div>
</DropdownMenu>

View File

@ -1,5 +1,5 @@
const fs = require("fs")
const sharp = require("sharp")
// const sharp = require("sharp")
const fsPromises = fs.promises
const FORMATS = {
@ -7,14 +7,14 @@ const FORMATS = {
}
async function processImage(file) {
const imgMeta = await sharp(file.path)
.resize(300)
.toFile(file.outputPath)
return {
...file,
...imgMeta,
}
// const imgMeta = await sharp(file.path)
// .resize(300)
// .toFile(file.outputPath)
//
// return {
// ...file,
// ...imgMeta,
// }
}
async function process(file) {

View File

@ -39,7 +39,7 @@
onMount(async () => {
if (!isEmpty(datasource)) {
data = await fetchData(datasource)
data = await fetchData(datasource, _bb)
if (data && data.length) {
await fetchModel(data[0].modelId)
headers = Object.keys(schema).filter(shouldDisplayField)
@ -99,7 +99,7 @@
{#if schema[header].type === 'attachment'}
<AttachmentList files={row[header]} />
{:else if schema[header].type === 'link'}
<td>{row[header] ? row[header].length : 0} related row(s)</td>
<td>{row[header]} related row(s)</td>
{:else if row[header]}
<td>{row[header]}</td>
{/if}

View File

@ -10,7 +10,7 @@
onMount(async () => {
if (!isEmpty(datasource)) {
const data = await fetchData(datasource)
const data = await fetchData(datasource, _bb)
_bb.attachChildren(target, {
hydrate: false,
context: data,

View File

@ -26,6 +26,10 @@
async function fetchData() {
const pathParts = window.location.pathname.split("/")
if (!model) {
return
}
let record
// if srcdoc, then we assume this is the builder preview
if (pathParts.length === 0 || pathParts[0] === "srcdoc") {

View File

@ -1,10 +1,17 @@
import api from "./api"
export default async function fetchData(datasource) {
const { isModel, name } = datasource
export default async function fetchData(datasource, _bb) {
const { type, name } = datasource
if (name) {
const records = isModel ? await fetchModelData() : await fetchViewData()
let records
if (type === "model") {
records = await fetchModelData()
} else if (type === "view") {
records = await fetchViewData()
} else if (type === "link") {
records = await fetchLinkedRecordsData()
}
// Fetch model schema so we can check for linked records
if (records && records.length) {
@ -53,4 +60,18 @@ export default async function fetchData(datasource) {
const response = await api.get(QUERY_VIEW_URL)
return await response.json()
}
async function fetchLinkedRecordsData() {
if (
!_bb.store.state ||
!_bb.store.state.data ||
!_bb.store.state.data._id
) {
return []
}
const QUERY_URL = `/api/${_bb.store.state.data.modelId}/${_bb.store.state.data._id}/enrich`
const response = await api.get(QUERY_URL)
const record = await response.json()
return record[datasource.fieldName]
}
}