Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/badges/tests.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 25 additions & 6 deletions code/internal/+openminds/+base/TypesEnumerationBase.m
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,37 @@
arguments
typeName (1,:) string
end

assert(all(startsWith(typeName, openminds.constant.BaseURI)), ...
'OPENMINDS_MATLAB:Types:InvalidAtType', ...
'Expected @type to start with "%s"', openminds.constant.BaseURI)


% Documents written for an older model use a different
% namespace. Both are accepted, because the type name is the
% last segment either way and the types themselves are stable
% across the versions that share a name.
knownBaseURIs = openminds.constant.BaseURI("v1") + "/" | ...
openminds.constant.BaseURI("v4") + "/";

isKnownNamespace = startsWith(typeName, knownBaseURIs);
if ~all(isKnownNamespace)
error('OPENMINDS_MATLAB:Types:InvalidAtType', ...
'Expected @type to start with "%s" or "%s". Got "%s".', ...
openminds.constant.BaseURI("v1"), ...
openminds.constant.BaseURI("v4"), ...
typeName(find(~isKnownNamespace, 1)))
end

if numel(typeName) > 1
typeEnum = arrayfun(@(str) openminds.enum.Types.fromAtType(str), typeName);
return
end

splitName = strsplit(typeName, '/');
typeEnum = eval(sprintf('openminds.enum.Types.%s', splitName{end}));

try
typeEnum = openminds.enum.Types(splitName{end});
catch
error('OPENMINDS_MATLAB:Types:UnknownAtType', ...
['"%s" does not name a type in version "%s" of the ', ...
'openMINDS model.'], splitName{end}, openminds.getModelVersion())
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function vocabularyIRIs = getVocabularyIRIs()
%getVocabularyIRIs Vocabulary IRIs used by any openMINDS model version
%
% vocabularyIRIs = getVocabularyIRIs() returns every vocabulary IRI an
% openMINDS document may use for property names in expanded form.
%
% Documents are read regardless of which model version wrote them, so
% all known vocabularies are needed, not only the one belonging to the
% active version.

vocabularyIRIs = [ ...
openminds.constant.BaseURI("v1") + "/vocab/", ...
openminds.constant.BaseURI("v4") + "/props/"];
end
21 changes: 18 additions & 3 deletions code/internal/+openminds/+internal/+serializer/jsonld2struct.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
function structInstance = jsonld2struct(jsonInstance)
%Convert metadata instance(s) from JSON-LD text strings to struct arrays
%jsonld2struct Convert JSON-LD text into struct form
%
% structInstance = jsonld2struct(jsonInstance) decodes one JSON-LD
% document. A collection document is returned as its @graph, so the
% result is always the nodes rather than the wrapper.
%
% Property names written in expanded form carry a vocabulary IRI. That
% prefix is removed so the field names match the property names of the
% generated type classes. Every openMINDS vocabulary is removed, not
% just the one belonging to the active model version, so a document
% written for an older model can still be read.

vocabBaseUri = "https://openminds.ebrains.eu/vocab/";
arguments
jsonInstance (1,1) string
end

for vocabularyIRI = openminds.internal.serializer.jsonld.getVocabularyIRIs()
jsonInstance = strrep(jsonInstance, vocabularyIRI, "");
end

jsonInstance = strrep(jsonInstance, vocabBaseUri, '');
structInstance = openminds.internal.utility.json.decode(jsonInstance);

if isfield(structInstance, 'at_graph')
Expand Down
41 changes: 29 additions & 12 deletions tools/tests/unitTests/FixtureTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,42 @@ function testGoldenFixtureLoadsWithValuesIntact(testCase)
"https://openminds.om-i.org/instances/species/homoSapiens")
end

function testLegacyNamespaceDocumentIsRejectedClearly(testCase)
% A document written with the pre-v4 EBRAINS namespace cannot be
% loaded while a v4 model is active.
%
% This pins current behaviour: the failure is a clear, identified
% error rather than silent data loss. Supporting cross-namespace
% loading would be an improvement, and this test must then be
% changed to assert that the document loads.
function testLegacyNamespaceDocumentIsRead(testCase)
% A document written with the pre-v4 EBRAINS namespace must load
% under a v4 model. The type name is the last segment of the @type
% either way, and the types that share a name are the same type.

legacyPath = fullfile(ommtest.helper.fixturePath(), ...
"collection_ebrains_legacy.jsonld");
testCase.assumeTrue(isfile(legacyPath))
testCase.assumeEqual(ommtest.helper.fixtureNamespaceTag(), "omi", ...
'This test only applies while a v4 or later model is active.')

testCase.verifyError(@() openminds.Collection(legacyPath), ...
'OPENMINDS_MATLAB:Types:InvalidAtType', ...
['Loading a legacy namespace document should fail with a ', ...
'clear error identifying the namespace mismatch.'])
collection = openminds.Collection(legacyPath);

person = collection.list(openminds.enum.Types("Person"));
testCase.assertNumElements(person, 1, ...
'The legacy document should produce one Person.')
testCase.verifyEqual(person.givenName, "Ada")
testCase.verifyEqual(person.familyName, "Lovelace")
end

function testUnknownNamespaceIsRejectedClearly(testCase)
% A document from something that is not openMINDS at all must fail
% with an identified error rather than being partly read.

documentPath = fullfile(testCase.TemporaryFolder, "foreign.jsonld");
document = [ ...
'{"@context": {"@vocab": "https://example.org/props/"},' ...
' "@graph": [{"@id": "_:x",' ...
' "@type": "https://example.org/types/Person",' ...
' "givenName": "Ada"}]}'];
fileIdentifier = fopen(documentPath, 'w');
fwrite(fileIdentifier, document);
fclose(fileIdentifier);

testCase.verifyError(@() openminds.Collection(documentPath), ...
'OPENMINDS_MATLAB:Types:InvalidAtType')
end
end

Expand Down