From e20efac3eaa79b2ed8b09cd72a7f8f0d85655894 Mon Sep 17 00:00:00 2001 From: Joachim Van Herwegen Date: Fri, 19 Apr 2024 11:40:39 +0200 Subject: [PATCH] fix: Combine metadata with data when generating resources --- src/pods/generate/BaseResourcesGenerator.ts | 31 ++++++++++++------- .../generate/BaseResourcesGenerator.test.ts | 24 +++++++------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/pods/generate/BaseResourcesGenerator.ts b/src/pods/generate/BaseResourcesGenerator.ts index 2b89cb616..a86ef3bfd 100644 --- a/src/pods/generate/BaseResourcesGenerator.ts +++ b/src/pods/generate/BaseResourcesGenerator.ts @@ -185,8 +185,20 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator { data = await this.processFile(link, options); metadata.contentType = link.contentType; } - // Do not yield a container resource if it already exists - if (!isContainerIdentifier(link.identifier) || !await this.store.hasResource(link.identifier)) { + + // Add metadata from .meta file if there is one + if (metaLink) { + const rawMetadata = await this.generateMetadata(metaLink, options); + if (rawMetadata.contentType) { + // Prevent having 2 content types + metadata.contentType = undefined; + } + metadata.setMetadata(rawMetadata); + this.logger.debug(`Adding metadata for ${metaLink.identifier.path}`); + } + + const shouldYield = !isContainerIdentifier(link.identifier) || !await this.store.hasResource(link.identifier); + if (shouldYield) { this.logger.debug(`Generating resource ${link.identifier.path}`); yield { identifier: link.identifier, @@ -194,20 +206,15 @@ export class BaseResourcesGenerator implements TemplatedResourcesGenerator { }; } - // Add metadata from .meta file if there is one - if (metaLink) { - const rawMetadata = await this.generateMetadata(metaLink, options); - if (!rawMetadata.contentType) { - // Make sure this does not remove the content-type if none is explicitly defined - rawMetadata.contentType = metadata.contentType; - } + // Still need to yield metadata in case the actual resource is not being yielded. + // We also do this for containers as existing containers can't be edited in the same way. + if (metaLink && (!shouldYield || isContainerIdentifier(link.identifier))) { const metaIdentifier = this.metadataStrategy.getAuxiliaryIdentifier(link.identifier); - const descriptionMeta = new RepresentationMetadata(metaIdentifier); - addResourceMetadata(rawMetadata, isContainerIdentifier(link.identifier)); + addResourceMetadata(metadata, isContainerIdentifier(link.identifier)); this.logger.debug(`Generating resource ${metaIdentifier.path}`); yield { identifier: metaIdentifier, - representation: new BasicRepresentation(rawMetadata.quads(), descriptionMeta, INTERNAL_QUADS), + representation: new BasicRepresentation(metadata.quads(), metaIdentifier, INTERNAL_QUADS), }; } } diff --git a/test/unit/pods/generate/BaseResourcesGenerator.test.ts b/test/unit/pods/generate/BaseResourcesGenerator.test.ts index 371f8ad76..31b1fb081 100644 --- a/test/unit/pods/generate/BaseResourcesGenerator.test.ts +++ b/test/unit/pods/generate/BaseResourcesGenerator.test.ts @@ -1,3 +1,4 @@ +import { DataFactory } from 'n3'; import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier'; import { BaseResourcesGenerator } from '../../../../src/pods/generate/BaseResourcesGenerator'; import type { @@ -10,7 +11,6 @@ import { asyncToArray } from '../../../../src/util/IterableUtil'; import { ensureTrailingSlash, joinFilePath, trimTrailingSlashes } from '../../../../src/util/PathUtil'; import { readableToQuads, readableToString } from '../../../../src/util/StreamUtil'; import { HandlebarsTemplateEngine } from '../../../../src/util/templates/HandlebarsTemplateEngine'; -import { CONTENT_TYPE_TERM } from '../../../../src/util/Vocabularies'; import { SimpleSuffixStrategy } from '../../../util/SimpleSuffixStrategy'; import { mockFileSystem } from '../../../util/Util'; @@ -110,7 +110,8 @@ describe('A BaseResourcesGenerator', (): void => { it('adds metadata from .meta files.', async(): Promise => { const meta = '<> "metadata".'; - cache.data = { '.meta': meta, container: { 'template.meta': meta, template }}; + const metaType = '<> "text/plain".'; + cache.data = { '.meta': meta, container: { 'template.meta': meta, template, type: 'dummy', 'type.meta': metaType }}; // Not using options since our dummy template generator generates invalid turtle const result = await asyncToArray(generator.generate(rootFilePath, location, { webId })); @@ -120,7 +121,7 @@ describe('A BaseResourcesGenerator', (): void => { { path: `${location.path}.meta` }, { path: `${location.path}container/` }, { path: `${location.path}container/template` }, - { path: `${location.path}container/template.meta` }, + { path: `${location.path}container/type` }, ]); // Root has the 1 raw metadata triple (with <> changed to its identifier) and content-type @@ -137,18 +138,17 @@ describe('A BaseResourcesGenerator', (): void => { expect(contMetadata.identifier.value).toBe(`${location.path}container/`); expect(contMetadata.quads()).toHaveLength(0); - // Document has the 1 raw metadata triple (with <> changed to its identifier) and content-type + // Document has the new metadata const docMetadata = result[3].representation.metadata; expect(docMetadata.identifier.value).toBe(`${location.path}container/template`); - expect(docMetadata.contentType).toBe('text/turtle'); - const docMetadataQuads = await readableToQuads(result[4].representation.data); - const expDocMetadataQuads = docMetadataQuads.getQuads(docMetadata.identifier, 'pre:has', null, null); - expect(expDocMetadataQuads).toHaveLength(1); - expect(expDocMetadataQuads[0].object.value).toBe('metadata'); // Metadata will replace existing metadata so need to make sure content-type is still there - const contentDocMetadataQuads = docMetadataQuads.getQuads(docMetadata.identifier, CONTENT_TYPE_TERM, null, null); - expect(contentDocMetadataQuads).toHaveLength(1); - expect(contentDocMetadataQuads[0].object.value).toBe('text/turtle'); + expect(docMetadata.contentType).toBe('text/turtle'); + expect(docMetadata.get(DataFactory.namedNode('pre:has'))?.value).toBe('metadata'); + + // Type document has new content type + const typeMetadata = result[4].representation.metadata; + expect(typeMetadata.identifier.value).toBe(`${location.path}container/type`); + expect(typeMetadata.contentType).toBe('text/plain'); }); it('does not create container when it already exists.', async(): Promise => {