Merge pull request #15469 from Budibase/BUDI-9010/validate-custom-components

Validate custom components
This commit is contained in:
Andrew Kingston 2025-01-31 11:17:45 +00:00 committed by GitHub
commit 7eb9a68af7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 61 deletions

View File

@ -41,12 +41,11 @@ module.exports = {
if ( if (
/^@budibase\/[^/]+\/.*$/.test(importPath) && /^@budibase\/[^/]+\/.*$/.test(importPath) &&
importPath !== "@budibase/backend-core/tests" && importPath !== "@budibase/backend-core/tests" &&
importPath !== "@budibase/string-templates/test/utils" && importPath !== "@budibase/string-templates/test/utils"
importPath !== "@budibase/client/manifest.json"
) { ) {
context.report({ context.report({
node, node,
message: `Importing from @budibase is not allowed, except for @budibase/backend-core/tests, @budibase/string-templates/test/utils and @budibase/client/manifest.json.`, message: `Importing from @budibase is not allowed, except for @budibase/backend-core/tests and @budibase/string-templates/test/utils.`,
}) })
} }
}, },

View File

@ -1,46 +0,0 @@
import { Component, Screen, ScreenProps } from "@budibase/types"
import clientManifest from "@budibase/client/manifest.json"
export function findComponentsBySettingsType(
screen: Screen,
type: string | string[]
) {
const typesArray = Array.isArray(type) ? type : [type]
const result: {
component: Component
setting: {
type: string
key: string
}
}[] = []
function recurseFieldComponentsInChildren(component: ScreenProps) {
if (!component) {
return
}
const definition = getManifestDefinition(component)
const setting =
"settings" in definition &&
definition.settings.find((s: any) => typesArray.includes(s.type))
if (setting && "type" in setting) {
result.push({
component,
setting: { type: setting.type!, key: setting.key! },
})
}
component._children?.forEach(child => {
recurseFieldComponentsInChildren(child)
})
}
recurseFieldComponentsInChildren(screen?.props)
return result
}
function getManifestDefinition(component: Component) {
const componentType = component._component.split("/").slice(-1)[0]
const definition =
clientManifest[componentType as keyof typeof clientManifest]
return definition
}

View File

@ -2,12 +2,17 @@ import { derived } from "svelte/store"
import { tables } from "./tables" import { tables } from "./tables"
import { selectedScreen } from "./screens" import { selectedScreen } from "./screens"
import { viewsV2 } from "./viewsV2" import { viewsV2 } from "./viewsV2"
import { findComponentsBySettingsType } from "@/helpers/screen" import {
import { UIDatasourceType, Screen, Component } from "@budibase/types" UIDatasourceType,
Screen,
Component,
ScreenProps,
} from "@budibase/types"
import { queries } from "./queries" import { queries } from "./queries"
import { views } from "./views" import { views } from "./views"
import { bindings, featureFlag } from "@/helpers" import { bindings, featureFlag } from "@/helpers"
import { getBindableProperties } from "@/dataBinding" import { getBindableProperties } from "@/dataBinding"
import { componentStore, ComponentDefinition } from "./components"
import { findAllComponents } from "@/helpers/components" import { findAllComponents } from "@/helpers/components"
function reduceBy<TItem extends {}, TKey extends keyof TItem>( function reduceBy<TItem extends {}, TKey extends keyof TItem>(
@ -39,11 +44,15 @@ const validationKeyByType: Record<UIDatasourceType, string | null> = {
} }
export const screenComponentErrors = derived( export const screenComponentErrors = derived(
[selectedScreen, tables, views, viewsV2, queries], [selectedScreen, tables, views, viewsV2, queries, componentStore],
([$selectedScreen, $tables, $views, $viewsV2, $queries]): Record< ([
string, $selectedScreen,
string[] $tables,
> => { $views,
$viewsV2,
$queries,
$componentStore,
]): Record<string, string[]> => {
if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) { if (!featureFlag.isEnabled("CHECK_SCREEN_COMPONENT_SETTINGS_ERRORS")) {
return {} return {}
} }
@ -52,9 +61,11 @@ export const screenComponentErrors = derived(
datasources: Record<string, any> datasources: Record<string, any>
) { ) {
const result: Record<string, string[]> = {} const result: Record<string, string[]> = {}
for (const { component, setting } of findComponentsBySettingsType( for (const { component, setting } of findComponentsBySettingsType(
screen, screen,
["table", "dataSource"] ["table", "dataSource"],
$componentStore.components
)) { )) {
const componentSettings = component[setting.key] const componentSettings = component[setting.key]
if (!componentSettings) { if (!componentSettings) {
@ -113,15 +124,52 @@ export const screenComponentErrors = derived(
} }
) )
function findComponentsBySettingsType(
screen: Screen,
type: string | string[],
definitions: Record<string, ComponentDefinition>
) {
const typesArray = Array.isArray(type) ? type : [type]
const result: {
component: Component
setting: {
type: string
key: string
}
}[] = []
function recurseFieldComponentsInChildren(component: ScreenProps) {
if (!component) {
return
}
const definition = definitions[component._component]
const setting = definition?.settings?.find((s: any) =>
typesArray.includes(s.type)
)
if (setting && "type" in setting) {
result.push({
component,
setting: { type: setting.type!, key: setting.key! },
})
}
component._children?.forEach(child => {
recurseFieldComponentsInChildren(child)
})
}
recurseFieldComponentsInChildren(screen?.props)
return result
}
export const screenComponents = derived( export const screenComponents = derived(
[selectedScreen], [selectedScreen],
([$selectedScreen]) => { ([$selectedScreen]) => {
if (!$selectedScreen) { if (!$selectedScreen) {
return [] return []
} }
const allComponents = findAllComponents( return findAllComponents($selectedScreen.props) as Component[]
$selectedScreen.props
) as Component[]
return allComponents
} }
) )