From fed9965a9959b807357768364d264a0959a65f31 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Sat, 24 Aug 2024 16:14:29 +0200 Subject: [PATCH] Schema validation on index --- plugs/index/index.plug.yaml | 22 ---------------------- plugs/index/page.ts | 34 +++++++++++++++++++++++++++++++--- plugs/tasks/tasks.plug.yaml | 4 ---- website/Schema.md | 9 ++++++--- 4 files changed, 37 insertions(+), 32 deletions(-) diff --git a/plugs/index/index.plug.yaml b/plugs/index/index.plug.yaml index a1f8311b..e7cd0dea 100644 --- a/plugs/index/index.plug.yaml +++ b/plugs/index/index.plug.yaml @@ -245,8 +245,6 @@ config: tag: type: string readOnly: true - enum: - - page tags: anyOf: - type: array @@ -320,8 +318,6 @@ config: tag: type: string readOnly: true - enum: - - aspiring-page name: type: string readOnly: true @@ -341,8 +337,6 @@ config: tag: type: string readOnly: true - enum: - - attachment tags: type: array readOnly: true @@ -386,8 +380,6 @@ config: tag: type: string readOnly: true - enum: - - item tags: type: array items: @@ -423,8 +415,6 @@ config: tag: type: string readOnly: true - enum: - - tag tags: type: array readOnly: true @@ -457,8 +447,6 @@ config: readOnly: true tag: type: string - enum: - - anchor tags: type: array items: @@ -483,8 +471,6 @@ config: readOnly: true tag: type: string - enum: - - link tags: type: array items: @@ -521,8 +507,6 @@ config: readOnly: true tag: type: string - enum: - - header tags: type: array items: @@ -549,8 +533,6 @@ config: readOnly: true tag: type: string - enum: - - paragraph tags: type: array items: @@ -575,8 +557,6 @@ config: readOnly: true tag: type: string - enum: - - template tags: anyOf: - type: array @@ -610,8 +590,6 @@ config: readOnly: true tag: type: string - enum: - - table tags: type: array items: diff --git a/plugs/index/page.ts b/plugs/index/page.ts index 37c6c88b..88fc3300 100644 --- a/plugs/index/page.ts +++ b/plugs/index/page.ts @@ -1,8 +1,10 @@ import type { IndexTreeEvent } from "@silverbulletmd/silverbullet/types"; import { editor, + jsonschema, markdown, space, + system, YAML, } from "@silverbulletmd/silverbullet/syscalls"; @@ -17,6 +19,7 @@ import { } from "@silverbulletmd/silverbullet/lib/tree"; import { updateITags } from "@silverbulletmd/silverbullet/lib/tags"; import type { AspiringPageObject } from "./page_links.ts"; +import { deepObjectMerge } from "@silverbulletmd/silverbullet/lib/json"; export async function indexPage({ name, tree }: IndexTreeEvent) { if (name.startsWith("_")) { @@ -61,13 +64,38 @@ export async function indexPage({ name, tree }: IndexTreeEvent) { updateITags(combinedPageMeta, frontmatter); - // console.log("Page object", combinedPageMeta); - await indexObjects(name, [combinedPageMeta]); - // Make sure this page is no (longer) in the aspiring pages list await queryDeleteObjects("aspiring-page", { filter: ["=", ["attr", "name"], ["string", name]], }); + + const tagSchema = (await system.getSpaceConfig("schema")).tag; + // Validate the page meta against schemas, and only index the tags that validate + for (const tag of combinedPageMeta.tags) { + let schema = tagSchema[tag]; + if (schema) { + schema = deepObjectMerge({ type: "object" }, schema); + const validationError = await jsonschema.validateObject( + schema, + combinedPageMeta, + ); + if (validationError) { + console.warn( + "Validation failed for", + combinedPageMeta, + "for tag", + tag, + ". Error:", + validationError, + ". Removing tag until this is resolved.", + ); + combinedPageMeta.tags.splice(combinedPageMeta.tags.indexOf(tag), 1); + } + } + } + + // console.log("Page object", combinedPageMeta); + await indexObjects(name, [combinedPageMeta]); } export async function lintFrontmatter(): Promise { diff --git a/plugs/tasks/tasks.plug.yaml b/plugs/tasks/tasks.plug.yaml index afc9f9c0..4086ecf3 100644 --- a/plugs/tasks/tasks.plug.yaml +++ b/plugs/tasks/tasks.plug.yaml @@ -53,8 +53,6 @@ config: readOnly: true tag: type: string - enum: - - task tags: type: array items: @@ -90,8 +88,6 @@ config: readOnly: true tag: type: string - enum: - - taskstate tags: type: array items: diff --git a/website/Schema.md b/website/Schema.md index 84e3789d..b485867c 100644 --- a/website/Schema.md +++ b/website/Schema.md @@ -82,11 +82,14 @@ schema.config.properties.myFullName.type: string Now, if you would accidentally change `myFullName` into a number or boolean value, you would get a validation error. # Validation -At the moment, validation only occurs in the editor in [[Space Config]] and [[Frontmatter]] blocks and shows any violations as highlighted errors. +Validation happens during linting in the editor (visually, with squiggly lines for errors) and indexing (at the database level). Therefore, changes to your schema will only go into effect when either of those two things kick in. -Even if data does not pass validation, it is still stored in the data store so it does not (currently) _enforce_ the format. +For pages, during indexing, specified [[Frontmatter]] is validated against schema specified for its tags. If validation fails for one of these tags, that _tag will be removed_ from its `tags` attribute and therefore **not** be indexed as such. This guarantees that the schema is enforced and any object follows the schema. -This may change in the future. +For instance, in the example mentioned in [[#Example]], if the `lastName` of either of the two `#contact` tagged pages is removed (which would mean it no longer matches the schema constraints), this object will no longer be indexed as a `contact` and thus not appear in query results for it. However, since the page still follows the regular `page` tag schema, it will still appear in `page` query results. + +> **note** Note +> If you would like to enforce a newly introduced or updated schema on your entire space, you will have to run a full {[Space: Reindex]}. # Supported types All standard JSON Schema types are supported. Likely you are interested in: