diff --git a/package-lock.json b/package-lock.json index 9eb4159c..ba6b4c6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18041,6 +18041,7 @@ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", "dev": true, + "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", diff --git a/src/commands/StatusListener.ts b/src/commands/StatusListener.ts index 9e1400a4..5aded95e 100644 --- a/src/commands/StatusListener.ts +++ b/src/commands/StatusListener.ts @@ -195,7 +195,7 @@ export class StatusListener { } // Validate against schema - const errors = StatusListener.validator.validate(article.data, contentType); + const errors = await StatusListener.validator.validate(article.data, contentType); if (errors.length === 0) { return; diff --git a/src/helpers/ContentTypeSchemaGenerator.ts b/src/helpers/ContentTypeSchemaGenerator.ts index 5f4984b9..52bb717d 100644 --- a/src/helpers/ContentTypeSchemaGenerator.ts +++ b/src/helpers/ContentTypeSchemaGenerator.ts @@ -1,6 +1,8 @@ -import { ContentType, Field, FieldType } from '../models'; +import { ContentType, Field, FieldType, CustomTaxonomy } from '../models'; import { Settings } from '../helpers/SettingsHelper'; -import { SETTING_TAXONOMY_FIELD_GROUPS } from '../constants'; +import { SETTING_TAXONOMY_FIELD_GROUPS, SETTING_TAXONOMY_CUSTOM } from '../constants'; +import { TaxonomyHelper } from './TaxonomyHelper'; +import { TaxonomyType } from '../models/TaxonomyType'; /** * JSON Schema type definition @@ -61,7 +63,7 @@ export class ContentTypeSchemaGenerator { * @param contentType The content type to generate schema from * @returns JSON Schema object */ - public static generateSchema(contentType: ContentType): JSONSchema { + public static async generateSchema(contentType: ContentType): Promise { const schema: JSONSchema = { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', @@ -75,7 +77,7 @@ export class ContentTypeSchemaGenerator { // Process each field in the content type for (const field of contentType.fields) { - const fieldSchema = this.generateFieldSchema(field); + const fieldSchema = await this.generateFieldSchema(field); if (fieldSchema && schema.properties) { schema.properties[field.name] = fieldSchema; @@ -99,7 +101,7 @@ export class ContentTypeSchemaGenerator { * @param field The field to generate schema from * @returns JSON Schema object for the field */ - private static generateFieldSchema(field: Field): JSONSchema | null { + private static async generateFieldSchema(field: Field): Promise { // Skip divider and heading fields as they are UI-only if (field.type === 'divider' || field.type === 'heading') { return null; @@ -166,9 +168,53 @@ export class ContentTypeSchemaGenerator { } break; - case 'tags': - case 'categories': - case 'taxonomy': + case 'tags': { + schema.type = 'array'; + schema.items = { + type: 'string' + }; + + // Get available tags and add as enum for validation + const availableTags = await TaxonomyHelper.get(TaxonomyType.Tag); + if (availableTags && availableTags.length > 0) { + schema.items.enum = availableTags; + } + break; + } + + case 'categories': { + schema.type = 'array'; + schema.items = { + type: 'string' + }; + + // Get available categories and add as enum for validation + const availableCategories = await TaxonomyHelper.get(TaxonomyType.Category); + if (availableCategories && availableCategories.length > 0) { + schema.items.enum = availableCategories; + } + break; + } + + case 'taxonomy': { + schema.type = 'array'; + schema.items = { + type: 'string' + }; + + // Get custom taxonomy options if taxonomyId is specified + if (field.taxonomyId) { + const customTaxonomies = Settings.get(SETTING_TAXONOMY_CUSTOM); + if (customTaxonomies && customTaxonomies.length > 0) { + const taxonomy = customTaxonomies.find((t) => t.id === field.taxonomyId); + if (taxonomy && taxonomy.options && taxonomy.options.length > 0) { + schema.items.enum = taxonomy.options; + } + } + } + break; + } + case 'list': schema.type = 'array'; schema.items = { @@ -183,7 +229,7 @@ export class ContentTypeSchemaGenerator { if (field.fields && field.fields.length > 0) { for (const subField of field.fields) { - const subFieldSchema = this.generateFieldSchema(subField); + const subFieldSchema = await this.generateFieldSchema(subField); if (subFieldSchema && schema.properties) { schema.properties[subField.name] = subFieldSchema; @@ -208,7 +254,7 @@ export class ContentTypeSchemaGenerator { }; // Try to get the field group schemas - const blockSchemas = this.getBlockFieldGroupSchemas(field); + const blockSchemas = await this.getBlockFieldGroupSchemas(field); if (blockSchemas.length > 0) { schema.items = { oneOf: blockSchemas @@ -276,7 +322,7 @@ export class ContentTypeSchemaGenerator { * @param field The block field * @returns Array of JSON Schemas for each field group */ - private static getBlockFieldGroupSchemas(field: Field): JSONSchema[] { + private static async getBlockFieldGroupSchemas(field: Field): Promise { const schemas: JSONSchema[] = []; if (!field.fieldGroup) { @@ -300,7 +346,7 @@ export class ContentTypeSchemaGenerator { }; for (const groupField of fieldGroup.fields) { - const fieldSchema = this.generateFieldSchema(groupField); + const fieldSchema = await this.generateFieldSchema(groupField); if (fieldSchema && groupSchema.properties) { groupSchema.properties[groupField.name] = fieldSchema; diff --git a/src/helpers/FrontMatterValidator.ts b/src/helpers/FrontMatterValidator.ts index 505fa9db..e2553ebb 100644 --- a/src/helpers/FrontMatterValidator.ts +++ b/src/helpers/FrontMatterValidator.ts @@ -55,13 +55,13 @@ export class FrontMatterValidator { * @param contentType The content type to validate against * @returns Array of validation errors (empty if valid) */ - public validate(data: any, contentType: ContentType): ValidationError[] { + public async validate(data: any, contentType: ContentType): Promise { if (!contentType || !contentType.fields || contentType.fields.length === 0) { return []; } // Get or generate schema - const schema = this.getSchema(contentType); + const schema = await this.getSchema(contentType); if (!schema) { return []; } @@ -83,7 +83,7 @@ export class FrontMatterValidator { * @param contentType The content type * @returns JSON Schema */ - private getSchema(contentType: ContentType): JSONSchema | null { + private async getSchema(contentType: ContentType): Promise { // Check cache first const cacheKey = contentType.name; if (this.schemaCache.has(cacheKey)) { @@ -91,7 +91,7 @@ export class FrontMatterValidator { } // Generate new schema - const schema = ContentTypeSchemaGenerator.generateSchema(contentType); + const schema = await ContentTypeSchemaGenerator.generateSchema(contentType); this.schemaCache.set(cacheKey, schema); return schema;