From 14df4cfa6efd28d7659f0c79bfa9da8a1313934f Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Thu, 30 Apr 2026 15:01:26 +0300 Subject: [PATCH 1/2] fix(ENG-9821): fix --- .../add-to-collection.component.ts | 22 ++++---- .../collection-metadata-step.component.html | 54 ++++++++++--------- .../collection-metadata-step.component.ts | 12 ++--- .../mappers/collections/collections.mapper.ts | 2 +- .../collections/collections-json-api.model.ts | 10 ++-- .../shared/services/collections.service.ts | 21 ++++++-- src/app/shared/services/metadata.service.ts | 7 +++ 7 files changed, 77 insertions(+), 51 deletions(-) diff --git a/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts b/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts index c90a8cee2..ed3b3b706 100644 --- a/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts +++ b/src/app/features/collections/components/add-to-collection/add-to-collection.component.ts @@ -207,7 +207,7 @@ export class AddToCollectionComponent implements CanDeactivateComponent { const payload = { collectionId: this.primaryCollectionId() || '', projectId: this.selectedProject()?.id || '', - collectionMetadata: this.isCedarMode() ? {} : this.collectionMetadataForm.value || {}, + collectionMetadata: this.collectionMetadataForm.value || {}, userId: this.currentUser()?.id || '', }; @@ -234,15 +234,17 @@ export class AddToCollectionComponent implements CanDeactivateComponent { }, }); } else { - this.customDialogService - .open(AddToCollectionConfirmationDialogComponent, { - header: 'collections.addToCollection.confirmationDialogHeader', - width: '500px', - data: { payload, project: this.selectedProject() }, - }) - .onClose.pipe( - filter((res) => !!res), - switchMap(() => this.saveCedarRecordIfNeeded()), + this.saveCedarRecordIfNeeded() + .pipe( + switchMap(() => + this.customDialogService + .open(AddToCollectionConfirmationDialogComponent, { + header: 'collections.addToCollection.confirmationDialogHeader', + width: '500px', + data: { payload, project: this.selectedProject() }, + }) + .onClose.pipe(filter((res) => !!res)) + ), takeUntilDestroyed(this.destroyRef) ) .subscribe({ diff --git a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.html b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.html index 0b0cd6498..8c7e779d1 100644 --- a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.html +++ b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.html @@ -20,16 +20,16 @@

{{ 'collections.addToCollection.collectionMetadata' | translate }}

[instanceObject]="cedarFormData()" > } - } @else { - @for (filterEntry of availableFilterEntries(); track filterEntry.key) { -
-

{{ filterEntry.labelKey | translate }}

+ } -

- {{ collectionMetadataForm().get(filterEntry.key)?.value }} -

-
- } + @for (filterEntry of availableFilterEntries(); track filterEntry.key) { +
+

{{ filterEntry.labelKey | translate }}

+ +

+ {{ collectionMetadataForm().get(filterEntry.key)?.value }} +

+
} } @@ -46,6 +46,21 @@

{{ 'collections.addToCollection.collectionMetadata' | translate }}

+
+ @for (filterEntry of availableFilterEntries(); track filterEntry.key) { +
+ + +
+ } +
+ @if (isCedarMode()) { @if (cedarTemplate()) {
@@ -65,27 +80,16 @@

{{ 'collections.addToCollection.collectionMetadata' | translate }}

[label]="'common.buttons.discardChanges' | translate" (onClick)="handleDiscardChanges()" /> - +
} @else {

{{ 'collections.addToCollection.cedarFormNotAvailable' | translate }}

} } @else { -
- @for (filterEntry of availableFilterEntries(); track filterEntry.key) { -
- - -
- } -
-
{ const filterEntries = this.availableFilterEntries(); - if (filterEntries.length && !this.isCedarMode()) { + if (filterEntries.length) { this.buildCollectionMetadataForm(); } }); @@ -223,8 +224,7 @@ export class CollectionMetadataStepComponent { form.controls && Object.keys(form.controls).length > 0 && filterEntries.length > 0 && - !alreadyPopulated && - !this.isCedarMode() + !alreadyPopulated ) { this.populateFormFromSubmission(submission.submission); this.formPopulatedFromSubmission.set(true); @@ -233,10 +233,8 @@ export class CollectionMetadataStepComponent { effect(() => { if (!this.collectionMetadataSaved() && this.stepperActiveValue() !== AddToCollectionSteps.CollectionMetadata) { - if (!this.isCedarMode()) { - this.collectionMetadataForm().reset(); - this.formPopulatedFromSubmission.set(false); - } + this.collectionMetadataForm().reset(); + this.formPopulatedFromSubmission.set(false); } }); } diff --git a/src/app/shared/mappers/collections/collections.mapper.ts b/src/app/shared/mappers/collections/collections.mapper.ts index cd7711c26..26c717ef5 100644 --- a/src/app/shared/mappers/collections/collections.mapper.ts +++ b/src/app/shared/mappers/collections/collections.mapper.ts @@ -71,7 +71,7 @@ export class CollectionsMapper { backgroundColor: response.embeds.brand.data.attributes.background_color, } : null, - requiredMetadataTemplate: response.embeds.required_metadata_template?.data ?? null, + requiredMetadataTemplate: null, }; } diff --git a/src/app/shared/models/collections/collections-json-api.model.ts b/src/app/shared/models/collections/collections-json-api.model.ts index 9dce2537f..20a0d15d0 100644 --- a/src/app/shared/models/collections/collections-json-api.model.ts +++ b/src/app/shared/models/collections/collections-json-api.model.ts @@ -1,4 +1,3 @@ -import { CedarMetadataDataTemplateJsonApi } from '@osf/features/metadata/models'; import { CollectionSubmissionReviewState } from '@osf/shared/enums/collection-submission-review-state.enum'; import { BrandDataJsonApi } from '../brand/brand.json-api.model'; @@ -15,9 +14,6 @@ export interface CollectionProviderResponseJsonApi { brand: { data?: BrandDataJsonApi; }; - required_metadata_template?: { - data?: CedarMetadataDataTemplateJsonApi | null; - }; }; relationships: { primary_collection: { @@ -26,6 +22,12 @@ export interface CollectionProviderResponseJsonApi { type: string; }; }; + required_metadata_template?: { + data?: { + id: string; + type: string; + } | null; + }; }; } diff --git a/src/app/shared/services/collections.service.ts b/src/app/shared/services/collections.service.ts index 2fea963f7..8390b9038 100644 --- a/src/app/shared/services/collections.service.ts +++ b/src/app/shared/services/collections.service.ts @@ -41,6 +41,7 @@ import { ReviewActionPayloadJsonApi } from '../models/review-action/review-actio import { SetTotalSubmissions } from '../stores/collections/collections.actions'; import { JsonApiService } from './json-api.service'; +import { MetadataService } from './metadata.service'; @Injectable({ providedIn: 'root', @@ -48,6 +49,7 @@ import { JsonApiService } from './json-api.service'; export class CollectionsService { private readonly jsonApiService = inject(JsonApiService); private readonly environment = inject(ENVIRONMENT); + private readonly metadataService = inject(MetadataService); get apiUrl() { return `${this.environment.apiDomainUrl}/v2`; @@ -56,11 +58,22 @@ export class CollectionsService { private actions = createDispatchMap({ setTotalSubmissions: SetTotalSubmissions }); getCollectionProvider(collectionName: string): Observable { - const url = `${this.apiUrl}/providers/collections/${collectionName}/?embed=brand,required_metadata_template`; + const url = `${this.apiUrl}/providers/collections/${collectionName}/?embed=brand`; - return this.jsonApiService - .get>(url) - .pipe(map((response) => CollectionsMapper.fromGetCollectionProviderResponse(response.data))); + return this.jsonApiService.get>(url).pipe( + switchMap((response) => { + const provider = CollectionsMapper.fromGetCollectionProviderResponse(response.data); + const templateId = response.data.relationships.required_metadata_template?.data?.id; + + if (!templateId) { + return of(provider); + } + + return this.metadataService + .getCedarMetadataTemplateDetail(templateId) + .pipe(map((template) => ({ ...provider, requiredMetadataTemplate: template }))); + }) + ); } getCollectionDetails(collectionId: string): Observable { diff --git a/src/app/shared/services/metadata.service.ts b/src/app/shared/services/metadata.service.ts index 82c1bd357..0d0df1364 100644 --- a/src/app/shared/services/metadata.service.ts +++ b/src/app/shared/services/metadata.service.ts @@ -6,6 +6,7 @@ import { inject, Injectable } from '@angular/core'; import { ENVIRONMENT } from '@core/provider/environment.provider'; import { CedarRecordsMapper, MetadataMapper, RorMapper } from '@osf/features/metadata/mappers'; import { + CedarMetadataDataTemplateJsonApi, CedarMetadataRecord, CedarMetadataRecordJsonApi, CedarMetadataTemplateJsonApi, @@ -102,6 +103,12 @@ export class MetadataService { ); } + getCedarMetadataTemplateDetail(templateId: string): Observable { + return this.jsonApiService + .get<{ data: CedarMetadataDataTemplateJsonApi }>(`${this.apiDomainUrl}/_/cedar_metadata_templates/${templateId}/`) + .pipe(map((response) => response.data)); + } + getMetadataCedarRecords( resourceId: string, resourceType: ResourceType, From 366375502dd54cb1b9cb5ed607b298329a487d7c Mon Sep 17 00:00:00 2001 From: Vlad0n20 Date: Thu, 7 May 2026 13:50:41 +0300 Subject: [PATCH 2/2] fix(ENG-9821): update cedar metadata sending --- ...collection-metadata-step.component.spec.ts | 18 ++- .../collection-metadata-step.component.ts | 26 ++-- .../cedar-template-form.component.spec.ts | 15 +- .../cedar-template-form.component.ts | 5 +- .../helpers/cedar-metadata.helper.spec.ts | 130 ++++++++++++++++++ .../metadata/helpers/cedar-metadata.helper.ts | 17 +++ 6 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 src/app/features/metadata/helpers/cedar-metadata.helper.spec.ts diff --git a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.spec.ts b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.spec.ts index f6dc67b64..b76b85874 100644 --- a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.spec.ts +++ b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.spec.ts @@ -136,14 +136,28 @@ describe('CollectionMetadataStepComponent', () => { expect(component.cedarTemplate()).toEqual(MOCK_CEDAR_TEMPLATE); }); - it('should handle discard changes in CEDAR mode', () => { + it('should initialize cedarFormData with CEDAR system fields when template is available and no existing record', () => { + const formData = component.cedarFormData(); + expect(formData['@id']).toBe(''); + expect(formData['schema:isBasedOn']).toBe(MOCK_CEDAR_TEMPLATE.attributes.template['@id']); + expect(formData['schema:name']).toBe(''); + expect(formData['schema:description']).toBe(''); + expect(formData['pav:createdBy']).toBe(''); + expect(formData['oslc:modifiedBy']).toBe(''); + expect(typeof formData['pav:createdOn']).toBe('string'); + expect(typeof formData['pav:lastUpdatedOn']).toBe('string'); + }); + + it('should handle discard changes in CEDAR mode with no existing record', () => { component.cedarFormData.set({ field: 'value' }); component.collectionMetadataSaved.set(true); component.handleDiscardChanges(); expect(component.collectionMetadataSaved()).toBe(false); - expect(component.cedarFormData()).toEqual({}); + const formData = component.cedarFormData(); + expect(formData['@id']).toBe(''); + expect(formData['schema:isBasedOn']).toBe(MOCK_CEDAR_TEMPLATE.attributes.template['@id']); }); it('should handle discard changes with existing record in CEDAR mode', () => { diff --git a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts index 88080951c..72bfb9aab 100644 --- a/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts +++ b/src/app/features/collections/components/add-to-collection/collection-metadata-step/collection-metadata-step.component.ts @@ -27,6 +27,7 @@ import { AddToCollectionSteps, CollectionFilterType } from '@osf/features/collec import { CollectionFilterEntry } from '@osf/features/collections/models/collection-filter-entry.model'; import { AddToCollectionSelectors } from '@osf/features/collections/store/add-to-collection'; import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '@osf/features/metadata/constants'; +import { CedarMetadataHelper } from '@osf/features/metadata/helpers'; import { CedarEditorElement, CedarMetadataDataTemplateJsonApi, @@ -99,9 +100,14 @@ export class CollectionMetadataStepComponent { handleDiscardChanges() { if (this.isCedarMode()) { const record = this.existingCedarRecord(); - this.cedarFormData.set( - record?.attributes?.metadata ? (record.attributes.metadata as Record) : {} - ); + const template = this.cedarTemplate(); + if (record?.attributes?.metadata) { + this.cedarFormData.set(record.attributes.metadata as Record); + } else if (template?.attributes?.template) { + this.cedarFormData.set(CedarMetadataHelper.buildCedarSystemMetadata(template.attributes.template)); + } else { + this.cedarFormData.set({}); + } const editor = this.cedarEditor()?.nativeElement; if (editor) { editor.instanceObject = this.cedarFormData(); @@ -135,15 +141,16 @@ export class CollectionMetadataStepComponent { const template = this.cedarTemplate(); if (!editor || !template) return; - const currentMetadata = editor.currentMetadata; + const currentMetadata = editor.currentMetadata as Record; const isValid = !!editor.dataQualityReport?.isValid; - if (currentMetadata) { - this.cedarFormData.set(currentMetadata as Record); - } + const systemFields = CedarMetadataHelper.buildCedarSystemMetadata(template.attributes.template); + const enrichedMetadata = { ...systemFields, ...(currentMetadata ?? {}) }; + + this.cedarFormData.set(enrichedMetadata); const cedarData: CedarRecordDataBinding = { - data: currentMetadata as CedarRecordDataBinding['data'], + data: enrichedMetadata as CedarRecordDataBinding['data'], id: template.id, isPublished: isValid, }; @@ -196,6 +203,7 @@ export class CollectionMetadataStepComponent { effect(() => { const record = this.existingCedarRecord(); + const template = this.cedarTemplate(); if (record?.attributes?.metadata) { const metadata = record.attributes.metadata as Record; this.cedarFormData.set(metadata); @@ -203,6 +211,8 @@ export class CollectionMetadataStepComponent { if (editor) editor.instanceObject = metadata; const viewer = this.cedarViewer()?.nativeElement; if (viewer) viewer.instanceObject = metadata; + } else if (template?.attributes?.template) { + this.cedarFormData.set(CedarMetadataHelper.buildCedarSystemMetadata(template.attributes.template)); } }); diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts index a4fc5693e..5d57ca88a 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts @@ -3,8 +3,6 @@ import { MockProvider } from 'ng-mocks'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; -import { CedarMetadataHelper } from '@osf/features/metadata/helpers'; - import { CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK } from '@testing/mocks/cedar-metadata-data-template-json-api.mock'; import { provideOSFCore } from '@testing/osf.testing.provider'; import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock'; @@ -79,12 +77,19 @@ describe('CedarTemplateFormComponent', () => { expect(emitSpy).toHaveBeenCalled(); }); - it('should initialize form data with empty metadata when no existing record', () => { + it('should initialize form data with system fields and empty metadata when no existing record', () => { fixture.componentRef.setInput('existingRecord', null); fixture.detectChanges(); - const expectedEmptyMetadata = CedarMetadataHelper.buildEmptyMetadata(); - expect(component.formData()).toEqual(expectedEmptyMetadata); + const formData = component.formData(); + expect(formData['@id']).toBe(''); + expect(formData['schema:isBasedOn']).toBe(mockTemplate.attributes.template['@id']); + expect(formData['schema:name']).toBe(''); + expect(formData['schema:description']).toBe(''); + expect(formData['pav:createdBy']).toBe(''); + expect(formData['oslc:modifiedBy']).toBe(''); + expect(formData['pav:createdOn']).toBeDefined(); + expect(formData['pav:lastUpdatedOn']).toBeDefined(); }); it('should handle cedar change event with undefined currentMetadata', () => { diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts index 8b8ff079c..de43abf44 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts @@ -129,7 +129,10 @@ export class CedarTemplateFormComponent { const structuredMetadata = CedarMetadataHelper.buildStructuredMetadata(metadata); this.formData.set(structuredMetadata); } else { - this.formData.set(CedarMetadataHelper.buildEmptyMetadata()); + this.formData.set({ + ...CedarMetadataHelper.buildCedarSystemMetadata(template), + ...CedarMetadataHelper.buildEmptyMetadata(), + }); } } diff --git a/src/app/features/metadata/helpers/cedar-metadata.helper.spec.ts b/src/app/features/metadata/helpers/cedar-metadata.helper.spec.ts new file mode 100644 index 000000000..6fb1e1b33 --- /dev/null +++ b/src/app/features/metadata/helpers/cedar-metadata.helper.spec.ts @@ -0,0 +1,130 @@ +import { CedarTemplate } from '../models'; + +import { CedarMetadataHelper } from './cedar-metadata.helper'; + +const MOCK_TEMPLATE: CedarTemplate = { + '@id': 'https://repo.metadatacenter.org/templates/test-id', + '@type': 'https://schema.metadatacenter.org/core/Template', + type: 'object', + title: 'Test Template', + description: 'Test', + $schema: 'http://json-schema.org/draft-04/schema#', + '@context': { + pav: 'http://purl.org/pav/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + bibo: 'http://purl.org/ontology/bibo/', + oslc: 'http://open-services.net/ns/core#', + schema: 'http://schema.org/', + 'schema:name': { '@type': 'xsd:string' }, + 'pav:createdBy': { '@type': '@id' }, + 'pav:createdOn': { '@type': 'xsd:dateTime' }, + 'oslc:modifiedBy': { '@type': '@id' }, + 'pav:lastUpdatedOn': { '@type': 'xsd:dateTime' }, + 'schema:description': { '@type': 'xsd:string' }, + }, + required: [], + properties: {}, + _ui: { order: [], propertyLabels: {}, propertyDescriptions: {} }, +}; + +describe('CedarMetadataHelper', () => { + describe('ensureProperStructure', () => { + it('should return an empty array for non-array input', () => { + expect(CedarMetadataHelper.ensureProperStructure(null)).toEqual([]); + expect(CedarMetadataHelper.ensureProperStructure('string')).toEqual([]); + expect(CedarMetadataHelper.ensureProperStructure({})).toEqual([]); + }); + + it('should normalize array items to have @id, @type, rdfs:label', () => { + const input = [{ '@id': 'id1', '@type': 'type1', 'rdfs:label': 'label1' }]; + expect(CedarMetadataHelper.ensureProperStructure(input)).toEqual([ + { '@id': 'id1', '@type': 'type1', 'rdfs:label': 'label1' }, + ]); + }); + + it('should fill missing properties with defaults', () => { + const input = [{}]; + expect(CedarMetadataHelper.ensureProperStructure(input)).toEqual([ + { '@id': '', '@type': '', 'rdfs:label': null }, + ]); + }); + }); + + describe('buildCedarSystemMetadata', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2025-01-15T10:00:00.000Z')); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it('should set @id to empty string', () => { + const result = CedarMetadataHelper.buildCedarSystemMetadata(MOCK_TEMPLATE); + expect(result['@id']).toBe(''); + }); + + it('should set schema:isBasedOn to the template @id', () => { + const result = CedarMetadataHelper.buildCedarSystemMetadata(MOCK_TEMPLATE); + expect(result['schema:isBasedOn']).toBe('https://repo.metadatacenter.org/templates/test-id'); + }); + + it('should set schema:name and schema:description to empty strings', () => { + const result = CedarMetadataHelper.buildCedarSystemMetadata(MOCK_TEMPLATE); + expect(result['schema:name']).toBe(''); + expect(result['schema:description']).toBe(''); + }); + + it('should set pav:createdBy and oslc:modifiedBy to empty strings', () => { + const result = CedarMetadataHelper.buildCedarSystemMetadata(MOCK_TEMPLATE); + expect(result['pav:createdBy']).toBe(''); + expect(result['oslc:modifiedBy']).toBe(''); + }); + + it('should set pav:createdOn and pav:lastUpdatedOn to the current timestamp', () => { + const result = CedarMetadataHelper.buildCedarSystemMetadata(MOCK_TEMPLATE); + expect(result['pav:createdOn']).toBe('2025-01-15T10:00:00.000Z'); + expect(result['pav:lastUpdatedOn']).toBe('2025-01-15T10:00:00.000Z'); + }); + + it('should copy @context from the template', () => { + const result = CedarMetadataHelper.buildCedarSystemMetadata(MOCK_TEMPLATE); + expect(result['@context']).toEqual(MOCK_TEMPLATE['@context']); + }); + + it('should use empty object for @context when template has none', () => { + const templateWithoutContext = { ...MOCK_TEMPLATE, '@context': undefined } as unknown as CedarTemplate; + const result = CedarMetadataHelper.buildCedarSystemMetadata(templateWithoutContext); + expect(result['@context']).toEqual({}); + }); + + it('should use empty string for schema:isBasedOn when template @id is missing', () => { + const templateWithoutId = { ...MOCK_TEMPLATE, '@id': undefined } as unknown as CedarTemplate; + const result = CedarMetadataHelper.buildCedarSystemMetadata(templateWithoutId); + expect(result['schema:isBasedOn']).toBe(''); + }); + }); + + describe('buildEmptyMetadata', () => { + it('should return an object with @context and LDbase-specific empty arrays', () => { + const result = CedarMetadataHelper.buildEmptyMetadata(); + expect(result['@context']).toEqual({}); + expect(result['Constructs']).toEqual([]); + expect(result['Assessments']).toEqual([]); + }); + }); + + describe('buildStructuredMetadata', () => { + it('should return metadata as-is for keys not in the fix list', () => { + const metadata = { customField: 'value' }; + expect(CedarMetadataHelper.buildStructuredMetadata(metadata)).toEqual({ customField: 'value' }); + }); + + it('should normalize array fields in the fix list', () => { + const metadata = { Constructs: [{ '@id': 'id1' }] }; + const result = CedarMetadataHelper.buildStructuredMetadata(metadata); + expect(result['Constructs']).toEqual([{ '@id': 'id1', '@type': '', 'rdfs:label': null }]); + }); + }); +}); diff --git a/src/app/features/metadata/helpers/cedar-metadata.helper.ts b/src/app/features/metadata/helpers/cedar-metadata.helper.ts index 9ee0ecc35..09549a366 100644 --- a/src/app/features/metadata/helpers/cedar-metadata.helper.ts +++ b/src/app/features/metadata/helpers/cedar-metadata.helper.ts @@ -1,3 +1,5 @@ +import { CedarTemplate } from '../models'; + export class CedarMetadataHelper { static ensureProperStructure(items: unknown): Record[] { if (!Array.isArray(items)) return []; @@ -38,6 +40,21 @@ export class CedarMetadataHelper { return fixedMetadata; } + static buildCedarSystemMetadata(template: CedarTemplate): Record { + const now = new Date().toISOString(); + return { + '@context': template['@context'] ?? {}, + '@id': '', + 'schema:isBasedOn': template['@id'] ?? '', + 'schema:name': '', + 'schema:description': '', + 'pav:createdOn': now, + 'pav:createdBy': '', + 'pav:lastUpdatedOn': now, + 'oslc:modifiedBy': '', + }; + } + static buildEmptyMetadata(): Record { return { '@context': {},