Skip to content

feat(ENG-9817): collection-specific metadata with cedar#963

Open
Vlad0n20 wants to merge 1 commit intoCenterForOpenScience:feature/es2-consolidationfrom
Vlad0n20:feat/ENG-9817
Open

feat(ENG-9817): collection-specific metadata with cedar#963
Vlad0n20 wants to merge 1 commit intoCenterForOpenScience:feature/es2-consolidationfrom
Vlad0n20:feat/ENG-9817

Conversation

@Vlad0n20
Copy link
Copy Markdown
Contributor

  • Ticket: [ENG-9817]
  • Feature flag: n/a

Purpose

Add support for collection-specific metadata using CEDAR (Center for
Expanded Data Annotation and Retrieval) templates when submitting or
editing a project in a collection. Collections that require structured
metadata can now enforce a CEDAR template form instead of the
standard filter-based metadata fields.

Summary of Changes

  • EnvironmentModel — added collectionSubmissionWithCedar: boolean
    feature flag
  • CollectionSubmission model — added optional
    requiredMetadataTemplateId field
  • CollectionProvider model — added optional requiredMetadataTemplate
    field (full CEDAR template object)
  • collections.mapper.ts / collections-json-api.model.ts — map
    requiredMetadataTemplateId from API responses into
    CollectionSubmission and expose requiredMetadataTemplate on the
    provider
  • collections.service.ts / collections.selectors.ts — expose the
    required metadata template via the collections store
  • AddToCollectionComponent — when isCedarMode is active, renders the
    CEDAR form instead of the standard filter fields; saves/updates a
    CEDAR record on submission via CreateCedarMetadataRecord /
    UpdateCedarMetadataRecord actions
  • CollectionMetadataStepComponent — new CEDAR branch: renders a
    web component, tracks form data via
    onCedarChange, and emits cedarDataSaved instead of metadataSaved
  • MetadataCollectionsComponent — accepts cedarRecords, cedarTemplates,
    and isCedarMode inputs; builds lookup maps (cedarRecordByTemplateId,
    cedarTemplateById) and passes matched data to each item
  • MetadataCollectionItemComponent — renders a
    when in CEDAR mode and hides standard filter
    attributes
  • template.json — added collectionSubmissionWithCedar config key
  • en.json — added i18n strings for CEDAR-related UI copy

Screenshot(s)

Side Effects

QA Notes

Comment on lines +156 to +157
id: creator.id,
fullName: creator.fullName,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert changes here, because user can be deleted and it will return erorr.

[isProjectSubmissionsLoading]="isProjectSubmissionsLoading()"
[cedarRecords]="cedarRecords()"
[cedarTemplates]="cedarTemplates()?.data ?? null"
[isCedarMode]="environment.collectionSubmissionWithCedar"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is wrong. Property environment is private and can't be accessible in html. Use variable for this or getter.

Comment on lines +40 to +48
cedarTemplateById = computed(() => {
const templates = this.cedarTemplates();
if (!templates?.length) return new Map<string, CedarMetadataDataTemplateJsonApi>();
const map = new Map<string, CedarMetadataDataTemplateJsonApi>();
for (const template of templates) {
map.set(template.id, template);
}
return map;
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cedarTemplateById = computed(() => {
const templates = this.cedarTemplates();
if (!templates?.length) return new Map<string, CedarMetadataDataTemplateJsonApi>();
const map = new Map<string, CedarMetadataDataTemplateJsonApi>();
for (const template of templates) {
map.set(template.id, template);
}
return map;
});
cedarTemplateById = computed(() => {
const templates = this.cedarTemplates();
return new Map(templates?.map((t) => [t.id, t] as const) ?? []);
});

Comment on lines +27 to +38
cedarRecordByTemplateId = computed(() => {
const records = this.cedarRecords();
if (!records?.length) return new Map<string, CedarMetadataRecordData>();
const map = new Map<string, CedarMetadataRecordData>();
for (const record of records) {
const templateId = record.relationships?.template?.data?.id;
if (templateId) {
map.set(templateId, record);
}
}
return map;
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cedarRecordByTemplateId = computed(() => {
const records = this.cedarRecords();
if (!records?.length) return new Map<string, CedarMetadataRecordData>();
const map = new Map<string, CedarMetadataRecordData>();
for (const record of records) {
const templateId = record.relationships?.template?.data?.id;
if (templateId) {
map.set(templateId, record);
}
}
return map;
});
cedarRecordByTemplateId = computed(() => {
const records = this.cedarRecords();
return new Map(
records?.flatMap((record) => {
const templateId = record.relationships?.template?.data?.id;
return templateId ? [[templateId, record] as const] : [];
}) ?? []
);
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs tests.

it('should populate cedarFormData from existingCedarRecord', () => {
const existingRecord: CedarMetadataRecordData = {
attributes: {
metadata: { field: 'existing' } as CedarMetadataRecordData['attributes']['metadata'],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix this, as there is a type mismatch.

const mockCedarRecord: CedarMetadataRecordData = {
id: 'record-1',
attributes: {
metadata: { field: 'value' } as CedarMetadataRecordData['attributes']['metadata'],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix this, as there is a type mismatch.

Comment on lines +12 to +62
const mockSubmission: CollectionSubmission = {
id: '1',
type: 'collection-submission',
collectionTitle: 'Test Collection',
collectionId: 'collection-123',
reviewsState: CollectionSubmissionReviewState.Pending,
collectedType: 'preprint',
status: 'pending',
volume: '1',
issue: '1',
programArea: 'Science',
schoolType: 'University',
studyDesign: 'Experimental',
dataType: 'Quantitative',
disease: 'Cancer',
gradeLevels: 'Graduate',
requiredMetadataTemplateId: 'template-1',
};

const mockCedarTemplate: CedarMetadataDataTemplateJsonApi = {
id: 'template-1',
type: 'cedar-metadata-templates',
attributes: {
schema_name: 'Test Template',
cedar_id: 'cedar-1',
template: {
'@id': 'https://repo.metadatacenter.org/templates/1',
'@type': 'https://schema.metadatacenter.org/core/Template',
type: 'object',
title: 'Test',
description: 'Test template',
$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: {} },
},
},
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to move into testing/data/collections.

Comment on lines +296 to +309
private saveCedarRecordIfNeeded(): Observable<unknown> {
const cedarData = this.pendingCedarData();
const projectId = this.selectedProject()?.id;
const templateId = this.requiredMetadataTemplate()?.id;
if (!this.isCedarMode() || !cedarData || !projectId || !templateId) {
return of(null);
}

const existing = this.existingCedarRecord();
if (existing?.id) {
return this.actions.updateCedarRecord(cedarData, existing.id, projectId, ResourceType.Project);
}
return this.actions.createCedarRecord(cedarData, projectId, ResourceType.Project);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private saveCedarRecordIfNeeded(): Observable<unknown> {
const cedarData = this.pendingCedarData();
const projectId = this.selectedProject()?.id;
const templateId = this.requiredMetadataTemplate()?.id;
if (!this.isCedarMode() || !cedarData || !projectId || !templateId) {
return of(null);
}
const existing = this.existingCedarRecord();
if (existing?.id) {
return this.actions.updateCedarRecord(cedarData, existing.id, projectId, ResourceType.Project);
}
return this.actions.createCedarRecord(cedarData, projectId, ResourceType.Project);
}
private saveCedarRecordIfNeeded(): Observable<unknown> {
if (!this.isCedarMode()) return of(null);
const cedarData = this.pendingCedarData();
const projectId = this.selectedProject()?.id;
const templateId = this.requiredMetadataTemplate()?.id;
if (!cedarData || !projectId || !templateId) return of(null);
const existingId = this.existingCedarRecord()?.id;
return existingId
? this.actions.updateCedarRecord(cedarData, existingId, projectId, ResourceType.Project)
: this.actions.createCedarRecord(cedarData, projectId, ResourceType.Project);
}

Comment on lines +18 to +49
const MOCK_CEDAR_TEMPLATE: CedarMetadataDataTemplateJsonApi = {
id: 'template-1',
type: 'cedar-metadata-templates',
attributes: {
schema_name: 'Test Template',
cedar_id: 'cedar-1',
template: {
'@id': 'https://repo.metadatacenter.org/templates/1',
'@type': 'https://schema.metadatacenter.org/core/Template',
type: 'object',
title: 'Test',
description: 'Test template',
$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: {} },
},
},
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move it to testing/data/collections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants