Implement doap exporter for software metadata - #4
Conversation
WalkthroughThese changes introduce DOAP (Description of a Project) RDF export functionality to the application. They add a new exporter class supporting Turtle and RDF/XML serialization formats, integrate it into the export registry, extend the unified metadata type with DOAP configuration, and add UI controls for maintainer and format selection in the Svelte route component. Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as Svelte Component
participant Metadata as Unified Metadata
participant Exporter as DOAP Exporter
participant RDF as RDF Serializer
User->>UI: Select DOAP format & configure maintainer
UI->>Metadata: Read doapConfig with resolved maintainer
User->>UI: Click Export
UI->>Exporter: export(data, doapConfig)
Exporter->>Exporter: Validate metadata & maintainer
Exporter->>Exporter: Build RDF triples<br/>(project, metadata fields)
Exporter->>Exporter: Normalize licenses to SPDX
Exporter->>Exporter: Generate URIs & person triples
alt Format = Turtle
Exporter->>RDF: serializeToTurtle(triples)
RDF->>Exporter: Turtle string
else Format = RDF/XML
Exporter->>RDF: serializeToRdfXml(triples)
RDF->>Exporter: RDF/XML string
end
Exporter->>UI: Return serialized string
UI->>UI: Trigger download with<br/>extension (.ttl or .rdf)
UI->>User: File downloaded
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas requiring extra attention:
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/lib/unified-metadata.ts (1)
227-241: Consider centralizing maintainer resolution to avoid drift
createUnifiedMetadatanow infersdoapConfig.maintainer(repo owner → first author), whileDoapExporter.resolveMaintainerhas its own fallback chain (config → repo owner viafullName→ first author). This duplication could diverge over time.You might extract a shared helper (or rely solely on
DoapExporter.resolveMaintainerat export time) so that maintainer resolution rules live in one place.src/routes/+page.svelte (1)
161-169: Handle “empty maintainer” so fallback behavior matches the UI textThe DOAP branch builds:
doapConfig.maintainer = doapMaintainer || undefineddoapMaintaineris updated via{ ...(doapMaintainer || {}), field: value }This means that after the user edits and then clears all maintainer fields,
doapMaintainerbecomes{}(truthy). The exporter then treats this as an explicit maintainer, producing a FOAF person with essentially no data (falling back to"unknown"), instead of using the repository owner or first author as promised by the UI text (“will be automatically resolved … if not specified”).Consider normalizing
doapMaintainerso that an object with no non-empty fields is treated asnull/undefined, e.g.:- const doapConfig: DoapConfig = { - maintainer: doapMaintainer || undefined, - format: doapOutputFormat - }; + const effectiveMaintainer = + doapMaintainer && + (doapMaintainer.name || + doapMaintainer.givenNames || + doapMaintainer.familyNames || + doapMaintainer.email || + doapMaintainer.orcid) + ? doapMaintainer + : undefined; + + const doapConfig: DoapConfig = { + maintainer: effectiveMaintainer, + format: doapOutputFormat + };and reuse the same logic in
getCurrentExportData. Alternatively, adjust the oninput handlers to setdoapMaintainerback tonullwhen all fields are emptied.Also applies to: 177-204, 592-672
src/lib/exporters/doap-exporter.ts (1)
428-516: Turtle/RDF‑XML serializers are fine, but consider future deduplicationThe Turtle and RDF/XML serializers both group triples by subject and then format predicates/objects with proper prefixes/escaping. The logic is clear and should be easy to extend if you add more DOAP/FOAF fields later. There's some duplicated “group by subject” code between the two serializers, but that’s mostly cosmetic.
If you end up extending this exporter further, you could factor the grouping into a small shared helper to avoid repeating the Map-building loop.
Also applies to: 521-571, 575-582
package.json (1)
29-30: Remove unusedn3dependency from package.jsonVerification confirms
n3is not referenced anywhere in the codebase. Either integrate it into the DOAP exporter or remove it to keep dependencies lean.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
package.json(1 hunks)src/lib/exporters/doap-exporter.ts(1 hunks)src/lib/exporters/export-registry.ts(2 hunks)src/lib/unified-metadata.ts(2 hunks)src/routes/+page.svelte(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/lib/exporters/export-registry.ts (1)
src/lib/exporters/doap-exporter.ts (1)
DoapExporter(11-633)
src/lib/exporters/doap-exporter.ts (1)
src/lib/unified-metadata.ts (2)
Author(7-14)UnifiedSoftwareMetadata(91-129)
🔇 Additional comments (7)
src/lib/exporters/export-registry.ts (1)
5-30: DOAP exporter is correctly integrated into the registryThe new
DoapExporterimport andexportFormatsentry look consistent (id matches exporter format, description/icon are clear). No issues with the existing helper functions.src/lib/unified-metadata.ts (1)
124-129: Unified metadata extension for DOAP config is soundAdding an optional
doapConfigwithmaintainer?: Authorcleanly extends the unified model without breaking existing callers, and fits the new exporter’s needs.src/routes/+page.svelte (2)
6-8: Type imports for DOAP are appropriateImporting
Authorfrom unified metadata andDoapConfigfrom the exporter aligns the UI with the underlying types; no issues here.
41-44: DOAP state and initialization logic are consistent with unified metadataThe
doapMaintaineranddoapOutputFormatstate plus the completion handler’s initialization (doapConfig → repo owner fromfullName→ first author) provide sensible defaults and mirror the backend inference strategy.Also applies to: 67-81
src/lib/exporters/doap-exporter.ts (3)
6-107: Core DOAP triple construction looks solidThe
DoapConfigshape andexportmethod cover the expected DOAP fields: project type, name/description/shortdesc, maintainer (with fallback viaresolveMaintainer), homepage, license (normalized to SPDX URIs), language, keywords, repository info, developers, and release metadata. The structure matches common DOAP/FOAF patterns and should interoperate well with RDF consumers.
229-327: Maintainer resolution and FOAF person modeling are reasonable
resolveMaintainer’s priority (config → repository owner fromfullName→ first author) is a pragmatic chain, andaddPersonTriplescorrectly populates FOAFPersonnodes with name, given/family names, mbox, and ORCID-based account links when available.
332-366: License normalization and URI helpers are pragmatic and safe
convertLicenseToSpdxUri’s normalization plus common-license map is a good compromise: well-known IDs get canonicalized, and unknown strings still become SPDX URIs under the namespace. The project/person/repo/release URI generators produce stable identifiers even without repo URLs, which is helpful for offline use.Also applies to: 371-423
| validate(data: UnifiedSoftwareMetadata, doapConfig?: DoapConfig): string[] { | ||
| const errors = super.validate(data); | ||
|
|
||
| // DOAP specific validations | ||
| const maintainer = this.resolveMaintainer(data, doapConfig); | ||
| if (!maintainer) { | ||
| errors.push('Maintainer is required for DOAP (could not resolve from repository owner or authors)'); | ||
| } | ||
|
|
||
| // Validate URIs if present | ||
| if (data.citation.url && !this.isValidUri(data.citation.url)) { | ||
| errors.push('Invalid URL in citation'); | ||
| } | ||
|
|
||
| if (data.repository.htmlUrl && !this.isValidUri(data.repository.htmlUrl)) { | ||
| errors.push('Invalid repository URL'); | ||
| } | ||
|
|
||
| return errors; | ||
| } |
There was a problem hiding this comment.
Align MIME type with selected output format for downloads
mimeType is fixed as 'application/rdf+xml', while export defaults to Turtle and download chooses the extension based on doapConfig.format (ttl vs rdf). For Turtle downloads this yields a .ttl file with an RDF/XML content type, which may confuse some tools.
You can compute the MIME type per call while keeping the class default as-is, for example:
download(data: UnifiedSoftwareMetadata, filename?: string, doapConfig?: DoapConfig): void {
- const content = this.export(data, doapConfig);
- const blob = new Blob([content], { type: this.mimeType });
+ const content = this.export(data, doapConfig);
+ const isTurtle = (doapConfig?.format || 'turtle') === 'turtle';
+ const mimeType = isTurtle ? 'text/turtle' : this.mimeType;
+ const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
- const ext = doapConfig?.format === 'turtle' ? 'ttl' : 'rdf';
+ const ext = isTurtle ? 'ttl' : 'rdf';
link.download = filename || `${data.name.replace(/[^a-zA-Z0-9]/g, '_')}.${ext}`;
...
}This keeps RDF/XML behavior unchanged while making Turtle downloads more self-describing.
Also applies to: 620-627
🤖 Prompt for AI Agents
In src/lib/exporters/doap-exporter.ts around lines 584-603 (and also apply same
change at 620-627), the MIME type is hardcoded to 'application/rdf+xml' which
mismatches when format is Turtle; compute the mimeType per call using the
selected doapConfig.format (if format === 'ttl' or 'turtle' use 'text/turtle',
otherwise use 'application/rdf+xml'), and use that computed mimeType for the
download/response so the file extension and Content-Type align with the chosen
output format.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.