Skip to content

Commit d736a36

Browse files
authored
Do not allow having both "id" and "@id" / type" and "@type" in Thing (#76)
1 parent 02f6756 commit d736a36

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

cypress/integration/validation.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (C) 2020-2021 The Software Heritage developers
2+
* Copyright (C) 2020-2025 The Software Heritage developers
33
* See the AUTHORS file at the top-level directory of this distribution
44
* License: GNU Affero General Public License version 3, or any later version
55
* See top-level LICENSE file for more information
@@ -105,7 +105,7 @@ describe('Document validation', function() {
105105
cy.get('#errorMessage').should('have.text', 'Unknown field "foobar".');
106106
});
107107

108-
it('errors when both "type" and "@type" are present', function() {
108+
it('errors on having both "type" and "@type"', function() {
109109
cy.get('#codemetaText').then((elem) =>
110110
elem.text(JSON.stringify({
111111
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
@@ -317,6 +317,42 @@ describe('Things or URLs validation', function() {
317317
cy.get('#errorMessage').should('have.text', '"license" must be an URL or a CreativeWork/SoftwareSourceCode/SoftwareApplication object, not: "Copyright 2021 Myself"');
318318
});
319319

320+
it('errors on having both "id" and "@id"', function () {
321+
cy.get('#codemetaText').then((elem) =>
322+
elem.text(JSON.stringify({
323+
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
324+
"@type": "SoftwareSourceCode",
325+
"author": {
326+
"id": "http://example.org/~jdoe",
327+
"@id": "http://example.org/~jdoe",
328+
"@type": "Person",
329+
"name": "Jane Doe",
330+
},
331+
}))
332+
);
333+
cy.get('#validateCodemeta').click();
334+
335+
cy.get('#errorMessage').should('have.text', '"author" must use either "id" or "@id", not both.');
336+
});
337+
338+
it('errors on having both "type" and "@type"', function () {
339+
cy.get('#codemetaText').then((elem) =>
340+
elem.text(JSON.stringify({
341+
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
342+
"@type": "SoftwareSourceCode",
343+
"author": {
344+
"id": "http://example.org/~jdoe",
345+
"type": "Person",
346+
"@type": "Person",
347+
"name": "Jane Doe",
348+
},
349+
}))
350+
);
351+
cy.get('#validateCodemeta').click();
352+
353+
cy.get('#errorMessage').should('have.text', '"author" must use either "type" or "@type", not both.');
354+
});
355+
320356
it('errors on wrong type', function() {
321357
cy.get('#codemetaText').then((elem) =>
322358
elem.text(JSON.stringify({

js/validation/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function validateDocument(doc) {
2121
// TODO: validate id/@id
2222

2323
// Ensure either "type" or "@type" is used, but not both
24-
const typeKeys = ['type', '@type'];
24+
const typeKeys = ["type", "@type"];
2525
if (typeKeys.filter(k => Object.prototype.hasOwnProperty.call(doc, k)).length > 1) {
2626
setError(`Document must use either "type" or "@type", not both.`);
2727
return false;

js/validation/things.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright (C) 2020-2021 The Software Heritage developers
2+
* Copyright (C) 2020-2025 The Software Heritage developers
33
* See the AUTHORS file at the top-level directory of this distribution
44
* License: GNU Affero General Public License version 3, or any later version
55
* See top-level LICENSE file for more information
@@ -67,8 +67,19 @@ function validateThingOrId(parentFieldName, typeFieldValidators, doc) {
6767
//
6868
// typeFieldValidators is a map: {type => {fieldName => fieldValidator}}
6969
function validateThing(parentFieldName, typeFieldValidators, doc) {
70-
// TODO: check there is either id or @id but not both
71-
// TODO: check there is either type or @type but not both
70+
// Ensure either "id" or "@id" is used, but not both
71+
const idKeys = ["id", "@id"];
72+
if (idKeys.filter(k => Object.prototype.hasOwnProperty.call(doc, k)).length > 1) {
73+
setError(`"${parentFieldName}" must use either "id" or "@id", not both.`);
74+
return false;
75+
}
76+
77+
// Ensure either "type" or "@type" is used, but not both
78+
const typeKeys = ["type", "@type"];
79+
if (typeKeys.filter(k => Object.prototype.hasOwnProperty.call(doc, k)).length > 1) {
80+
setError(`"${parentFieldName}" must use either "type" or "@type", not both.`);
81+
return false;
82+
}
7283

7384
var acceptedTypesString = Object.keys(typeFieldValidators).join('/');
7485

@@ -196,6 +207,7 @@ function validateReview(fieldName, doc) {
196207
return validateThingOrId(fieldName, {"Review": reviewFieldValidators}, doc);
197208
}
198209

210+
// Define validators for each field of each type of Thing
199211

200212
var softwareFieldValidators = {
201213
"@id": validateUrl,
@@ -323,7 +335,6 @@ var personFieldValidators = {
323335
"url": validateUrls,
324336
};
325337

326-
327338
var organizationFieldValidators = {
328339
"@id": validateUrl,
329340
"id": validateUrl,

0 commit comments

Comments
 (0)