diff --git a/backend/molgenis-emx2-rdf/src/main/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapper.java b/backend/molgenis-emx2-rdf/src/main/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapper.java index 2d8f7a3a1f..55fbba2652 100644 --- a/backend/molgenis-emx2-rdf/src/main/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapper.java +++ b/backend/molgenis-emx2-rdf/src/main/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapper.java @@ -9,7 +9,6 @@ import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; -import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Namespace; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.base.CoreDatatype; @@ -108,10 +107,8 @@ public static CoreDatatype.XSD getCoreDataType(ColumnType columnType) { * */ public Set retrieveValues(final Row row, final Column column) { - if (row.getString(column.getName()) == null) { - return Set.of(); - } - return mapping.get(column.getColumnType()).retrieveValues(baseURL, row, column); + RdfColumnType mapper = mapping.get(column.getColumnType()); + return (mapper.isEmpty(row, column) ? Set.of() : mapper.retrieveValues(baseURL, row, column)); } private enum RdfColumnType { @@ -216,35 +213,34 @@ Set retrieveValues(String baseURL, Row row, Column column) { UrlEscapers.urlPathSegmentEscaper().escape(target.getRootTable().getIdentifier()); final Namespace ns = getSchemaNamespace(baseURL, target.getRootTable().getSchema()); - final Set iris = new HashSet<>(); final Map> items = new HashMap<>(); for (final Reference reference : column.getReferences()) { - final String localColumn = reference.getName(); - final String targetColumn = reference.getRefTo(); - if (column.isArray()) { - final String[] values = row.getStringArray(localColumn); - if (values != null) { - for (int i = 0; i < values.length; i++) { - var keyValuePairs = items.getOrDefault(i, new LinkedHashMap<>()); - keyValuePairs.put(targetColumn, values[i]); - items.put(i, keyValuePairs); - } - } - } else { - final String value = row.getString(localColumn); - if (value != null) { - var keyValuePairs = items.getOrDefault(0, new LinkedHashMap<>()); - keyValuePairs.put(targetColumn, value); - items.put(0, keyValuePairs); - } + final String[] values = + (column.isArray() + ? row.getStringArray(reference.getName()) + : new String[] {row.getString(reference.getName())}); + + if (values == null) continue; + + for (int i = 0; i < values.length; i++) { + Map keyValuePairs = items.getOrDefault(i, new LinkedHashMap<>()); + keyValuePairs.put(reference.getRefTo(), values[i]); + items.put(i, keyValuePairs); } } - for (final var item : items.values()) { + final Set values = new HashSet<>(); + for (final Map item : items.values()) { PrimaryKey key = new PrimaryKey(item); - iris.add(Values.iri(ns, rootTableName + "?" + key.getEncodedValue())); + values.add(Values.iri(ns, rootTableName + "?" + key.getEncodedValue())); } - return Set.copyOf(iris); + return Set.copyOf(values); + } + + @Override + boolean isEmpty(Row row, Column column) { + // Composite key requires all fields to be filled. If one is null, all should be null. + return row.getString(column.getReferences().get(0).getName()) == null; } }, ONTOLOGY(CoreDatatype.XSD.ANYURI) { @@ -289,7 +285,7 @@ private static Namespace getSchemaNamespace(final String baseURL, final SchemaMe private static Set basicRetrieval(Object[] object, Function function) { return Arrays.stream(object) .map(value -> (Value) function.apply(value)) - .collect(Collectors.toSet()); + .collect(Collectors.toUnmodifiableSet()); } /** @@ -305,9 +301,13 @@ private static Set basicRetrievalString( String[] object, Function function) { return Arrays.stream(object) .map(value -> (Value) function.apply(value)) - .collect(Collectors.toSet()); + .collect(Collectors.toUnmodifiableSet()); } abstract Set retrieveValues(final String baseURL, final Row row, final Column column); + + boolean isEmpty(final Row row, final Column column) { + return row.getString(column.getName()) == null; + } } } diff --git a/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapperTest.java b/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapperTest.java index 0b1021b645..41e2e9ae5a 100644 --- a/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapperTest.java +++ b/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/ColumnTypeRdfMapperTest.java @@ -1,7 +1,12 @@ package org.molgenis.emx2.rdf; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.molgenis.emx2.Column.column; import static org.molgenis.emx2.Row.row; +import static org.molgenis.emx2.SelectColumn.s; import static org.molgenis.emx2.TableMetadata.table; import java.io.File; @@ -9,10 +14,8 @@ import java.util.stream.Collectors; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.base.CoreDatatype; -import org.eclipse.rdf4j.model.impl.SimpleValueFactory; import org.eclipse.rdf4j.model.util.Values; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.molgenis.emx2.*; @@ -27,19 +30,27 @@ class ColumnTypeRdfMapperTest { static final String TEST_TABLE = "TestTable"; static final String REF_TABLE = "TestRefTable"; static final String REFBACK_TABLE = "TestRefBackTable"; + static final String COMPOSITE_REF_TABLE = "TestCompositeRefTable"; + static final String COMPOSITE_REFBACK_TABLE = "TestCompositeRefbackTable"; static final String ONT_TABLE = "TestOntology"; + + static final String BASE_URL = "http://localhost:8080/"; + static final String RDF_API_URL_PREFIX = BASE_URL + TEST_SCHEMA + "/api/rdf/"; + static final String FILE_API_URL_PREFIX = BASE_URL + TEST_SCHEMA + "/api/file/"; + + static final ColumnTypeRdfMapper mapper = new ColumnTypeRdfMapper(BASE_URL); + static final ClassLoader classLoader = ColumnTypeRdfMapperTest.class.getClassLoader(); - static final SimpleValueFactory factory = SimpleValueFactory.getInstance(); - static final String baseUrl = "http://localhost:8080/"; - static final String rdfApiUrlPrefix = baseUrl + TEST_SCHEMA + "/api/rdf/"; - static final String fileApiUrlPrefix = baseUrl + TEST_SCHEMA + "/api/file/"; - static final ColumnTypeRdfMapper mapper = new ColumnTypeRdfMapper(baseUrl); static final File TEST_FILE = new File(classLoader.getResource("testfiles/molgenis.png").getFile()); + static final String COLUMN_COMPOSITE_REF = "composite_ref"; + static final String COLUMN_COMPOSITE_REF_ARRAY = "composite_ref_array"; + static final String COLUMN_COMPOSITE_REFBACK = "composite_refback"; + static Database database; static Schema allColumnTypes; - static Row firstRow; + static List testRows; @BeforeAll public static void setup() { @@ -49,39 +60,53 @@ public static void setup() { // Generates a column for each ColumnType. // Filters out REFBACK so that it can be added as last step when all REFs are generated. - List columns = - new ArrayList<>( - Arrays.stream(ColumnType.values()) - .map((value) -> column(value.name(), value)) - .filter((column -> !column.getColumnType().equals(ColumnType.REFBACK))) - .toList()); + List columnList = + Arrays.stream(ColumnType.values()) + .map((value) -> column(value.name(), value)) + .collect(Collectors.toList()); // Defines column-specific settings. - for (Column column : columns) { + for (Column column : columnList) { switch (column.getColumnType()) { case STRING -> column.setPkey(); case REF, REF_ARRAY -> column.setRefTable(REF_TABLE); + case REFBACK -> column.setRefTable(REFBACK_TABLE).setRefBack("ref"); case ONTOLOGY, ONTOLOGY_ARRAY -> column.setRefTable(ONT_TABLE); } } - // refback is possible in one go since 26 nov 2024 :-) - columns.add( - column(ColumnType.REFBACK.name(), ColumnType.REFBACK) - .setRefTable(REFBACK_TABLE) + + // Add extra custom columns for additional tests. + columnList.add(column(COLUMN_COMPOSITE_REF, ColumnType.REF).setRefTable(COMPOSITE_REF_TABLE)); + columnList.add( + column(COLUMN_COMPOSITE_REF_ARRAY, ColumnType.REF_ARRAY).setRefTable(COMPOSITE_REF_TABLE)); + columnList.add( + column(COLUMN_COMPOSITE_REFBACK, ColumnType.REFBACK) + .setRefTable(COMPOSITE_REFBACK_TABLE) .setRefBack("ref")); // Creates tables. allColumnTypes.create( // Ontology table table(ONT_TABLE).setTableType(TableType.ONTOLOGIES), + // Table to test on + table(TEST_TABLE, columnList.toArray(Column[]::new)), // Table to ref towards table(REF_TABLE, column("id", ColumnType.STRING).setPkey()), - // Table to test on - table(TEST_TABLE, columns.toArray(new Column[0])), // Table to get refbacks from table( REFBACK_TABLE, column("id", ColumnType.STRING).setPkey(), + column("ref", ColumnType.REF).setRefTable(TEST_TABLE)), + // Table containing composite primary key to ref towards + table( + COMPOSITE_REF_TABLE, + column("ids", ColumnType.STRING).setPkey(), + column("idi", ColumnType.INT).setPkey()), + // Table containing composite primary key to get refback from + table( + COMPOSITE_REFBACK_TABLE, + column("id1", ColumnType.STRING).setPkey(), + column("id2", ColumnType.STRING).setPkey(), column("ref", ColumnType.REF).setRefTable(TEST_TABLE))); // Inserts table data @@ -94,6 +119,11 @@ public static void setup() { allColumnTypes.getTable(REF_TABLE).insert(row("id", "1"), row("id", "2"), row("id", "3")); + allColumnTypes + .getTable(COMPOSITE_REF_TABLE) + .insert( + row("ids", "a", "idi", "1"), row("ids", "b", "idi", "2"), row("ids", "c", "idi", "3")); + allColumnTypes .getTable(TEST_TABLE) .insert( @@ -167,20 +197,43 @@ public static void setup() { ColumnType.HYPERLINK.name(), "https://molgenis.org", ColumnType.HYPERLINK_ARRAY.name(), - "https://molgenis.org, https://github.com/molgenis")); + "https://molgenis.org, https://github.com/molgenis", + // Extra columns for composite key testing + // -- no manual entry: COLUMN_COMPOSITE_REFBACK + COLUMN_COMPOSITE_REF + ".ids", + "a", + COLUMN_COMPOSITE_REF + ".idi", + "1", + COLUMN_COMPOSITE_REF_ARRAY + ".ids", + "b,c", + COLUMN_COMPOSITE_REF_ARRAY + ".idi", + "2,3"), + // Empty row for validating correct empty behaviour (only primary key & AUTO_ID present) + row(ColumnType.STRING.name(), "emptyValuesRow")); allColumnTypes.getTable(REFBACK_TABLE).insert(row("id", "1", "ref", "lonelyString")); + allColumnTypes + .getTable(COMPOSITE_REFBACK_TABLE) + .insert( + row("id1", "a", "id2", "b", "ref", "lonelyString"), + row("id1", "c", "id2", "d", "ref", "lonelyString")); // Use query to explicitly retrieve all rows as the following would exclude REFBACK values: // allColumnTypes.getTable(TEST_TABLE).retrieveRows() - SelectColumn[] selectColumns = + List selectColumnList = Arrays.stream(ColumnType.values()) .map(i -> new SelectColumn(i.name())) - .toArray(SelectColumn[]::new); + .collect(Collectors.toList()); + // Add Composite columns manually. + selectColumnList.add(s(COLUMN_COMPOSITE_REF + ".ids")); + selectColumnList.add(s(COLUMN_COMPOSITE_REF + ".idi")); + selectColumnList.add(s(COLUMN_COMPOSITE_REF_ARRAY + ".ids")); + selectColumnList.add(s(COLUMN_COMPOSITE_REF_ARRAY + ".idi")); + selectColumnList.add(s(COLUMN_COMPOSITE_REFBACK, s("id1"), s("id2"))); + SelectColumn[] selectColumns = selectColumnList.toArray(SelectColumn[]::new); - // Describe first row for easy access. - firstRow = - allColumnTypes.getTable(TEST_TABLE).query().select(selectColumns).retrieveRows().get(0); + // Describes rows for easy access. + testRows = allColumnTypes.getTable(TEST_TABLE).query().select(selectColumns).retrieveRows(); } @AfterAll @@ -190,8 +243,19 @@ public static void tearDown() { } private Set retrieveValues(String columnName) { + return retrieveValues(columnName, 0); + } + + /** Only primary key & AUTO_ID is filled. */ + private Set retrieveEmptyValues(String columnName) { + // REFBACK causes duplicate row (with only REFBACK values being different). + // Therefore, 3rd row is empty one. + return retrieveValues(columnName, 2); + } + + private Set retrieveValues(String columnName, int row) { return mapper.retrieveValues( - firstRow, allColumnTypes.getTable(TEST_TABLE).getMetadata().getColumn(columnName)); + testRows.get(row), allColumnTypes.getTable(TEST_TABLE).getMetadata().getColumn(columnName)); } private Value retrieveFirstValue(String columnName) { @@ -207,157 +271,161 @@ void validateAllColumnTypesCovered() { Set columnTypes = Arrays.stream(ColumnType.values()).collect(Collectors.toSet()); Set columnMappings = ColumnTypeRdfMapper.getMapperKeys(); - Assertions.assertEquals(columnTypes, columnMappings); + assertEquals(columnTypes, columnMappings); } /** - * Validates if {@link org.eclipse.rdf4j.model.Value} is of expected type. Only validates the - * non-array {@link ColumnType}{@code s} (as array-versions should be of identical type). + * Validates if {@link Value} is of expected type. Only validates the non-array {@link + * ColumnType}{@code s} (as array-versions should be of identical type). */ @Test void validateValueTypes() { Row row = allColumnTypes.getTable(TEST_TABLE).retrieveRows().get(0); - Assertions.assertAll( + assertAll( // SIMPLE - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.BOOL.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.UUID.name()).isIRI()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.FILE.name()).isIRI()), + () -> assertTrue(retrieveFirstValue(ColumnType.BOOL.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.UUID.name()).isIRI()), + () -> assertTrue(retrieveFirstValue(ColumnType.FILE.name()).isIRI()), // STRING - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.STRING.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.TEXT.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.JSON.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.STRING.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.TEXT.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.JSON.name()).isLiteral()), // NUMERIC - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.INT.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.LONG.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.DECIMAL.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.DATE.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.DATETIME.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.PERIOD.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.INT.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.LONG.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.DECIMAL.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.DATE.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.DATETIME.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.PERIOD.name()).isLiteral()), // RELATIONSHIP - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.REF.name()).isIRI()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.REFBACK.name()).isIRI()), + () -> assertTrue(retrieveFirstValue(ColumnType.REF.name()).isIRI()), + () -> assertTrue(retrieveFirstValue(ColumnType.REFBACK.name()).isIRI()), // LAYOUT and other constants // ColumnType.HEADING.name() -> no Value should be present to validate on // format flavors that extend a baseType - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.AUTO_ID.name()).isLiteral()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.ONTOLOGY.name()).isIRI()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.EMAIL.name()).isIRI()), - () -> Assertions.assertTrue(retrieveFirstValue(ColumnType.HYPERLINK.name()).isIRI())); + () -> assertTrue(retrieveFirstValue(ColumnType.AUTO_ID.name()).isLiteral()), + () -> assertTrue(retrieveFirstValue(ColumnType.ONTOLOGY.name()).isIRI()), + () -> assertTrue(retrieveFirstValue(ColumnType.EMAIL.name()).isIRI()), + () -> assertTrue(retrieveFirstValue(ColumnType.HYPERLINK.name()).isIRI()), + + // Composite keys + () -> assertTrue(retrieveFirstValue(COLUMN_COMPOSITE_REF).isIRI()), + () -> assertTrue(retrieveFirstValue(COLUMN_COMPOSITE_REFBACK).isIRI())); } @Test void validateValuesRetrieval() { - Assertions.assertAll( + // REFBACK is special usecase that returns multiple rows if multiple matches are found where + // all columns are identical except the REFBACK. + HashSet actualRefback = new HashSet<>(); + for (int i = 0; i < testRows.size() - 1; i++) { // Last row is empty row. + actualRefback.addAll(retrieveValues(COLUMN_COMPOSITE_REFBACK, i)); + } + + // Validation + assertAll( // SIMPLE + () -> assertEquals(Set.of(Values.literal(true)), retrieveValues(ColumnType.BOOL.name())), () -> - Assertions.assertEquals( - Set.of(Values.literal(true)), retrieveValues(ColumnType.BOOL.name())), - () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal(true), Values.literal(false)), retrieveValues(ColumnType.BOOL_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.iri("urn:uuid:e8af409e-86f7-11ef-85b2-6b76fd707d70")), retrieveValues(ColumnType.UUID.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.iri("urn:uuid:e8af409e-86f7-11ef-85b2-6b76fd707d70"), Values.iri("urn:uuid:14bfb4ca-86f8-11ef-8cc0-378b59fe72e8")), retrieveValues(ColumnType.UUID_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.iri( - fileApiUrlPrefix + FILE_API_URL_PREFIX + TEST_TABLE + "/" + ColumnType.FILE.name() + "/" // Not sure how to retrieve more directly as changes everytime - + firstRow.getString(ColumnType.FILE.name()))), + + testRows.get(0).getString(ColumnType.FILE.name()))), retrieveValues(ColumnType.FILE.name())), // STRING () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal("lonelyString", CoreDatatype.XSD.STRING)), retrieveValues(ColumnType.STRING.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.literal("string1", CoreDatatype.XSD.STRING), Values.literal("string2", CoreDatatype.XSD.STRING)), retrieveValues(ColumnType.STRING_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal("lonelyText", CoreDatatype.XSD.STRING)), retrieveValues(ColumnType.TEXT.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.literal("text1", CoreDatatype.XSD.STRING), Values.literal("text2", CoreDatatype.XSD.STRING)), retrieveValues(ColumnType.TEXT_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal("{\"a\":1,\"b\":2}", CoreDatatype.XSD.STRING)), retrieveValues(ColumnType.JSON.name())), // NUMERIC + () -> assertEquals(Set.of(Values.literal(0)), retrieveValues(ColumnType.INT.name())), () -> - Assertions.assertEquals( - Set.of(Values.literal(0)), retrieveValues(ColumnType.INT.name())), - () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal(1), Values.literal(2)), retrieveValues(ColumnType.INT_ARRAY.name())), + () -> assertEquals(Set.of(Values.literal(3L)), retrieveValues(ColumnType.LONG.name())), () -> - Assertions.assertEquals( - Set.of(Values.literal(3L)), retrieveValues(ColumnType.LONG.name())), - () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal(4L), Values.literal(5L)), retrieveValues(ColumnType.LONG_ARRAY.name())), + () -> assertEquals(Set.of(Values.literal(0.5D)), retrieveValues(ColumnType.DECIMAL.name())), () -> - Assertions.assertEquals( - Set.of(Values.literal(0.5D)), retrieveValues(ColumnType.DECIMAL.name())), - () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal(1.5D), Values.literal(2.5D)), retrieveValues(ColumnType.DECIMAL_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal("2000-01-01", CoreDatatype.XSD.DATE)), retrieveValues(ColumnType.DATE.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.literal("2001-01-01", CoreDatatype.XSD.DATE), Values.literal("2002-01-01", CoreDatatype.XSD.DATE)), retrieveValues(ColumnType.DATE_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal("3000-01-01T12:30:00", CoreDatatype.XSD.DATETIME)), retrieveValues(ColumnType.DATETIME.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.literal("3001-01-01T12:30:00", CoreDatatype.XSD.DATETIME), Values.literal("3002-01-01T12:30:00", CoreDatatype.XSD.DATETIME)), retrieveValues(ColumnType.DATETIME_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.literal("P1D", CoreDatatype.XSD.DURATION)), retrieveValues(ColumnType.PERIOD.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.literal("P1M", CoreDatatype.XSD.DURATION), Values.literal("P1Y", CoreDatatype.XSD.DURATION)), @@ -365,57 +433,104 @@ void validateValuesRetrieval() { // RELATIONSHIP () -> - Assertions.assertEquals( - Set.of(Values.iri(rdfApiUrlPrefix + REF_TABLE + "?id=1")), + assertEquals( + Set.of(Values.iri(RDF_API_URL_PREFIX + REF_TABLE + "?id=1")), retrieveValues(ColumnType.REF.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( - Values.iri(rdfApiUrlPrefix + REF_TABLE + "?id=2"), - Values.iri(rdfApiUrlPrefix + REF_TABLE + "?id=3")), + Values.iri(RDF_API_URL_PREFIX + REF_TABLE + "?id=2"), + Values.iri(RDF_API_URL_PREFIX + REF_TABLE + "?id=3")), retrieveValues(ColumnType.REF_ARRAY.name())), () -> - Assertions.assertEquals( - Set.of(Values.iri(rdfApiUrlPrefix + REFBACK_TABLE + "?id=1")), + assertEquals( + Set.of(Values.iri(RDF_API_URL_PREFIX + REFBACK_TABLE + "?id=1")), retrieveValues(ColumnType.REFBACK.name())), // LAYOUT and other constants -> should return empty sets as they should be excluded - () -> Assertions.assertEquals(Set.of(), retrieveValues(ColumnType.HEADING.name())), + () -> assertEquals(Set.of(), retrieveValues(ColumnType.HEADING.name())), // format flavors that extend a baseType () -> // AUTO_ID is unique so full equality check not possible - Assertions.assertTrue( + assertTrue( retrieveValues(ColumnType.AUTO_ID.name()).stream() .findFirst() .get() .stringValue() .matches("[0-9a-zA-Z]+")), () -> - Assertions.assertEquals( - Set.of(Values.iri(rdfApiUrlPrefix + ONT_TABLE + "?name=aa")), + assertEquals( + Set.of(Values.iri(RDF_API_URL_PREFIX + ONT_TABLE + "?name=aa")), retrieveValues(ColumnType.ONTOLOGY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( - Values.iri(rdfApiUrlPrefix + ONT_TABLE + "?name=bb"), - Values.iri(rdfApiUrlPrefix + ONT_TABLE + "?name=cc")), + Values.iri(RDF_API_URL_PREFIX + ONT_TABLE + "?name=bb"), + Values.iri(RDF_API_URL_PREFIX + ONT_TABLE + "?name=cc")), retrieveValues(ColumnType.ONTOLOGY_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.iri("mailto:aap@example.com")), retrieveValues(ColumnType.EMAIL.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.iri("mailto:noot@example.com"), Values.iri("mailto:mies@example.com")), retrieveValues(ColumnType.EMAIL_ARRAY.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of(Values.iri("https://molgenis.org")), retrieveValues(ColumnType.HYPERLINK.name())), () -> - Assertions.assertEquals( + assertEquals( Set.of( Values.iri("https://molgenis.org"), Values.iri("https://github.com/molgenis")), - retrieveValues(ColumnType.HYPERLINK_ARRAY.name()))); + retrieveValues(ColumnType.HYPERLINK_ARRAY.name())), + // Composite reference / refback + () -> + assertEquals( + Set.of(Values.iri(RDF_API_URL_PREFIX + COMPOSITE_REF_TABLE + "?idi=1&ids=a")), + retrieveValues(COLUMN_COMPOSITE_REF)), + () -> + assertEquals( + Set.of( + Values.iri(RDF_API_URL_PREFIX + COMPOSITE_REF_TABLE + "?idi=2&ids=b"), + Values.iri(RDF_API_URL_PREFIX + COMPOSITE_REF_TABLE + "?idi=3&ids=c")), + retrieveValues(COLUMN_COMPOSITE_REF_ARRAY)), + () -> + assertEquals( + Set.of( + Values.iri(RDF_API_URL_PREFIX + COMPOSITE_REFBACK_TABLE + "?id1=a&id2=b"), + Values.iri(RDF_API_URL_PREFIX + COMPOSITE_REFBACK_TABLE + "?id1=c&id2=d")), + actualRefback)); + } + + @Test + void validateEmptyValuesRetrieval() { + HashSet emptySet = new HashSet<>(); + Column[] columns = + allColumnTypes.getTable(TEST_TABLE).getMetadata().getColumns().stream() + // Primary key and AUTO_ID are filled so skipped. + .filter(c -> !(c.isPrimaryKey() || c.getColumnType().equals(ColumnType.AUTO_ID))) + .toArray(Column[]::new); + + for (Column column : columns) { + Set actual = retrieveEmptyValues(column.getName()); + assertEquals( + emptySet, + actual, + column.getName() + " has a value while it should be empty: " + actual.toString()); + } + } + + @Test + void validateUnmodifiable() { + allColumnTypes.getTable(TEST_TABLE).getMetadata().getColumns().stream() + .forEach( + c -> { + assertThrows( + UnsupportedOperationException.class, + () -> retrieveValues(c.getName()).clear(), + c.getName() + " returns a modifiable set while it should be unmodifiable"); + }); } } diff --git a/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/PrimaryKeyTest.java b/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/PrimaryKeyTest.java index 479b1dfbaf..b6af565a4c 100644 --- a/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/PrimaryKeyTest.java +++ b/backend/molgenis-emx2-rdf/src/test/java/org/molgenis/emx2/rdf/PrimaryKeyTest.java @@ -1,6 +1,10 @@ package org.molgenis.emx2.rdf; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.molgenis.emx2.FilterBean.f; import static org.molgenis.emx2.Operator.EQUALS; @@ -27,13 +31,7 @@ void testThatAPrimaryKeyIsSorted() { @Test void testThatAPrimaryKeyMustHaveAtLeastOneComponent() { - var pairs = new HashMap(); - try { - var key = new PrimaryKey(pairs); - assertNull(key, "Should have thrown an exception during initialisation"); - } catch (Exception e) { - // Expected - } + assertThrows(IllegalArgumentException.class, () -> new PrimaryKey(Map.of())); } @Test @@ -79,4 +77,15 @@ void testThatKeyCanBeConvertedToAFilter() { assertTrue(filterFirst, "The filter should contain a sub filter for the first key."); assertTrue(filterLast, "The filter should contain a sub filter for the last key."); } + + @Test + void testEncodedValues() { + assertAll( + () -> assertEquals("a=1", new PrimaryKey(Map.of("a", "1")).getEncodedValue()), + () -> assertEquals("a=1&b=2", new PrimaryKey(Map.of("a", "1", "b", "2")).getEncodedValue()), + () -> + assertEquals( + "a=1&b=2&c=3", + new PrimaryKey(Map.of("a", "1", "b", "2", "c", "3")).getEncodedValue())); + } }