Skip to content

Commit

Permalink
Merge branch 'main' into 169-improve-the-magmacoreservice
Browse files Browse the repository at this point in the history
  • Loading branch information
GCHQDeveloper42 committed Jan 25, 2024
2 parents 261c01b + 5de974d commit 4b2c7e8
Show file tree
Hide file tree
Showing 26 changed files with 476 additions and 11 deletions.
5 changes: 3 additions & 2 deletions core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@

requires transitive uk.gov.gchq.magmacore.hqdm;

exports uk.gov.gchq.magmacore.service;
exports uk.gov.gchq.magmacore.database.query;
exports uk.gov.gchq.magmacore.exception;
exports uk.gov.gchq.magmacore.service.dto;
exports uk.gov.gchq.magmacore.service.transformation;
exports uk.gov.gchq.magmacore.exception;
exports uk.gov.gchq.magmacore.service;
exports uk.gov.gchq.magmacore.util;
}
Original file line number Diff line number Diff line change
Expand Up @@ -906,4 +906,13 @@ void abort() {
database.abort();
}

/**
* Execute a SELECT query.
*
* @param query a SELECT query {@link String}
* @return a {@link QueryResultList}
*/
public QueryResultList executeQuery(final String query) {
return database.executeQuery(query);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ public String toString() {
return "ParticipantDetails [participant=" + participant + ", roles=" + roles + "]";
}

/** TODO: Comment. */
/** A {@link Participant}. */
public final Participant participant;

/** TODO: Comment. */
/** A {@link Set} of {@link Role}s. */
public final Set<Role> roles;

/**
* Constructor.
*
* @param participant A {@link Participant}.
* @param roles A {@link Set} of {@link Role}.
* @param roles A {@link Set} of {@link Role}s.
*/
public ParticipantDetails(final Participant participant, final Set<Role> roles) {
this.participant = participant;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,7 @@ public class MagmaCoreServiceQueries {
""";

/**
* TODO: Comment.
* Find by field value and class query.
*/
public static final String FIND_BY_FIELD_VALUE_AND_CLASS = """
PREFIX hqdm: <https://hqdmtop.github.io/hqdm#>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import uk.gov.gchq.magmacore.database.MagmaCoreDatabase;
import uk.gov.gchq.magmacore.database.MagmaCoreJenaDatabase;
import uk.gov.gchq.magmacore.database.query.QueryResultList;
import uk.gov.gchq.magmacore.exception.MagmaCoreException;
import uk.gov.gchq.magmacore.hqdm.model.Individual;
import uk.gov.gchq.magmacore.hqdm.model.PointInTime;
Expand All @@ -38,6 +39,8 @@
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IriBase;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.RDFS;
import uk.gov.gchq.magmacore.hqdm.services.SpatioTemporalExtentServices;
import uk.gov.gchq.magmacore.service.transformation.DbChangeSet;
import uk.gov.gchq.magmacore.service.transformation.DbCreateOperation;

/**
* Check that {@link MagmaCoreService} works correctly.
Expand Down Expand Up @@ -343,4 +346,44 @@ public void testFindByPartialSignAndClassCaseSensitive() throws MagmaCoreExcepti
assertTrue(found1.isEmpty());
assertTrue(found2.isEmpty());
}

/**
* Check that SPARQL queries can be executed and produce arbitrary results.
*/
@Test
public void testSparqlQuery() {

// Create an in-memory databse.
final MagmaCoreService service = MagmaCoreServiceFactory.createWithJenaDatabase();

// Populate some arbitrary data.
final IRI subj1 = new IRI(TEST_BASE, "subj1");
final IRI pred1 = new IRI(TEST_BASE, "pred1");
final IRI obj1 = new IRI(TEST_BASE, "obj1");
final IRI pred2 = new IRI(TEST_BASE, "pred2");
final IRI obj2 = new IRI(TEST_BASE, "obj2");

new DbChangeSet(
List.of(), // no deletes
List.of(// Two creates
new DbCreateOperation(subj1, pred1, obj1),
new DbCreateOperation(obj1, pred2, obj2)
)
).apply(service);

// Query the service by joining the two statements in a single result.
final QueryResultList result = service.executeQuery("SELECT * WHERE { ?a ?b ?c. ?c ?d ?e}");

// Verify the result.
assertNotNull(result);
assertTrue(result.getVarNames().contains("a"));
assertTrue(result.getVarNames().contains("b"));
assertTrue(result.getVarNames().contains("c"));
assertTrue(result.getVarNames().contains("d"));
assertTrue(result.getVarNames().contains("e"));

// There should be one result record with five columns.
assertEquals(1, result.getQueryResults().size());
assertEquals(5, result.getQueryResults().get(0).getMap().size());
}
}
2 changes: 1 addition & 1 deletion examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</dependency>
<dependency>
<groupId>uk.gov.gchq.magma-core</groupId>
<artifactId>hqdm</artifactId>
<artifactId>model-extension-example</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand Down
4 changes: 3 additions & 1 deletion examples/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
module uk.gov.gchq.magmacore.examples {
requires uk.gov.gchq.magmacore.hqdm;
requires uk.gov.gchq.magmacore;

requires uk.gov.gchq.magmacore.examples.extensions;
exports uk.gov.gchq.magmacore.examples.service;

uses uk.gov.gchq.magmacore.hqdm.extensions.ExtensionServiceProvider;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package uk.gov.gchq.magmacore.examples.extensions;

import java.util.UUID;

import uk.gov.gchq.magmacore.examples.extensions.model.Constants;
import uk.gov.gchq.magmacore.examples.extensions.model.UkLimitedCompany;
import uk.gov.gchq.magmacore.examples.extensions.model.UkLimitedCompanyImpl;
import uk.gov.gchq.magmacore.hqdm.model.Thing;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.HQDM;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IRI;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IriBase;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.RDFS;
import uk.gov.gchq.magmacore.service.MagmaCoreServiceFactory;

/**
* Test the model extension provided by the model-extension-example module.
*
* <p>
* Run this using mvn exec:java
* -Dexec.mainClass=uk.gov.gchq.magmacore.examples.extensions.ModelExtensionTest
* </p>
*/
public class ModelExtensionTest {

// Declare an IRI base for the data to be created.
private static final IriBase TEST_BASE = new IriBase("test", "http://example.com/test#");

/**
* Main entry point.
*
* @param args a String array
*/
public static void main(final String[] args) {
// Create a MagmaCoreService with an in-memory Apache Jena database.
final var mcs = MagmaCoreServiceFactory.createWithJenaDatabase();

// The entity will be a part of a dummy possible_world, we just use the IRI
// rather than creating the possible_world for this example.
final var possibleWorldIri = new IRI(TEST_BASE, UUID.randomUUID().toString());

// Create an IRI for the entity we want to create, then create the entity.
final var entityIri = new IRI(TEST_BASE, UUID.randomUUID().toString());
final Thing entity = new UkLimitedCompanyImpl(entityIri);

// Set the RDF_TYPE and add the entity as a `part_of_possible_world`.
entity.addValue(RDFS.RDF_TYPE, Constants.UK_LIMITED_COMPANY_IRI);
entity.addValue(HQDM.PART_OF_POSSIBLE_WORLD, possibleWorldIri);

// Persist the entity in the database.
mcs.runInWriteTransaction(svc -> {
svc.create(entity);
return svc;
});

// Read the entity back and assert that it matches the original.
mcs.runInReadTransaction(svc -> {
final var restoredEntity = svc.get(entityIri);

if (restoredEntity == null) {
System.err.println("restoredEntity should not be null.");
} else if (!(restoredEntity instanceof UkLimitedCompany)) {
System.err.println("restoredEntity should be an instanceof UkLimitedCompany.");
} else if (!entity.equals(restoredEntity)) {
System.err.println("restoredEntity should be equal to the original entity.");
} else {
System.out.println("Success.");
}
return svc;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public static DbTransformation populateExampleData(final MagmaCoreService mcServ

// Apply the DbChangeSet.
mcService.runInWriteTransaction(signsChangeSet);
//

// Combine the DbChangeSets into a DbTransformation and return it as a record of the changes.
return new DbTransformation(List.of(rdlChangeSet, signsChangeSet));
}
Expand Down
2 changes: 1 addition & 1 deletion hqdm-canonical/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/**
* Classes for constructing HQDM objects as RDF triples.
*/
module uk.gov.gchq.magmacore.hqdm.rdf_canonical {
module uk.gov.gchq.magmacore.hqdm.canonical {
requires transitive uk.gov.gchq.magmacore;

exports uk.gov.gchq.magmacore.hqdm.rdfbuilders;
Expand Down
2 changes: 2 additions & 0 deletions hqdm/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
*/
module uk.gov.gchq.magmacore.hqdm {
exports uk.gov.gchq.magmacore.hqdm.exception;
exports uk.gov.gchq.magmacore.hqdm.extensions;
exports uk.gov.gchq.magmacore.hqdm.model;
exports uk.gov.gchq.magmacore.hqdm.services;
exports uk.gov.gchq.magmacore.hqdm.pojo;
exports uk.gov.gchq.magmacore.hqdm.rdf.exception;
exports uk.gov.gchq.magmacore.hqdm.rdf.iri;
exports uk.gov.gchq.magmacore.hqdm.rdf.util;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package uk.gov.gchq.magmacore.hqdm.extensions;

import uk.gov.gchq.magmacore.hqdm.model.Thing;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IRI;

/**
* An SPI interface for extending HQDM.
*
* <p>
* See: <a href="https://github.com/twalmsley/hqdm_model_extension_example">HQDM Model Extension Example</a> for
* an example of how to write an extension module.
* </p>
*/
public interface ExtensionService {

/**
* Create and entity with the given typeName, or return null if the typeName is not recognised
* by the ExtensionService.
*
* @param typeName A String with the required type to be created.
* @param iri The IRI to use when creating the instance.
* @return A Thing if the typeName is recognised, null otherwise.
*/
Thing createEntity(final String typeName, final IRI iri);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package uk.gov.gchq.magmacore.hqdm.extensions;

import java.util.Map;

import uk.gov.gchq.magmacore.hqdm.model.Thing;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IRI;

/**
* An SPI interface for extending HQDM.
*
* <p>
* See: <a href="https://github.com/twalmsley/hqdm_model_extension_example">HQDM Model Extension Example</a> for
* an example of how to write an extension module.
* </p>
*/
public interface ExtensionServiceProvider {

/**
* Create an instance of the extension service and ask it to register any new HQDM types in the Map.
*
* @param map a Map of IRI to Class so that MagmaCore can dynamically create Entities when found in a database.
* @return ExtensionService instance.
*/
ExtensionService createService(final Map<IRI, Class<? extends Thing>> map);

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;

import uk.gov.gchq.magmacore.hqdm.exception.HqdmException;
import uk.gov.gchq.magmacore.hqdm.extensions.*;
import uk.gov.gchq.magmacore.hqdm.model.*;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.HQDM;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.HqdmIri;
Expand All @@ -40,9 +42,25 @@
*/
public final class HqdmObjectFactory {

private static List<ExtensionService> providers = null;

private HqdmObjectFactory() {
}

private static List<ExtensionService> getExtensionServices() {
if (providers == null) {
providers = new ArrayList<>();

ServiceLoader
.load(ExtensionServiceProvider.class)
.iterator()
.forEachRemaining(p -> {
providers.add(p.createService(iriToClassMap));
});
}
return providers;
}

/**
* Create a new HQDM object from a HQDM entity type and IRI.
*
Expand Down Expand Up @@ -833,7 +851,15 @@ private static Thing mapToThing(final String typeName, final IRI iri) {
return RelationshipServices.createUnitOfMeasure(iri);
case "participant_in_activity_or_association":
default:
throw new HqdmException("Unknown type name: " + typeName);
// Check whether any extensions can handle the type.
for (final var service : getExtensionServices()) {
final Thing t = service.createEntity(typeName, iri);
if (t != null) {
return t;
}
}
// We still don't recognise the type so just create a Thing to represent it.
return SpatioTemporalExtentServices.createThing(iri);
}
}
}
Loading

0 comments on commit 4b2c7e8

Please sign in to comment.