Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions frontend/src/components/ResourceDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@

<Checkbox size="sm" label="Auto fetch data on load" v-model="newResource.auto" />

<Checkbox
v-if="newResource.resource_type === 'API Resource'"
size="sm"
label="Cache data across page loads"
v-model="newResource.cache"
/>

<!-- Transform Results for any Resource Type -->
<ScriptSection
title="Transform Results"
Expand Down Expand Up @@ -237,6 +244,7 @@ const emptyResource: Resource = {
on_success: "",
on_error: "",
auto: true,
cache: false,
}

const newResource = ref<Resource>({ ...emptyResource })
Expand Down
1 change: 1 addition & 0 deletions frontend/src/data/studioResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const studioPageResources = createListResource({
"resource_type",
"resource_name",
"auto",
"cache",
"fields",
"filters",
"limit",
Expand Down
36 changes: 22 additions & 14 deletions frontend/src/pages/AppContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,47 @@ const codeStore = useCodeStore()
const page = ref<StudioPage | null>(null)

const rootBlock = ref<Block | null>(null)
let renderedPath: string | null = null

async function loadPage() {
let { pageRoute } = route.params as { pageRoute: string[] }
const isDynamic = route.meta?.isDynamic

let currentPath = "/"
if (isDynamic) {
currentPath = route.matched?.[0]?.path
} else if (pageRoute) {
currentPath = pageRoute[0]
}

async function loadPage(force: boolean = false) {
const currentPath = resolvePagePath()
if (!currentPath) {
rootBlock.value = null
renderedPath = null
return
}

page.value = await findPageWithRoute(window.app_name, currentPath)
const isSamePage = !force && currentPath === renderedPath
if (!isSamePage) {
const nextPage = await findPageWithRoute(window.app_name, currentPath)
if (!nextPage) return
page.value = nextPage
}
if (!page.value) return

await store.setPageData(page.value)
await codeStore.setPageScript(page.value, Boolean(page.value.is_standard))

if (isSamePage) return

const blocks = window.is_preview
? JSON.parse(page.value?.draft_blocks || page.value?.blocks)
: JSON.parse(page.value?.blocks)
if (blocks) {
rootBlock.value = getBlockInstance(blocks[0])
renderedPath = currentPath
}
}

watch(() => route.path, loadPage, { immediate: true })
watch(() => route.path, () => loadPage(), { immediate: true })

if (window.is_preview) useLivePreview(page, loadPage)
if (window.is_preview) useLivePreview(page, () => loadPage(true))

function resolvePagePath() {
if (route.meta?.isDynamic) return route.matched?.[0]?.path
const { pageRoute } = route.params as { pageRoute: string[] }
return pageRoute ? pageRoute[0] : "/"
}

usePageMeta(() => {
return {
Expand Down
26 changes: 18 additions & 8 deletions frontend/src/stores/codeStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as globalUtils from "@/utils/globalUtils"
import { getInitialVariableValue, getValueFromObject, setValueInObject } from "@/utils/helpers"
import { isDynamicValue, normalizeDynamicValue } from "@/utils/code"
import { isFunctionExpression, toOptionalChaining, getTopLevelBindings } from "@/utils/parseCode"
import type { Filters, Resource, DocumentResource, DataResult } from "@/types/Studio/StudioResource"
import type { Filters, Resource, APIResource, DocumentResource, DataResult } from "@/types/Studio/StudioResource"
import type { StudioPage } from "@/types/Studio/StudioPage"
import type { Variable } from "@/types/Studio/StudioPageVariable"
import type { ExpressionEvaluationContext } from "@/types"
Expand Down Expand Up @@ -55,7 +55,6 @@ const useCodeStore = defineStore("codeStore", () => {
async function setPageResources(page: StudioPage, setResourceConfig: boolean = false) {
studioPageResources.filters = { parent: page.name }
await studioPageResources.reload()
resources.value = {}

const resourcePromises = studioPageResources.data.map(async (resource: Resource) => {
const newResource = await getNewResource(resource, {
Expand All @@ -73,14 +72,16 @@ const useCodeStore = defineStore("codeStore", () => {

const resolvedResources = await Promise.all(resourcePromises)

const nextResources: Record<string, Resource> = {}
resolvedResources.forEach((item) => {
resources.value[item.resource_name] = item.value
nextResources[item.resource_name] = item.value
if (setResourceConfig) {
if (!item.value) return
resources.value[item.resource_name].resource_id = item.resource_id
resources.value[item.resource_name].resource_type = item.resource_type
nextResources[item.resource_name].resource_id = item.resource_id
nextResources[item.resource_name].resource_type = item.resource_type
}
})
resources.value = nextResources
}

async function setPageVariables(page: StudioPage) {
Expand Down Expand Up @@ -449,7 +450,7 @@ const useCodeStore = defineStore("codeStore", () => {
switch (resource.resource_type) {
case "Document":
return getDocumentResource(resource, context)
case "Document List":
case "Document List": {
const params: any = {
doctype: resource.document_type,
fields: fields.length ? fields : "*",
Expand All @@ -463,18 +464,27 @@ const useCodeStore = defineStore("codeStore", () => {
params["orderBy"] = `${resource.sort_field} ${resource.sort_order}`
}
return createListResource(params)
case "API Resource":
}
case "API Resource": {
const apiParams = getAPIParams(resource.params, context)
return createResource({
url: resource.url,
method: resource.method,
params: getAPIParams(resource.params, context),
params: apiParams,
auto: resource.auto,
cache: getCacheOption(resource, apiParams),
...getTransforms(resource),
...getSuccessErrorHandlers(resource),
})
}
}
}

function getCacheOption(resource: APIResource, params: Record<string, any> | string | null) {
if (!resource.cache) return undefined
return [resource.resource_name, resource.url, resource.method, JSON.stringify(params)]
}

function getAPIParams(params: Record<string, any> | string | null = null, context: ExpressionEvaluationContext) {
if (!params) return null
if (typeof params === "string") {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/types/Studio/StudioResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ interface BaseResource {
resource_type: ResourceType
/** Whether to automatically fetch data on first load */
auto?: boolean
/** Whether to reuse the resource and its data across page loads, keyed on url + params */
cache?: boolean
transform?: string | null
on_success?: string
on_error?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"sort_field",
"sort_order",
"auto",
"cache",
"document_resource_section",
"document_type",
"column_break_mkdu",
Expand Down Expand Up @@ -173,6 +174,13 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Auto fetch data on load"
},
{
"default": "0",
"description": "Reuse this resource and its data across page loads, keyed on url + params",
"fieldname": "cache",
"fieldtype": "Check",
"label": "Cache data across page loads"
}
],
"grid_page_length": 50,
Expand Down
Loading