From 56ddbe5b80bd85d8dcc310c006fb852e5b44d301 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Mon, 6 Jul 2026 23:15:21 +0100 Subject: [PATCH 01/22] Replace SimpleQuery runtime paths with repository-backed resolution --- docs/repository-sqlite-migration-audit.md | 10 +- .../core/query/QueryFiltersBooleanTest.java | 4 + .../core/query/QueryFiltersFloatTest.java | 4 + .../core/query/QueryFiltersIdTest.java | 4 + .../core/query/QueryFiltersIntegerTest.java | 4 + .../core/query/QueryFiltersStringTest.java | 4 + .../query/SortingViaQueryFiltersTest.java | 4 + .../query/TodoManagerQueryEngineTest.java | 4 + .../api/restapihandlers/EntityUrlMatcher.java | 2 +- .../restapihandlers/RelationshipCreation.java | 11 +- ...positoryBackedRelationshipUrlResolver.java | 161 ++++++++++++++ .../restapihandlers/RestApiDeleteHandler.java | 37 ++-- .../restapihandlers/RestApiGetHandler.java | 13 +- .../restapihandlers/RestApiPostHandler.java | 13 +- .../RelationshipApiSqliteRepositoryTest.java | 207 ++++++++++++++++++ ...toryBackedRelationshipUrlResolverTest.java | 165 ++++++++++++++ 16 files changed, 599 insertions(+), 48 deletions(-) create mode 100644 thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolver.java create mode 100644 thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java diff --git a/docs/repository-sqlite-migration-audit.md b/docs/repository-sqlite-migration-audit.md index 92b3c50f..84ff89ab 100644 --- a/docs/repository-sqlite-migration-audit.md +++ b/docs/repository-sqlite-migration-audit.md @@ -10,7 +10,9 @@ Simple relationship `GET` traversal, such as `/projects/{id}/tasks`, now uses `T Simple entity `POST`, `PUT`, and `DELETE` requests resolve entity definitions and entity instances through `ThingRepository` for root collection and root instance paths. These paths should not call `getInstanceData()` for SQLite unless a payload uses unsupported legacy behavior. -Relationship create/delete URL paths still use the legacy `SimpleQuery` fallback. This includes write paths such as `POST /projects/{id}/tasks` and `DELETE /projects/{id}/tasks/{id}`. +Relationship create/delete URL paths now use a repository-backed relationship URL resolver. This includes write paths such as `POST /projects/{id}/tasks`, `POST /todo/{id}/task-of`, `DELETE /projects/{id}/tasks/{id}`, and `DELETE /todo/{id}/task-of/{projectId}`. The resolver identifies the parent entity, parent instance, relationship name, and optional child instance through `ThingRepository` APIs rather than traversing `ERInstanceData`. + +Unsupported legacy GET traversals that relied on entity-type matching through `SimpleQuery`, such as `/project/{id}/todo`, now return 404 from the runtime API instead of hydrating a SQLite compatibility snapshot. ## Relationship Payload Helpers @@ -25,7 +27,7 @@ Known compatibility users: - data populator and JSON import paths; - admin/debug/export views and reporting; - legacy data populators that have not implemented `RepositoryDataPopulator`; -- relationship write URL fallback paths. +- legacy `SimpleQuery` tests and direct compatibility callers. The default Todo API and Todo Manager seed populators now implement `RepositoryDataPopulator`, so SQLite-backed Todo apps can seed through repository writes without first hydrating `ERInstanceData`. @@ -33,6 +35,10 @@ The base GUI data explorer list/detail pages now read via `ThingRepository` and These remaining compatibility paths are acceptable for this phase, but they are the next places to reduce if memory efficiency needs to extend beyond simple entity reads and Todo-style population. +## Legacy Query Engine + +`SimpleQuery` remains in `ercoremodel` as deprecated compatibility code. Normal Thingifier API runtime paths should use `RepositoryBackedSimpleQuery`, `ThingRepository`, or the repository-backed relationship URL resolver. The remaining direct `SimpleQuery` references are legacy ER-core query/filter tests that preserve historical behavior until those tests are migrated or the compatibility class is removed. + ## Test Helpers Most `getInstanceData()` and `getThingInstancesNamed()` usage is in `src/test`. These are test fixture and assertion helpers, not runtime leaks, unless a test is specifically exercising SQLite memory efficiency. diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java index 1ad50f08..406de338 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java @@ -12,6 +12,10 @@ import java.util.List; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class QueryFiltersBooleanTest { // todo: advanced filtering i.e. < > partial text match, etc. diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java index 84296c97..898ab37d 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java @@ -12,6 +12,10 @@ import java.util.List; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class QueryFiltersFloatTest { EntityRelModel erModel; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java index ef6fcad3..83a9f860 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java @@ -12,6 +12,10 @@ import java.util.List; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class QueryFiltersIdTest { EntityRelModel erModel; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java index 2495e0aa..579a989b 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java @@ -12,6 +12,10 @@ import java.util.List; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class QueryFiltersIntegerTest { // todo: advanced filtering i.e. < > partial text match, etc. diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java index 0a064a37..5ee4671b 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java @@ -12,6 +12,10 @@ import java.util.List; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class QueryFiltersStringTest { EntityRelModel erModel; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java index 75b7c952..c59fa16d 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java @@ -11,6 +11,10 @@ import java.util.List; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class SortingViaQueryFiltersTest { // todo: lower level testing at the EntityInstanceListSorter level diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java index 38d36a08..7f9ef35e 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java @@ -15,6 +15,10 @@ import static uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType.STRING; +/** + * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths + * should use repository-backed query resolution. + */ public class TodoManagerQueryEngineTest { private EntityRelModel todoManager; diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityUrlMatcher.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityUrlMatcher.java index f1e79243..87788650 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityUrlMatcher.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityUrlMatcher.java @@ -58,7 +58,7 @@ static EntityInstance findInstanceFromUrl( findInstanceByQueryIdentifier(entity, identifier); } - private static String[] parts(final String url) { + static String[] parts(final String url) { String normalized = url == null ? "" : url.trim(); while (normalized.startsWith("/")) { normalized = normalized.substring(1); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java index 2d6584b9..0a0747af 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java @@ -9,7 +9,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.query.SimpleQuery; import java.util.List; import java.util.Map; @@ -22,19 +21,23 @@ public RelationshipCreation(final Thingifier aThingifier) { this.thingifier = aThingifier; } - public ApiResponse create(final String url, final BodyParser bodyargs, final SimpleQuery query, final String database) { + public ApiResponse create( + final String url, + final BodyParser bodyargs, + final RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution relationship, + final String database) { final Map args = bodyargs.getStringMap(); // get the relationship name - String relationshipName = query.getLastRelationshipName(); + String relationshipName = relationship.relationshipName(); // find the thing in the body EntityInstance relatedItem = null; // find the thing from the query to connect the relatedItem to - EntityInstance connectThis = query.getParentInstance(); + EntityInstance connectThis = relationship.parentInstance(); if (connectThis == null) { // TODO: I don't think it is possible to ever hit this line of code return ApiResponse.error404(String.format("Could not find parent thing for relationship %s", url)); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolver.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolver.java new file mode 100644 index 00000000..60447e9a --- /dev/null +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolver.java @@ -0,0 +1,161 @@ +package uk.co.compendiumdev.thingifier.api.restapihandlers; + +import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +import java.util.List; + +final class RepositoryBackedRelationshipUrlResolver { + + private final Thingifier thingifier; + private final ThingRepository repository; + + RepositoryBackedRelationshipUrlResolver( + final Thingifier thingifier, + final String databaseName) { + this.thingifier = thingifier; + this.repository = thingifier.getRepository(databaseName); + } + + RelationshipUrlResolution resolveCollection(final String url) { + String[] parts = EntityUrlMatcher.parts(url); + if (parts.length != 3) { + return RelationshipUrlResolution.notMatched(); + } + + EntityDefinition parentEntity = entityFor(parts[0]); + if (parentEntity == null || !parentEntity.related().hasRelationship(parts[2])) { + return RelationshipUrlResolution.notMatched(); + } + + EntityInstance parent = + repository.findInstanceByQueryIdentifier(parentEntity, parts[1]); + + return RelationshipUrlResolution.collection( + parentEntity, parent, parts[2]); + } + + RelationshipUrlResolution resolveRelationshipInstance(final String url) { + String[] parts = EntityUrlMatcher.parts(url); + if (parts.length != 4) { + return RelationshipUrlResolution.notMatched(); + } + + RelationshipUrlResolution collection = + resolveCollection(parts[0] + "/" + parts[1] + "/" + parts[2]); + if (!collection.matchedRelationshipPath() || collection.parentInstance() == null) { + return collection; + } + + EntityInstance child = null; + List relatedInstances = repository.listRelatedInstances( + collection.parentInstance(), collection.relationshipName()); + for (EntityInstance relatedInstance : relatedInstances) { + if (matchesQueryIdentifier(relatedInstance, parts[3])) { + child = relatedInstance; + break; + } + } + + return RelationshipUrlResolution.relationshipInstance( + collection.parentEntity(), + collection.parentInstance(), + collection.relationshipName(), + child); + } + + private EntityDefinition entityFor(final String term) { + return thingifier.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed(term); + } + + private boolean matchesQueryIdentifier( + final EntityInstance instance, + final String identifier) { + for (Field autoIncrementField : + instance.getEntity().getFieldsOfType(FieldType.AUTO_INCREMENT)) { + String idValue = instance.getFieldValue(autoIncrementField.getName()).asString(); + if (idValue.contentEquals(identifier)) { + return true; + } + break; + } + + String primaryKeyValue = instance.getPrimaryKeyValue(); + return primaryKeyValue != null && primaryKeyValue.contentEquals(identifier); + } + + static final class RelationshipUrlResolution { + private final boolean matchedRelationshipPath; + private final boolean relationshipInstancePath; + private final EntityDefinition parentEntity; + private final EntityInstance parentInstance; + private final String relationshipName; + private final EntityInstance childInstance; + + private RelationshipUrlResolution( + final boolean matchedRelationshipPath, + final boolean relationshipInstancePath, + final EntityDefinition parentEntity, + final EntityInstance parentInstance, + final String relationshipName, + final EntityInstance childInstance) { + this.matchedRelationshipPath = matchedRelationshipPath; + this.relationshipInstancePath = relationshipInstancePath; + this.parentEntity = parentEntity; + this.parentInstance = parentInstance; + this.relationshipName = relationshipName; + this.childInstance = childInstance; + } + + static RelationshipUrlResolution notMatched() { + return new RelationshipUrlResolution( + false, false, null, null, null, null); + } + + static RelationshipUrlResolution collection( + final EntityDefinition parentEntity, + final EntityInstance parentInstance, + final String relationshipName) { + return new RelationshipUrlResolution( + true, false, parentEntity, parentInstance, relationshipName, null); + } + + static RelationshipUrlResolution relationshipInstance( + final EntityDefinition parentEntity, + final EntityInstance parentInstance, + final String relationshipName, + final EntityInstance childInstance) { + return new RelationshipUrlResolution( + true, true, parentEntity, parentInstance, relationshipName, childInstance); + } + + boolean matchedRelationshipPath() { + return matchedRelationshipPath; + } + + boolean relationshipInstancePath() { + return relationshipInstancePath; + } + + EntityDefinition parentEntity() { + return parentEntity; + } + + EntityInstance parentInstance() { + return parentInstance; + } + + String relationshipName() { + return relationshipName; + } + + EntityInstance childInstance() { + return childInstance; + } + } +} diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiDeleteHandler.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiDeleteHandler.java index 780363f2..988d3e01 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiDeleteHandler.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiDeleteHandler.java @@ -5,9 +5,6 @@ import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.query.SimpleQuery; - -import java.util.List; public class RestApiDeleteHandler { private final Thingifier thingifier; @@ -38,27 +35,23 @@ public ApiResponse handle(final String url, HttpHeadersBlock requestHeaders) { return ApiResponse.success(); } - SimpleQuery queryResult = new SimpleQuery(thingifier.getERmodel().getSchema(), thingifier.getERmodel().getInstanceData(instanceDatabaseName), url).performQuery(); - - if (queryResult.wasItemFoundUnderARelationship()) { - // delete the relationships not the items - EntityInstance parent = queryResult.getParentInstance(); - EntityInstance child = queryResult.getLastInstance(); - // todo: this returns a list of 'items' to be removed based on - // removal of the relationships, these should really be deleted from thingifier now - thingifier.getRepository(instanceDatabaseName). - removeRelationshipsInvolving(parent, child, queryResult.getLastRelationshipName()); - } else { - List items = queryResult.getListEntityInstances(); - if (items.isEmpty()) { - // 404 not found - nothing to delete + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution relationship = + new RepositoryBackedRelationshipUrlResolver(thingifier, instanceDatabaseName). + resolveRelationshipInstance(url); + if (relationship.matchedRelationshipPath()) { + if (!relationship.relationshipInstancePath() || + relationship.parentInstance() == null || + relationship.childInstance() == null) { return ApiResponse.error404(String.format("Could not find any instances with %s", url)); } - for (EntityInstance instance : items) { - thingifier.deleteThing(instance, instanceDatabaseName); - } - + thingifier.getRepository(instanceDatabaseName). + removeRelationshipsInvolving( + relationship.parentInstance(), + relationship.childInstance(), + relationship.relationshipName()); + return ApiResponse.success(); } - return ApiResponse.success(); + + return ApiResponse.error404(String.format("Could not find any instances with %s", url)); } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java index 7c6e2d3b..01ccea64 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java @@ -7,7 +7,6 @@ import uk.co.compendiumdev.thingifier.core.query.QueryResult; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.RepositoryBackedSimpleQuery; -import uk.co.compendiumdev.thingifier.core.query.SimpleQuery; import java.util.List; @@ -40,18 +39,8 @@ public ApiResponse handle(final String url, final QueryFilterParams queryParams, thingifier.getRepository(instanceDatabaseName), url). performQuery(effectiveQueryParams); - } else if(allowFiltering){ - queryResults = new SimpleQuery( - thingifier.getERmodel().getSchema(), - thingifier.getERmodel().getInstanceData(instanceDatabaseName), - url). - performQuery(queryParams); } else { - queryResults = new SimpleQuery( - thingifier.getERmodel().getSchema(), - thingifier.getERmodel().getInstanceData(instanceDatabaseName), - url). - performQuery(); + return ApiResponse.error404(String.format("Could not find an instance with %s", url)); } // TODO: we should support pagination through query params diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java index b5b28e90..9bf6bcfb 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java @@ -9,7 +9,6 @@ import uk.co.compendiumdev.thingifier.api.restapihandlers.commonerrorresponse.NoSuchEntity; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.query.SimpleQuery; import java.util.List; @@ -93,12 +92,12 @@ public ApiResponse handle(final String url, final BodyParser args, final HttpHea /* Match a Relationship */ - // get the things to post to - SimpleQuery query = new SimpleQuery( - thingifier.getERmodel().getSchema(), - thingifier.getERmodel().getInstanceData(instanceDatabaseName), url).performQuery(); - if (query.lastMatchWasRelationship()) { - return new RelationshipCreation(thingifier).create(url, args, query, instanceDatabaseName); + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution relationship = + new RepositoryBackedRelationshipUrlResolver(thingifier, instanceDatabaseName). + resolveCollection(url); + if (relationship.matchedRelationshipPath()) { + return new RelationshipCreation(thingifier). + create(url, args, relationship, instanceDatabaseName); } // WHAT was that query? diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java index 60379606..d69dcac6 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java @@ -1,7 +1,10 @@ package uk.co.compendiumdev.casestudy.todomanager.api_non_http; +import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; +import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; @@ -15,6 +18,10 @@ import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class RelationshipApiSqliteRepositoryTest { @Test @@ -67,4 +74,204 @@ public void getRelationshipPathsUseRepositoryWithoutLoadingCompatibilitySnapshot Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } + + @Test + public void getUnsupportedRelationshipTraversalDoesNotUseCompatibilityFallback() { + try (Thingifier todoManager = sqliteTodoManager()) { + ThingRepository repository = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityDefinition todo = todoManager.getDefinitionNamed("todo"); + EntityDefinition project = todoManager.getDefinitionNamed("project"); + + EntityInstance task = repository.addInstance(new EntityInstance(todo). + setValue("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "SQLite relationship project")); + repository.connectRelationship(projectInstance, "tasks", task); + + ApiResponse response = todoManager.api().get( + String.format("project/%s/todo", projectInstance.getPrimaryKeyValue()), + new QueryFilterParams(), + new HttpHeadersBlock()); + + Assertions.assertEquals(404, response.getStatusCode()); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void postRelationshipPathConnectsExistingItemWithoutLoadingCompatibilitySnapshot() { + try (Thingifier todoManager = sqliteTodoManager()) { + ThingRepository repository = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityDefinition todo = todoManager.getDefinitionNamed("todo"); + EntityDefinition project = todoManager.getDefinitionNamed("project"); + + EntityInstance task = repository.addInstance(new EntityInstance(todo). + setValue("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "SQLite relationship project")); + + Map body = new HashMap<>(); + body.put("guid", task.getPrimaryKeyValue()); + + ApiResponse response = todoManager.api().post( + String.format("project/%s/tasks", projectInstance.getPrimaryKeyValue()), + parserFor(todoManager, body), + new HttpHeadersBlock()); + + Assertions.assertEquals(201, response.getStatusCode()); + Assertions.assertEquals( + 1, + repository.listRelatedInstances(projectInstance, "tasks").size()); + Assertions.assertEquals( + task.getPrimaryKeyValue(), + repository.listRelatedInstances(projectInstance, "tasks"). + get(0).getPrimaryKeyValue()); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void postReverseRelationshipPathConnectsExistingItemWithoutLoadingCompatibilitySnapshot() { + try (Thingifier todoManager = sqliteTodoManager()) { + ThingRepository repository = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityDefinition todo = todoManager.getDefinitionNamed("todo"); + EntityDefinition project = todoManager.getDefinitionNamed("project"); + + EntityInstance task = repository.addInstance(new EntityInstance(todo). + setValue("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "SQLite relationship project")); + + Map body = new HashMap<>(); + body.put("guid", projectInstance.getPrimaryKeyValue()); + + ApiResponse response = todoManager.api().post( + String.format("todo/%s/task-of", task.getPrimaryKeyValue()), + parserFor(todoManager, body), + new HttpHeadersBlock()); + + Assertions.assertEquals(201, response.getStatusCode()); + Assertions.assertEquals( + 1, + repository.listRelatedInstances(task, "task-of").size()); + Assertions.assertEquals( + projectInstance.getPrimaryKeyValue(), + repository.listRelatedInstances(task, "task-of"). + get(0).getPrimaryKeyValue()); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void postRelationshipPathCreatesAndConnectsNewItemWithoutLoadingCompatibilitySnapshot() { + try (Thingifier todoManager = sqliteTodoManager()) { + ThingRepository repository = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityDefinition todo = todoManager.getDefinitionNamed("todo"); + EntityDefinition project = todoManager.getDefinitionNamed("project"); + + EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "SQLite relationship project")); + + Map body = new HashMap<>(); + body.put("title", "created through relationship path"); + + ApiResponse response = todoManager.api().post( + String.format("project/%s/tasks", projectInstance.getPrimaryKeyValue()), + parserFor(todoManager, body), + new HttpHeadersBlock()); + + Assertions.assertEquals(201, response.getStatusCode()); + List relatedTasks = + repository.listRelatedInstances(projectInstance, "tasks"); + Assertions.assertEquals(1, relatedTasks.size()); + Assertions.assertEquals( + "created through relationship path", + relatedTasks.get(0).getFieldValue("title").asString()); + Assertions.assertEquals(1, repository.listInstances(todo).size()); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void deleteRelationshipPathRemovesOnlyRelationshipWithoutLoadingCompatibilitySnapshot() { + try (Thingifier todoManager = sqliteTodoManager()) { + ThingRepository repository = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityDefinition todo = todoManager.getDefinitionNamed("todo"); + EntityDefinition project = todoManager.getDefinitionNamed("project"); + + EntityInstance task = repository.addInstance(new EntityInstance(todo). + setValue("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "SQLite relationship project")); + repository.connectRelationship(projectInstance, "tasks", task); + + ApiResponse response = todoManager.api().delete( + String.format( + "project/%s/tasks/%s", + projectInstance.getPrimaryKeyValue(), + task.getPrimaryKeyValue()), + new HttpHeadersBlock()); + + Assertions.assertEquals(200, response.getStatusCode()); + Assertions.assertTrue( + repository.listRelatedInstances(projectInstance, "tasks").isEmpty()); + Assertions.assertNotNull( + repository.findInstanceByQueryIdentifier(todo, task.getPrimaryKeyValue())); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void deleteMissingRelationshipPathReturns404WithoutLoadingCompatibilitySnapshot() { + try (Thingifier todoManager = sqliteTodoManager()) { + ThingRepository repository = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityDefinition todo = todoManager.getDefinitionNamed("todo"); + EntityDefinition project = todoManager.getDefinitionNamed("project"); + + EntityInstance task = repository.addInstance(new EntityInstance(todo). + setValue("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "SQLite relationship project")); + + ApiResponse response = todoManager.api().delete( + String.format( + "project/%s/tasks/%s", + projectInstance.getPrimaryKeyValue(), + task.getPrimaryKeyValue()), + new HttpHeadersBlock()); + + Assertions.assertEquals(404, response.getStatusCode()); + Assertions.assertTrue( + repository.listRelatedInstances(projectInstance, "tasks").isEmpty()); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + + private Thingifier sqliteTodoManager() { + Thingifier todoManager = new Thingifier( + new EntityRelModel(SqliteThingRepositoryProvider.inMemory())); + EntityDefinition todo = todoManager.defineThing("todo", "todos"); + todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + todo.addField(Field.is("title", FieldType.STRING)); + + EntityDefinition project = todoManager.defineThing("project", "projects"); + project.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + project.addField(Field.is("title", FieldType.STRING)); + + todoManager.defineRelationship(project, todo, "tasks", Cardinality.ONE_TO_MANY()). + whenReversed(Cardinality.ONE_TO_MANY(), "task-of"); + return todoManager; + } + + private BodyParser parserFor(final Thingifier todoManager, final Map body) { + HttpApiRequest request = new HttpApiRequest("/path"). + setBody(new Gson().toJson(body)); + return new BodyParser(request, todoManager.getThingNames()); + } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java new file mode 100644 index 00000000..858bee1e --- /dev/null +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java @@ -0,0 +1,165 @@ +package uk.co.compendiumdev.thingifier.api.restapihandlers; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.definitions.Cardinality; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +public class RepositoryBackedRelationshipUrlResolverTest { + + private Thingifier thingifier; + private ThingRepository repository; + private EntityDefinition todo; + private EntityDefinition project; + private EntityInstance task; + private EntityInstance projectInstance; + private RepositoryBackedRelationshipUrlResolver resolver; + + @BeforeEach + public void createModel() { + thingifier = new Thingifier(); + + todo = thingifier.defineThing("todo", "todos"); + todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + todo.addField(Field.is("id", FieldType.AUTO_INCREMENT)); + todo.addField(Field.is("title", FieldType.STRING)); + + project = thingifier.defineThing("project", "projects"); + project.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + project.addField(Field.is("id", FieldType.AUTO_INCREMENT)); + project.addField(Field.is("title", FieldType.STRING)); + + thingifier.defineRelationship(project, todo, "tasks", Cardinality.ONE_TO_MANY()). + whenReversed(Cardinality.ONE_TO_MANY(), "task-of"); + + repository = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + task = repository.addInstance(new EntityInstance(todo). + setValue("title", "relationship task")); + projectInstance = repository.addInstance(new EntityInstance(project). + setValue("title", "relationship project")); + repository.connectRelationship(projectInstance, "tasks", task); + + resolver = new RepositoryBackedRelationshipUrlResolver( + thingifier, EntityRelModel.DEFAULT_DATABASE_NAME); + } + + @Test + public void resolvesRelationshipCollectionPathWithSingularEntityName() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveCollection( + String.format("project/%s/tasks", projectInstance.getPrimaryKeyValue())); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertFalse(resolution.relationshipInstancePath()); + Assertions.assertEquals(project, resolution.parentEntity()); + Assertions.assertEquals(projectInstance, resolution.parentInstance()); + Assertions.assertEquals("tasks", resolution.relationshipName()); + Assertions.assertNull(resolution.childInstance()); + } + + @Test + public void resolvesRelationshipCollectionPathWithPluralEntityName() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveCollection( + String.format("projects/%s/tasks", projectInstance.getPrimaryKeyValue())); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertEquals(projectInstance, resolution.parentInstance()); + Assertions.assertEquals("tasks", resolution.relationshipName()); + } + + @Test + public void resolvesReverseRelationshipCollectionPath() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveCollection( + String.format("todo/%s/task-of", task.getPrimaryKeyValue())); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertEquals(todo, resolution.parentEntity()); + Assertions.assertEquals(task, resolution.parentInstance()); + Assertions.assertEquals("task-of", resolution.relationshipName()); + } + + @Test + public void relationshipCollectionPathCanMatchEvenWhenParentInstanceDoesNotExist() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveCollection("project/not-a-guid/tasks"); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertEquals(project, resolution.parentEntity()); + Assertions.assertNull(resolution.parentInstance()); + Assertions.assertEquals("tasks", resolution.relationshipName()); + } + + @Test + public void doesNotMatchUnknownRelationshipForEntity() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveCollection( + String.format("project/%s/unknown", projectInstance.getPrimaryKeyValue())); + + Assertions.assertFalse(resolution.matchedRelationshipPath()); + } + + @Test + public void resolvesRelationshipInstancePathByPrimaryKey() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveRelationshipInstance( + String.format( + "project/%s/tasks/%s", + projectInstance.getPrimaryKeyValue(), + task.getPrimaryKeyValue())); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertTrue(resolution.relationshipInstancePath()); + Assertions.assertEquals(projectInstance, resolution.parentInstance()); + Assertions.assertEquals(task, resolution.childInstance()); + Assertions.assertEquals("tasks", resolution.relationshipName()); + } + + @Test + public void resolvesRelationshipInstancePathByAutoIncrementId() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveRelationshipInstance( + String.format( + "project/%s/tasks/%s", + projectInstance.getPrimaryKeyValue(), + task.getFieldValue("id").asString())); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertTrue(resolution.relationshipInstancePath()); + Assertions.assertEquals(task, resolution.childInstance()); + } + + @Test + public void relationshipInstancePathRequiresChildToBeConnected() { + EntityInstance unconnectedTask = repository.addInstance(new EntityInstance(todo). + setValue("title", "unconnected task")); + + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveRelationshipInstance( + String.format( + "project/%s/tasks/%s", + projectInstance.getPrimaryKeyValue(), + unconnectedTask.getPrimaryKeyValue())); + + Assertions.assertTrue(resolution.matchedRelationshipPath()); + Assertions.assertTrue(resolution.relationshipInstancePath()); + Assertions.assertNull(resolution.childInstance()); + } + + @Test + public void relationshipInstancePathDoesNotMatchCollectionOnlyUrl() { + RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = + resolver.resolveRelationshipInstance( + String.format("project/%s/tasks", projectInstance.getPrimaryKeyValue())); + + Assertions.assertFalse(resolution.matchedRelationshipPath()); + } +} From 3fa843187b81651089fdb28a5010574be9f86adc Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 7 Jul 2026 12:10:40 +0100 Subject: [PATCH 02/22] Add ChallengerAuto environment matrix harness --- challengerAuto/readme.md | 183 ++++++++ .../restassured/ChallengerAutoSmokeTest.java | 29 ++ .../restassured/api/RestAssuredBaseTest.java | 19 +- .../BasicSimpleApiCrudCoverageTest.java | 8 +- .../sparkstart/ChallengerAutoConfig.java | 429 ++++++++++++++++++ .../sparkstart/ChallengerAutoConfigTest.java | 159 +++++++ .../ChallengerAutoLocalArgsTest.java | 68 +++ .../sparkstart/ChallengerAutoRunProfiles.java | 71 +++ .../sparkstart/ChallengerAutoRuntime.java | 282 ++++++++++++ .../ChallengerAutoRuntimeHarnessCheck.java | 83 ++++ .../compendiumdev/sparkstart/Environment.java | 62 +-- 11 files changed, 1327 insertions(+), 66 deletions(-) create mode 100644 challengerAuto/readme.md create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/ChallengerAutoSmokeTest.java create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfig.java create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfigTest.java create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoLocalArgsTest.java create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRunProfiles.java create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntime.java create mode 100644 challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntimeHarnessCheck.java diff --git a/challengerAuto/readme.md b/challengerAuto/readme.md new file mode 100644 index 00000000..80f93b39 --- /dev/null +++ b/challengerAuto/readme.md @@ -0,0 +1,183 @@ +# Challenger Auto Tests + +`challengerAuto` runs the API Challenges REST tests against a configurable Challenger environment. + +By default, the tests start a local Challenger app in a separate JVM, wait for `/heartbeat`, run the full suite, and then shut down only the process they started. + +- local - startup the application locally prior to running the code +- existing - app is running locally already so test doesn't start it +- live - app is external so we need to set the baseurl to access it + +You can choose between running 'smoke' tests for heartbeat or all tests for external `challenger.auto.external.full` + +See usage combinations below. + +## Local Runs + +PowerShell users should quote Maven `-D` properties, e.g. `"-Dchallenger.auto.target=local"`. + +Default local run: + +```powershell +mvn -pl challengerAuto -am test +``` + +This starts a local Challenger process with: + +- SQLite in-memory repository; +- multiplayer mode; +- automatically selected free port; +- full mutating test suite enabled. + +Run local in-memory, single-player: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=local" ` + "-Dchallenger.auto.local.repository=memory" ` + "-Dchallenger.auto.local.playerMode=single" +``` + +Run local in-memory, multiplayer: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=local" ` + "-Dchallenger.auto.local.repository=memory" ` + "-Dchallenger.auto.local.playerMode=multi" +``` + +Run local SQLite in-memory, single-player: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=local" ` + "-Dchallenger.auto.local.repository=sqlite-memory" ` + "-Dchallenger.auto.local.playerMode=single" +``` + +Run local SQLite in-memory, multiplayer: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=local" ` + "-Dchallenger.auto.local.repository=sqlite-memory" ` + "-Dchallenger.auto.local.playerMode=multi" +``` + +Run local SQLite file-backed: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=local" ` + "-Dchallenger.auto.local.repository=sqlite-file" ` + "-Dchallenger.auto.local.extraArgs=-thingifier-sqlite-directory=target/challenger-auto-sqlite" +``` + +Use a fixed local port: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=local" ` + "-Dchallenger.auto.local.port=4567" +``` + +If the fixed port is already in use, the harness fails fast. Use `challenger.auto.target=existing` when you want to attach to an app you already started. + +Pass extra Challenger args: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.local.extraArgs=-unlimitedtodos,-enableadminapi" +``` + +Extra args are comma-separated and appended to the generated `ChallengeMain` command line. + +## Existing, Live, And Railway Runs + +Run against an existing local app: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=existing" ` + "-Dchallenger.auto.baseUrl=http://localhost:4567" +``` + +`existing` never starts or stops Challenger. It only uses the configured base URL. + +Run smoke checks against the live site: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=live" ` + "-Dtest=ChallengerAutoSmokeTest" +``` + +Run smoke checks against Railway or another hosted environment: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=live" ` + "-Dchallenger.auto.baseUrl=https://your-railway-app.up.railway.app" ` + "-Dtest=ChallengerAutoSmokeTest" +``` + +`live` defaults to `https://apichallenges.eviltester.com`. Override `challenger.auto.baseUrl` for Railway, staging, or any other remote Challenger deployment. + +External targets are smoke-only by default. The mutating challenge and simple API suites are skipped unless explicitly enabled: + +```powershell +mvn -pl challengerAuto -am test ` + "-Dchallenger.auto.target=live" ` + "-Dchallenger.auto.baseUrl=https://your-remote-app.example" ` + "-Dchallenger.auto.external.full=true" +``` + +Only use `challenger.auto.external.full=true` on environments where creating, updating, and deleting test data is acceptable. + +## Configuration Properties + +| Property | Values | Default | Purpose | +| --- | --- | --- | --- | +| `challenger.auto.target` | `local`, `existing`, `live` | `local` | Chooses whether the harness starts Challenger or attaches to a URL. | +| `challenger.auto.baseUrl` | URL | target-specific | Base URL for `existing` and remote `live` runs. | +| `challenger.auto.local.repository` | `memory`, `sqlite-memory`, `sqlite-file` | `sqlite-memory` | Repository mode for owned local Challenger. | +| `challenger.auto.local.playerMode` | `single`, `multi` | `multi` | Single-player or multiplayer mode for owned local Challenger. | +| `challenger.auto.local.port` | `auto` or port number | `auto` | Local port for owned local Challenger. | +| `challenger.auto.local.extraArgs` | comma-separated args | empty | Extra `ChallengeMain` command-line args. | +| `challenger.auto.external.full` | `true`, `false` | `false` | Allows full mutating suite for `live` targets. | + +Environment variables use uppercase names with underscores, for example `CHALLENGER_AUTO_BASE_URL`. + +## IDE Run Profiles + +`uk.co.compendiumdev.sparkstart.ChallengerAutoRunProfiles` contains convenience methods that can be right-clicked in IntelliJ: + +- `localMemorySinglePlayer()` +- `localMemoryMultiPlayer()` +- `localSqliteMemorySinglePlayer()` +- `localSqliteMemoryMultiPlayer()` +- `existingLocal()` +- `liveSmoke()` + +The class name deliberately does not end in `Test`, `Tests`, or `TestCase`, so normal Surefire discovery does not run every profile automatically. Each method delegates to the same harness used by Maven property runs and performs smoke checks against `/heartbeat` and `/challenges`. + +## Harness Tests + +The harness has its own focused tests: + +```powershell +mvn -pl challengerAuto -am ` + "-Dtest=ChallengerAutoConfigTest,ChallengerAutoLocalArgsTest,ChallengerAutoRuntimeHarnessCheck,ChallengerAutoSmokeTest" ` + "-DfailIfNoTests=false" test +``` + +These explicit harness checks cover: + +- config parsing, defaults, aliases, invalid values, and property/env precedence; +- generated `ChallengeMain` args for repository and player-mode variants; +- owned local JVM startup for memory single-player and SQLite-memory multiplayer; +- smoke endpoints for the selected target; +- external target safety defaults. + +Use the harness checks when changing `challengerAuto` startup/config behavior. `ChallengerAutoRuntimeHarnessCheck` is deliberately not part of normal Surefire discovery because it starts additional local Challenger modes. Use the full suite commands above when validating Challenger behavior in a specific environment. diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/ChallengerAutoSmokeTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/ChallengerAutoSmokeTest.java new file mode 100644 index 00000000..ea218ed3 --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/ChallengerAutoSmokeTest.java @@ -0,0 +1,29 @@ +package uk.co.compendiumdev.challenger.restassured; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.sparkstart.Environment; + +public class ChallengerAutoSmokeTest { + + @Test + void heartbeatEndpointIsAvailable() { + RestAssured. + given(). + get(Environment.getEnv("/heartbeat")). + then(). + statusCode(204); + } + + @Test + void challengesEndpointIsAvailable() { + RestAssured. + given(). + accept(ContentType.JSON). + get(Environment.getEnv("/challenges")). + then(). + statusCode(200). + contentType(ContentType.JSON); + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/api/RestAssuredBaseTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/api/RestAssuredBaseTest.java index 05f274d5..1ae2a210 100644 --- a/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/api/RestAssuredBaseTest.java +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/challenger/restassured/api/RestAssuredBaseTest.java @@ -1,11 +1,10 @@ package uk.co.compendiumdev.challenger.restassured.api; import io.restassured.RestAssured; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import uk.co.compendiumdev.sparkstart.Environment; -import java.io.File; - public class RestAssuredBaseTest { static String environment=""; @@ -13,6 +12,10 @@ public class RestAssuredBaseTest { @BeforeAll static void enableEnv(){ + Assumptions.assumeTrue( + Environment.shouldRunFullSuite(), + Environment.fullSuiteSkipReason()); + environment = Environment.getBaseUri(); // switch on logging for RestAssured requests @@ -23,18 +26,6 @@ static void enableEnv(){ //RestAssured.proxy("localhost",8888); if(xChallenger.isEmpty()){ - - // because we are unit tests we are running in single player mode so delete the single player data - try { - final File folder = new File(System.getProperty("User.dir"), "challengersessions"); - final File dataFile = new File(folder, "rest-api-challenges-single-player.data.txt"); - if(dataFile.exists()){ - dataFile.delete(); - } - }catch(Exception e){ - - } - xChallenger = RestAssured. given(). post(Environment.getEnv( "/challenger")). diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/simpleapi/BasicSimpleApiCrudCoverageTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/simpleapi/BasicSimpleApiCrudCoverageTest.java index 314575b9..1202ea7e 100644 --- a/challengerAuto/src/test/java/uk/co/compendiumdev/simpleapi/BasicSimpleApiCrudCoverageTest.java +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/simpleapi/BasicSimpleApiCrudCoverageTest.java @@ -5,6 +5,7 @@ import io.restassured.filter.log.ResponseLoggingFilter; import io.restassured.http.ContentType; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.simpleapi.payloads.Item; @@ -15,12 +16,13 @@ public class BasicSimpleApiCrudCoverageTest { - String apiPathPrefix = Environment.getBaseUri(); - // TODO: risk that the isbns here are not unique and tests fail intermittently @BeforeAll static void logRestAssuredCalls(){ + Assumptions.assumeTrue( + Environment.shouldRunFullSuite(), + Environment.fullSuiteSkipReason()); RestAssured.filters(new RequestLoggingFilter(), new ResponseLoggingFilter()); } @@ -126,6 +128,6 @@ void canDeleteAnItem(){ } private String apiPath(String postfix) { - return apiPathPrefix + "/simpleapi" + postfix; + return Environment.getBaseUri() + "/simpleapi" + postfix; } } diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfig.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfig.java new file mode 100644 index 00000000..973c7783 --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfig.java @@ -0,0 +1,429 @@ +package uk.co.compendiumdev.sparkstart; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +public final class ChallengerAutoConfig { + + public static final String PROPERTY_TARGET = "challenger.auto.target"; + public static final String PROPERTY_BASE_URL = "challenger.auto.baseUrl"; + public static final String PROPERTY_LOCAL_REPOSITORY = "challenger.auto.local.repository"; + public static final String PROPERTY_LOCAL_PLAYER_MODE = "challenger.auto.local.playerMode"; + public static final String PROPERTY_LOCAL_PORT = "challenger.auto.local.port"; + public static final String PROPERTY_LOCAL_EXTRA_ARGS = "challenger.auto.local.extraArgs"; + public static final String PROPERTY_EXTERNAL_FULL = "challenger.auto.external.full"; + + public static final String DEFAULT_EXISTING_BASE_URL = "http://localhost:4567"; + public static final String DEFAULT_LIVE_BASE_URL = "https://apichallenges.eviltester.com"; + + public enum Target { + LOCAL, + EXISTING, + LIVE + } + + public enum Repository { + MEMORY, + SQLITE_MEMORY, + SQLITE_FILE + } + + public enum PlayerMode { + SINGLE, + MULTI + } + + private final Target target; + private final String baseUrl; + private final Repository localRepository; + private final PlayerMode localPlayerMode; + private final String localPort; + private final List localExtraArgs; + private final boolean externalFull; + + private ChallengerAutoConfig( + final Target target, + final String baseUrl, + final Repository localRepository, + final PlayerMode localPlayerMode, + final String localPort, + final List localExtraArgs, + final boolean externalFull) { + this.target = target; + this.baseUrl = normalizeBaseUrl(baseUrl); + this.localRepository = localRepository; + this.localPlayerMode = localPlayerMode; + this.localPort = localPort; + this.localExtraArgs = Collections.unmodifiableList(new ArrayList<>(localExtraArgs)); + this.externalFull = externalFull; + validate(); + } + + public static ChallengerAutoConfig current() { + return from(systemPropertiesAsMap(), System.getenv()); + } + + public static ChallengerAutoConfig from( + final Map properties, + final Map environment) { + Map props = properties == null ? Collections.emptyMap() : properties; + Map env = environment == null ? Collections.emptyMap() : environment; + + Target target = parseTarget(valueFor(props, env, PROPERTY_TARGET, "local")); + Repository repository = parseRepository( + valueFor(props, env, PROPERTY_LOCAL_REPOSITORY, "sqlite-memory")); + PlayerMode playerMode = parsePlayerMode( + valueFor(props, env, PROPERTY_LOCAL_PLAYER_MODE, "multi")); + String port = normalizePort(valueFor(props, env, PROPERTY_LOCAL_PORT, "auto")); + List extraArgs = parseExtraArgs(valueFor(props, env, PROPERTY_LOCAL_EXTRA_ARGS, "")); + boolean externalFull = parseBoolean(valueFor(props, env, PROPERTY_EXTERNAL_FULL, "false")); + + String baseUrl = configuredBaseUrlFor(target, props, env); + + return new ChallengerAutoConfig( + target, + baseUrl, + repository, + playerMode, + port, + extraArgs, + externalFull); + } + + public static ChallengerAutoConfig localProfile( + final Repository repository, + final PlayerMode playerMode) { + Map properties = new LinkedHashMap<>(); + properties.put(PROPERTY_TARGET, "local"); + properties.put(PROPERTY_LOCAL_REPOSITORY, repositoryName(repository)); + properties.put(PROPERTY_LOCAL_PLAYER_MODE, playerModeName(playerMode)); + return from(properties, System.getenv()); + } + + public static ChallengerAutoConfig existingLocal() { + return from(Map.of( + PROPERTY_TARGET, "existing", + PROPERTY_BASE_URL, DEFAULT_EXISTING_BASE_URL), System.getenv()); + } + + public static ChallengerAutoConfig liveSmoke() { + return from(Map.of(PROPERTY_TARGET, "live"), System.getenv()); + } + + public Target getTarget() { + return target; + } + + public String getBaseUrl() { + return baseUrl; + } + + public Repository getLocalRepository() { + return localRepository; + } + + public PlayerMode getLocalPlayerMode() { + return localPlayerMode; + } + + public String getLocalPort() { + return localPort; + } + + public boolean isAutoPort() { + return "auto".equals(localPort); + } + + public int fixedPort() { + if (isAutoPort()) { + throw new IllegalStateException("Local port is configured as auto"); + } + return Integer.parseInt(localPort); + } + + public List getLocalExtraArgs() { + return localExtraArgs; + } + + public boolean isExternalFull() { + return externalFull; + } + + public boolean isExternalTarget() { + return target == Target.LIVE; + } + + public boolean startsOwnedLocalProcess() { + return target == Target.LOCAL; + } + + public boolean shouldRunFullSuite() { + return !isExternalTarget() || externalFull; + } + + public String fullSuiteSkipReason() { + if (shouldRunFullSuite()) { + return ""; + } + return "External Challenger target is smoke-only by default; set -D" + + PROPERTY_EXTERNAL_FULL + "=true to run the mutating suite."; + } + + public List challengeMainArgs(final int port) { + List args = new ArrayList<>(); + args.add("-port=" + port); + + switch (localRepository) { + case MEMORY: + args.add("-thingifier-repository=memory"); + break; + case SQLITE_MEMORY: + args.add("-sqlite-memory"); + break; + case SQLITE_FILE: + args.add("-thingifier-repository=sqlite-file"); + break; + default: + throw new IllegalStateException("Unsupported repository " + localRepository); + } + + if (localPlayerMode == PlayerMode.MULTI) { + args.add("-multiplayer"); + } + + args.addAll(localExtraArgs); + return args; + } + + @Override + public String toString() { + return "ChallengerAutoConfig{" + + "target=" + target + + ", baseUrl='" + baseUrl + '\'' + + ", localRepository=" + localRepository + + ", localPlayerMode=" + localPlayerMode + + ", localPort='" + localPort + '\'' + + ", localExtraArgs=" + localExtraArgs + + ", externalFull=" + externalFull + + '}'; + } + + private void validate() { + } + + private static Map systemPropertiesAsMap() { + Properties properties = System.getProperties(); + Map values = new LinkedHashMap<>(); + for (String name : properties.stringPropertyNames()) { + values.put(name, properties.getProperty(name)); + } + return values; + } + + private static String configuredBaseUrlFor( + final Target target, + final Map properties, + final Map environment) { + switch (target) { + case LOCAL: + return ""; + case EXISTING: + return valueFor(properties, environment, PROPERTY_BASE_URL, DEFAULT_EXISTING_BASE_URL); + case LIVE: + return valueFor(properties, environment, PROPERTY_BASE_URL, DEFAULT_LIVE_BASE_URL); + default: + throw new IllegalArgumentException("Unsupported target " + target); + } + } + + private static String valueFor( + final Map properties, + final Map environment, + final String propertyName, + final String defaultValue) { + String propertyValue = trimToNull(properties.get(propertyName)); + if (propertyValue != null) { + return propertyValue; + } + + String environmentValue = trimToNull(environment.get(envName(propertyName))); + if (environmentValue == null) { + environmentValue = trimToNull(environment.get(legacyEnvName(propertyName))); + } + if (environmentValue != null) { + return environmentValue; + } + + return defaultValue; + } + + private static Target parseTarget(final String value) { + String normalized = normalize(value); + switch (normalized) { + case "local": + case "owned-local": + return Target.LOCAL; + case "existing": + case "existing-local": + case "local-existing": + return Target.EXISTING; + case "live": + case "prod": + case "production": + return Target.LIVE; + default: + throw new IllegalArgumentException( + "Unknown challenger auto target " + value + + ". Expected local, existing, or live."); + } + } + + private static Repository parseRepository(final String value) { + String normalized = normalize(value); + switch (normalized) { + case "memory": + case "in-memory": + case "custom-memory": + return Repository.MEMORY; + case "sqlite": + case "sqlite-memory": + case "sqlite-in-memory": + return Repository.SQLITE_MEMORY; + case "sqlite-file": + case "sqlite-disk": + case "file": + return Repository.SQLITE_FILE; + default: + throw new IllegalArgumentException( + "Unknown local repository " + value + + ". Expected memory, sqlite-memory, or sqlite-file."); + } + } + + private static PlayerMode parsePlayerMode(final String value) { + String normalized = normalize(value); + switch (normalized) { + case "single": + case "single-player": + case "singleplayer": + return PlayerMode.SINGLE; + case "multi": + case "multiplayer": + case "multi-player": + case "multiuser": + case "multi-user": + return PlayerMode.MULTI; + default: + throw new IllegalArgumentException( + "Unknown local player mode " + value + + ". Expected single or multi."); + } + } + + private static String normalizePort(final String value) { + String port = trimToEmpty(value).toLowerCase(Locale.ROOT); + if (port.isEmpty() || "auto".equals(port)) { + return "auto"; + } + try { + int parsed = Integer.parseInt(port); + if (parsed < 1 || parsed > 65535) { + throw new IllegalArgumentException("Local port out of range: " + value); + } + return String.valueOf(parsed); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Invalid local port " + value + ". Expected auto or a port number.", e); + } + } + + private static List parseExtraArgs(final String value) { + List args = new ArrayList<>(); + if (isBlank(value)) { + return args; + } + for (String part : value.split(",")) { + String arg = part.trim(); + if (!arg.isEmpty()) { + args.add(arg); + } + } + return args; + } + + private static boolean parseBoolean(final String value) { + String normalized = normalize(value); + return "true".equals(normalized) || + "yes".equals(normalized) || + "y".equals(normalized) || + "1".equals(normalized); + } + + private static String repositoryName(final Repository repository) { + switch (repository) { + case MEMORY: + return "memory"; + case SQLITE_MEMORY: + return "sqlite-memory"; + case SQLITE_FILE: + return "sqlite-file"; + default: + throw new IllegalStateException("Unknown repository " + repository); + } + } + + private static String playerModeName(final PlayerMode playerMode) { + switch (playerMode) { + case SINGLE: + return "single"; + case MULTI: + return "multi"; + default: + throw new IllegalStateException("Unknown player mode " + playerMode); + } + } + + private static String envName(final String propertyName) { + return propertyName. + replaceAll("([a-z])([A-Z])", "$1_$2"). + toUpperCase(Locale.ROOT). + replace('.', '_'); + } + + private static String legacyEnvName(final String propertyName) { + return propertyName.toUpperCase(Locale.ROOT).replace('.', '_'); + } + + private static String normalize(final String value) { + return trimToEmpty(value). + toLowerCase(Locale.ROOT). + replace('_', '-'). + replace(' ', '-'); + } + + private static String normalizeBaseUrl(final String value) { + String baseUrl = trimToEmpty(value); + while (baseUrl.endsWith("/") && baseUrl.length() > "https://".length()) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + return baseUrl; + } + + private static String trimToEmpty(final String value) { + return value == null ? "" : value.trim(); + } + + private static String trimToNull(final String value) { + String trimmed = trimToEmpty(value); + return trimmed.isEmpty() ? null : trimmed; + } + + private static boolean isBlank(final String value) { + return value == null || value.trim().isEmpty(); + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfigTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfigTest.java new file mode 100644 index 00000000..aac983fe --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoConfigTest.java @@ -0,0 +1,159 @@ +package uk.co.compendiumdev.sparkstart; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class ChallengerAutoConfigTest { + + @Test + void defaultsToOwnedLocalSqliteMemoryMultiplayerOnAutoPort() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Collections.emptyMap(), Collections.emptyMap()); + + Assertions.assertEquals(ChallengerAutoConfig.Target.LOCAL, config.getTarget()); + Assertions.assertEquals( + ChallengerAutoConfig.Repository.SQLITE_MEMORY, + config.getLocalRepository()); + Assertions.assertEquals( + ChallengerAutoConfig.PlayerMode.MULTI, + config.getLocalPlayerMode()); + Assertions.assertTrue(config.isAutoPort()); + Assertions.assertTrue(config.shouldRunFullSuite()); + } + + @Test + void systemPropertiesOverrideEnvironmentValues() { + Map properties = Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "existing", + ChallengerAutoConfig.PROPERTY_BASE_URL, "http://localhost:4567"); + Map environment = Map.of( + "CHALLENGER_AUTO_TARGET", "live", + "CHALLENGER_AUTO_BASE_URL", "https://example.invalid"); + + ChallengerAutoConfig config = ChallengerAutoConfig.from(properties, environment); + + Assertions.assertEquals(ChallengerAutoConfig.Target.EXISTING, config.getTarget()); + Assertions.assertEquals("http://localhost:4567", config.getBaseUrl()); + } + + @Test + void existingDefaultsToLocalhostWhenBaseUrlNotProvided() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_TARGET, "existing"), + Collections.emptyMap()); + + Assertions.assertEquals("http://localhost:4567", config.getBaseUrl()); + } + + @Test + void liveDefaultsToPublicApiChallengesHost() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_TARGET, "live"), + Collections.emptyMap()); + + Assertions.assertEquals("https://apichallenges.eviltester.com", config.getBaseUrl()); + Assertions.assertFalse(config.shouldRunFullSuite()); + } + + @Test + void liveCanOverrideBaseUrlForRailwayOrOtherRemoteEnvironments() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "live", + ChallengerAutoConfig.PROPERTY_BASE_URL, "https://railway.example"), + Collections.emptyMap()); + + Assertions.assertEquals(ChallengerAutoConfig.Target.LIVE, config.getTarget()); + Assertions.assertEquals("https://railway.example", config.getBaseUrl()); + } + + @Test + void externalFullOptInAllowsMutatingSuiteForExternalTargets() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "live", + ChallengerAutoConfig.PROPERTY_EXTERNAL_FULL, "true"), + Collections.emptyMap()); + + Assertions.assertTrue(config.shouldRunFullSuite()); + } + + @Test + void aliasesAreAcceptedForRepositoryAndPlayerMode() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "in_memory", + ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "multi-user"), + Collections.emptyMap()); + + Assertions.assertEquals( + ChallengerAutoConfig.Repository.MEMORY, + config.getLocalRepository()); + Assertions.assertEquals( + ChallengerAutoConfig.PlayerMode.MULTI, + config.getLocalPlayerMode()); + } + + @Test + void invalidTargetIsRejected() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_TARGET, "somewhere"), + Collections.emptyMap())); + } + + @Test + void invalidRepositoryIsRejected() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "postgres"), + Collections.emptyMap())); + } + + @Test + void invalidPlayerModeIsRejected() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "co-op"), + Collections.emptyMap())); + } + + @Test + void invalidPortIsRejected() { + Assertions.assertThrows( + IllegalArgumentException.class, + () -> ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_LOCAL_PORT, "not-a-port"), + Collections.emptyMap())); + } + + @Test + void baseUrlHasTrailingSlashesRemoved() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "existing", + ChallengerAutoConfig.PROPERTY_BASE_URL, "http://localhost:4567///"), + Collections.emptyMap()); + + Assertions.assertEquals("http://localhost:4567", config.getBaseUrl()); + } + + @Test + void environmentCanProvideGeneralBaseUrl() { + Map env = new HashMap<>(); + env.put("CHALLENGER_AUTO_TARGET", "existing"); + env.put("CHALLENGER_AUTO_BASE_URL", "http://localhost:5678"); + + ChallengerAutoConfig config = ChallengerAutoConfig.from(Collections.emptyMap(), env); + + Assertions.assertEquals(ChallengerAutoConfig.Target.EXISTING, config.getTarget()); + Assertions.assertEquals("http://localhost:5678", config.getBaseUrl()); + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoLocalArgsTest.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoLocalArgsTest.java new file mode 100644 index 00000000..f3efacda --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoLocalArgsTest.java @@ -0,0 +1,68 @@ +package uk.co.compendiumdev.sparkstart; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class ChallengerAutoLocalArgsTest { + + @Test + void memorySinglePlayerArgsUseMemoryRepositoryAndNoMultiplayerFlag() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "memory", + ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "single"), + Collections.emptyMap()); + + Assertions.assertEquals( + List.of("-port=4567", "-thingifier-repository=memory"), + config.challengeMainArgs(4567)); + } + + @Test + void sqliteMemoryMultiplayerArgsUseExistingShortcutFlag() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "sqlite-memory", + ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "multi"), + Collections.emptyMap()); + + Assertions.assertEquals( + List.of("-port=4568", "-sqlite-memory", "-multiplayer"), + config.challengeMainArgs(4568)); + } + + @Test + void sqliteFileArgsUseRepositoryModeFlag() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "sqlite-file", + ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "single"), + Collections.emptyMap()); + + Assertions.assertEquals( + List.of("-port=4569", "-thingifier-repository=sqlite-file"), + config.challengeMainArgs(4569)); + } + + @Test + void extraArgsAreAppendedInOrder() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_LOCAL_EXTRA_ARGS, + "-unlimitedtodos,-enableadminapi"), + Collections.emptyMap()); + + Assertions.assertEquals( + List.of( + "-port=4570", + "-sqlite-memory", + "-multiplayer", + "-unlimitedtodos", + "-enableadminapi"), + config.challengeMainArgs(4570)); + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRunProfiles.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRunProfiles.java new file mode 100644 index 00000000..cefbf9ff --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRunProfiles.java @@ -0,0 +1,71 @@ +package uk.co.compendiumdev.sparkstart; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.Test; + +/** + * IDE convenience runners. This class name deliberately avoids Surefire's + * default *Test naming pattern so each profile can be right-clicked manually. + */ +public class ChallengerAutoRunProfiles { + + @Test + void localMemorySinglePlayer() { + runProfile(ChallengerAutoConfig.localProfile( + ChallengerAutoConfig.Repository.MEMORY, + ChallengerAutoConfig.PlayerMode.SINGLE)); + } + + @Test + void localMemoryMultiPlayer() { + runProfile(ChallengerAutoConfig.localProfile( + ChallengerAutoConfig.Repository.MEMORY, + ChallengerAutoConfig.PlayerMode.MULTI)); + } + + @Test + void localSqliteMemorySinglePlayer() { + runProfile(ChallengerAutoConfig.localProfile( + ChallengerAutoConfig.Repository.SQLITE_MEMORY, + ChallengerAutoConfig.PlayerMode.SINGLE)); + } + + @Test + void localSqliteMemoryMultiPlayer() { + runProfile(ChallengerAutoConfig.localProfile( + ChallengerAutoConfig.Repository.SQLITE_MEMORY, + ChallengerAutoConfig.PlayerMode.MULTI)); + } + + @Test + void existingLocal() { + runProfile(ChallengerAutoConfig.existingLocal()); + } + + @Test + void liveSmoke() { + runProfile(ChallengerAutoConfig.liveSmoke()); + } + + private void runProfile(final ChallengerAutoConfig config) { + Environment.useConfiguration(config); + assertSmokeEndpoints(Environment.getBaseUri()); + } + + private void assertSmokeEndpoints(final String baseUrl) { + RestAssured. + given(). + get(baseUrl + "/heartbeat"). + then(). + statusCode(204); + + RestAssured. + given(). + accept(ContentType.JSON). + get(baseUrl + "/challenges"). + then(). + statusCode(200). + contentType(ContentType.JSON); + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntime.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntime.java new file mode 100644 index 00000000..6c6d44fd --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntime.java @@ -0,0 +1,282 @@ +package uk.co.compendiumdev.sparkstart; + +import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public final class ChallengerAutoRuntime implements AutoCloseable { + + private static ChallengerAutoRuntime currentRuntime; + private static ChallengerAutoConfig currentConfig; + private static boolean configurationOverridden; + + private final ChallengerAutoConfig config; + private final String baseUrl; + private final Process localProcess; + private final File logFile; + private boolean closed; + + private ChallengerAutoRuntime( + final ChallengerAutoConfig config, + final String baseUrl, + final Process localProcess, + final File logFile) { + this.config = config; + this.baseUrl = baseUrl; + this.localProcess = localProcess; + this.logFile = logFile; + this.closed = false; + } + + public static synchronized ChallengerAutoRuntime current() { + if (currentRuntime != null && configurationOverridden) { + return currentRuntime; + } + + ChallengerAutoConfig config = ChallengerAutoConfig.current(); + if (currentRuntime == null || + currentConfig == null || + !currentConfig.toString().equals(config.toString())) { + closeCurrentRuntime(); + currentRuntime = start(config); + currentConfig = config; + } + return currentRuntime; + } + + public static synchronized ChallengerAutoConfig currentConfig() { + if (currentConfig == null) { + currentConfig = ChallengerAutoConfig.current(); + } + return currentConfig; + } + + public static synchronized void useConfiguration(final ChallengerAutoConfig config) { + closeCurrentRuntime(); + currentRuntime = start(config); + currentConfig = config; + configurationOverridden = true; + } + + public static synchronized void reset() { + closeCurrentRuntime(); + currentConfig = null; + configurationOverridden = false; + } + + public static ChallengerAutoRuntime start(final ChallengerAutoConfig config) { + if (config.startsOwnedLocalProcess()) { + return startOwnedLocal(config); + } + + ChallengerAutoRuntime runtime = new ChallengerAutoRuntime( + config, config.getBaseUrl(), null, null); + runtime.waitUntilReady(); + return runtime; + } + + public String getBaseUrl() { + return baseUrl; + } + + public ChallengerAutoConfig getConfig() { + return config; + } + + public boolean ownsLocalProcess() { + return localProcess != null; + } + + @Override + public synchronized void close() { + if (closed) { + return; + } + closed = true; + + if (localProcess == null) { + return; + } + + requestShutdown(); + try { + if (!localProcess.waitFor(5, TimeUnit.SECONDS)) { + localProcess.destroy(); + } + if (!localProcess.waitFor(5, TimeUnit.SECONDS)) { + localProcess.destroyForcibly(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + localProcess.destroyForcibly(); + } + } + + private static ChallengerAutoRuntime startOwnedLocal(final ChallengerAutoConfig config) { + int port = config.isAutoPort() ? findFreePort() : config.fixedPort(); + if (!config.isAutoPort() && portIsOpen("localhost", port, 250)) { + throw new IllegalStateException( + "Configured Challenger local port is already in use: " + port + + ". Use -D" + ChallengerAutoConfig.PROPERTY_LOCAL_PORT + "=auto " + + "or target=existing to attach to it."); + } + + deleteSinglePlayerDataIfNeeded(config); + + File targetDirectory = new File(System.getProperty("user.dir"), "target"); + if (!targetDirectory.exists() && !targetDirectory.mkdirs()) { + throw new IllegalStateException("Could not create " + targetDirectory.getAbsolutePath()); + } + File logFile = new File(targetDirectory, "challenger-auto-local-" + port + ".log"); + + Process process = startProcess(config, port, logFile); + String baseUrl = "http://localhost:" + port; + ChallengerAutoRuntime runtime = new ChallengerAutoRuntime(config, baseUrl, process, logFile); + Runtime.getRuntime().addShutdownHook(new Thread(runtime::close)); + runtime.waitUntilReady(); + return runtime; + } + + private static Process startProcess( + final ChallengerAutoConfig config, + final int port, + final File logFile) { + List command = new ArrayList<>(); + command.add(javaBinary()); + command.add("-cp"); + command.add(testClasspath()); + command.add("uk.co.compendiumdev.challenge.ChallengeMain"); + command.addAll(config.challengeMainArgs(port)); + + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.directory(new File(System.getProperty("user.dir"))); + processBuilder.redirectErrorStream(true); + processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile)); + + try { + return processBuilder.start(); + } catch (IOException e) { + throw new IllegalStateException( + "Could not start Challenger local process. Command: " + command, e); + } + } + + private void waitUntilReady() { + long deadline = System.currentTimeMillis() + 30_000; + String heartbeat = baseUrl + "/heartbeat"; + while (System.currentTimeMillis() < deadline) { + if (localProcess != null && !localProcess.isAlive()) { + throw new IllegalStateException( + "Challenger local process exited before readiness. Log: " + + logFile.getAbsolutePath()); + } + if (httpStatus(heartbeat) == 204) { + return; + } + sleep(250); + } + String message = "Challenger target did not become ready at " + heartbeat; + if (logFile != null) { + message += ". Log: " + logFile.getAbsolutePath(); + } + throw new IllegalStateException(message); + } + + private void requestShutdown() { + httpStatus(baseUrl + "/shutdown"); + } + + private static int httpStatus(final String url) { + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setConnectTimeout(1000); + connection.setReadTimeout(1000); + connection.setRequestMethod("GET"); + return connection.getResponseCode(); + } catch (IOException e) { + return -1; + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + + private static void deleteSinglePlayerDataIfNeeded(final ChallengerAutoConfig config) { + if (config.getLocalPlayerMode() != ChallengerAutoConfig.PlayerMode.SINGLE) { + return; + } + File folder = new File(System.getProperty("user.dir"), "challengersessions"); + File dataFile = new File(folder, "rest-api-challenges-single-player.data.txt"); + if (dataFile.exists() && !dataFile.delete()) { + throw new IllegalStateException( + "Could not delete single-player data file " + dataFile.getAbsolutePath()); + } + } + + private static int findFreePort() { + try (ServerSocket socket = new ServerSocket(0)) { + socket.setReuseAddress(true); + return socket.getLocalPort(); + } catch (IOException e) { + throw new IllegalStateException("Could not find a free local port", e); + } + } + + private static boolean portIsOpen( + final String host, + final int port, + final int timeoutMillis) { + try (Socket socket = new Socket()) { + socket.connect(new java.net.InetSocketAddress(host, port), timeoutMillis); + return true; + } catch (IOException e) { + return false; + } + } + + private static String javaBinary() { + String executable = isWindows() ? "java.exe" : "java"; + Path java = Paths.get(System.getProperty("java.home"), "bin", executable); + return java.toString(); + } + + private static boolean isWindows() { + return System.getProperty("os.name", ""). + toLowerCase(java.util.Locale.ROOT). + contains("win"); + } + + private static String testClasspath() { + String surefireClasspath = System.getProperty("surefire.test.class.path"); + if (surefireClasspath != null && !surefireClasspath.trim().isEmpty()) { + return surefireClasspath; + } + return System.getProperty("java.class.path"); + } + + private static void closeCurrentRuntime() { + if (currentRuntime != null) { + currentRuntime.close(); + currentRuntime = null; + } + } + + private static void sleep(final long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException("Interrupted waiting for Challenger readiness", e); + } + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntimeHarnessCheck.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntimeHarnessCheck.java new file mode 100644 index 00000000..b46b24d4 --- /dev/null +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/ChallengerAutoRuntimeHarnessCheck.java @@ -0,0 +1,83 @@ +package uk.co.compendiumdev.sparkstart; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Map; + +public class ChallengerAutoRuntimeHarnessCheck { + + @Test + void localMemorySinglePlayerCanStartInOwnedJvm() { + assumeCurrentRunAllowsOwnedLocalHarnesses(); + + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "local", + ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "memory", + ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "single", + ChallengerAutoConfig.PROPERTY_LOCAL_PORT, "auto"), + Collections.emptyMap()); + + assertOwnedLocalRuntimeStarts(config); + } + + @Test + void localSqliteMemoryMultiplayerCanStartInOwnedJvm() { + assumeCurrentRunAllowsOwnedLocalHarnesses(); + + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "local", + ChallengerAutoConfig.PROPERTY_LOCAL_REPOSITORY, "sqlite-memory", + ChallengerAutoConfig.PROPERTY_LOCAL_PLAYER_MODE, "multi", + ChallengerAutoConfig.PROPERTY_LOCAL_PORT, "auto"), + Collections.emptyMap()); + + assertOwnedLocalRuntimeStarts(config); + } + + @Test + void existingTargetDoesNotRepresentOwnedLocalProcess() { + ChallengerAutoConfig config = ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_TARGET, "existing"), + Collections.emptyMap()); + + Assertions.assertFalse(config.startsOwnedLocalProcess()); + } + + @Test + void externalTargetsAreSmokeOnlyUnlessOptedIn() { + ChallengerAutoConfig smokeOnly = ChallengerAutoConfig.from( + Map.of(ChallengerAutoConfig.PROPERTY_TARGET, "live"), + Collections.emptyMap()); + ChallengerAutoConfig full = ChallengerAutoConfig.from( + Map.of( + ChallengerAutoConfig.PROPERTY_TARGET, "live", + ChallengerAutoConfig.PROPERTY_EXTERNAL_FULL, "true"), + Collections.emptyMap()); + + Assertions.assertFalse(smokeOnly.shouldRunFullSuite()); + Assertions.assertTrue(full.shouldRunFullSuite()); + } + + private void assertOwnedLocalRuntimeStarts(final ChallengerAutoConfig config) { + try (ChallengerAutoRuntime runtime = ChallengerAutoRuntime.start(config)) { + Assertions.assertTrue(runtime.ownsLocalProcess()); + RestAssured. + given(). + get(runtime.getBaseUrl() + "/heartbeat"). + then(). + statusCode(204); + } + } + + private void assumeCurrentRunAllowsOwnedLocalHarnesses() { + Assumptions.assumeTrue( + ChallengerAutoConfig.current().getTarget() == ChallengerAutoConfig.Target.LOCAL, + "Owned local harness tests run only for challenger.auto.target=local"); + } +} diff --git a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/Environment.java b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/Environment.java index 5f06988f..521ca4be 100644 --- a/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/Environment.java +++ b/challengerAuto/src/test/java/uk/co/compendiumdev/sparkstart/Environment.java @@ -1,65 +1,29 @@ package uk.co.compendiumdev.sparkstart; -import spark.Spark; -import uk.co.compendiumdev.challenge.ChallengeMain; - -import java.lang.reflect.Array; -import java.util.ArrayList; - public class Environment { - // these integration tests can work in both single player and multi-player modes - public static boolean SINGLE_PLAYER_MODE = false; - public static String getEnv(String urlPath){ return getBaseUri() + urlPath; } public static String getBaseUri() { + return ChallengerAutoRuntime.current().getBaseUrl(); + } - // if(true) - // return "https://thingifier-production.up.railway.app"; - - // return environment if want to run externally -// if(true) -// return "https://apichallenges.eviltester.com"; - -// if(true) -// return "http://localhost:4567"; - - // if not running then start the spark - if(Port.inUse("localhost", 4567)) { - return "http://localhost:4567"; - }else{ - //start it up - Spark.port(4567); - String [] args; - - if(SINGLE_PLAYER_MODE){ - args = "-sqlite-memory".split(","); - }else{ - args = "-sqlite-memory,-multiplayer".split(","); - } - - - ChallengeMain.main(args); + public static boolean shouldRunFullSuite() { + return ChallengerAutoRuntime.currentConfig().shouldRunFullSuite(); + } - // wait till running - int maxtries=10; - while(!Port.inUse("localhost", 4567)){ - maxtries--; - try { - Thread.sleep(500); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + public static String fullSuiteSkipReason() { + return ChallengerAutoRuntime.currentConfig().fullSuiteSkipReason(); + } - return "http://localhost:4567"; - } + public static void useConfiguration(final ChallengerAutoConfig config) { + ChallengerAutoRuntime.useConfiguration(config); + } - // TODO: incorporate browsermob proxy and allow configuration of all - // requests through a proxy file to output a HAR file of all requests for later review + public static void reset() { + ChallengerAutoRuntime.reset(); } } From ab3076f55f39c982b7ae73423db12057958f896e Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 7 Jul 2026 21:07:53 +0100 Subject: [PATCH 03/22] Use repository-native query and export paths --- .../ChallengerApiResponseHook.java | 61 ++++- .../ChallengerTrackingRoutes.java | 6 +- .../challenge/gui/ChallengerWebGUI.java | 5 +- .../ChallengerApiResponseHookTest.java | 218 ++++++++++++++++++ .../thingifier/core/EntityRelModel.java | 16 ++ ...mpleQuery.java => RepositoryUrlQuery.java} | 10 +- .../thingifier/core/query/SimpleQuery.java | 2 +- .../{QueryResult.java => UrlQueryResult.java} | 2 +- .../core/reporting/ERModelReport.java | 22 +- .../reporting/RepositoryJsonExporter.java | 97 ++++++++ .../repository/InMemoryThingRepository.java | 9 + .../repository/SqliteThingRepository.java | 84 +++++++ .../core/repository/ThingRepository.java | 12 + .../core/query/QueryFiltersBooleanTest.java | 17 +- .../core/query/QueryFiltersFloatTest.java | 21 +- .../core/query/QueryFiltersIdTest.java | 7 +- .../core/query/QueryFiltersIntegerTest.java | 27 ++- .../core/query/QueryFiltersStringTest.java | 21 +- .../query/RepositoryUrlQueryTestSupport.java | 16 ++ .../query/SortingViaQueryFiltersTest.java | 15 +- .../query/TodoManagerQueryEngineTest.java | 64 +++-- .../ThingRepositoryContractTest.java | 136 ++++++++++- .../compendiumdev/thingifier/Thingifier.java | 8 + .../restapihandlers/RestApiGetHandler.java | 10 +- .../thingifier/reporting/ThingReporter.java | 5 +- 25 files changed, 761 insertions(+), 130 deletions(-) create mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/{RepositoryBackedSimpleQuery.java => RepositoryUrlQuery.java} (94%) rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/{QueryResult.java => UrlQueryResult.java} (93%) create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/RepositoryJsonExporter.java create mode 100644 ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java index e22d72df..75ed6a33 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java @@ -5,7 +5,6 @@ import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.challengers.Challengers; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.ContentTypeHeaderParser; import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.AcceptHeaderParser; @@ -13,7 +12,10 @@ import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.application.httpapimessagehooks.HttpApiResponseHook; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + import java.util.Collection; @@ -125,9 +127,8 @@ public HttpApiResponse run(final HttpApiRequest request, request.getQueryParams().get("doneStatus").contentEquals("true") && response.getStatusCode()==200){ // only pass if there are done and not done todos - final EntityInstanceCollection thing = thingifier.getThingInstancesNamed("todo", challenger.getXChallenger()); - final EntityInstance aDoneThing = thing.findInstanceByFieldNameAndValue("doneStatus", "true"); - final EntityInstance aNotDoneThing = thing.findInstanceByFieldNameAndValue("doneStatus", "false"); + final EntityInstance aDoneThing = findTodoByField(challenger, "doneStatus", "true"); + final EntityInstance aNotDoneThing = findTodoByField(challenger, "doneStatus", "false"); if(aDoneThing!=null && aNotDoneThing!=null) { challengers.pass(challenger,CHALLENGE.GET_TODOS_FILTERED); } @@ -159,12 +160,12 @@ public HttpApiResponse run(final HttpApiRequest request, String location = response.getHeaders().get("Location"); String[] locationParts = location.split("/"); - if(locationParts.length>1){ + if(locationParts.length>2){ // to check it is an int - int todoId = Integer.parseInt(locationParts[2]); - final EntityInstanceCollection thing = thingifier.getThingInstancesNamed("todo", challenger.getXChallenger()); - EntityInstance aTodo = thing.findInstanceByPrimaryKey(locationParts[2]); - if(aTodo.getFieldValue("title").asString().length() == 50 && + Integer.parseInt(locationParts[2]); + EntityInstance aTodo = findTodoByIdentifier(challenger, locationParts[2]); + if(aTodo != null && + aTodo.getFieldValue("title").asString().length() == 50 && aTodo.getFieldValue("description").asString().length() == 200 ){ challengers.pass(challenger, CHALLENGE.POST_MAX_OUT_TITLE_DESCRIPTION_LENGTH); @@ -286,7 +287,7 @@ public HttpApiResponse run(final HttpApiRequest request, if(request.getVerb() == HttpApiRequest.VERB.DELETE && request.getPath().matches("todos/.*") && response.getStatusCode()==200 && - thingifier.getThingInstancesNamed("todo", challenger.getXChallenger()).countInstances()==0){ + countTodos(challenger)==0){ challengers.pass(challenger,CHALLENGE.DELETE_ALL_TODOS); } @@ -294,6 +295,46 @@ public HttpApiResponse run(final HttpApiRequest request, return null; } + private EntityInstance findTodoByField( + final ChallengerAuthData challenger, + final String fieldName, + final String fieldValue) { + EntityDefinition todo = todoDefinition(); + ThingRepository repository = repositoryFor(challenger); + if (todo == null || repository == null) { + return null; + } + return repository.findInstanceByFieldNameAndValue(todo, fieldName, fieldValue); + } + + private EntityInstance findTodoByIdentifier( + final ChallengerAuthData challenger, + final String identifier) { + EntityDefinition todo = todoDefinition(); + ThingRepository repository = repositoryFor(challenger); + if (todo == null || repository == null) { + return null; + } + return repository.findInstanceByQueryIdentifier(todo, identifier); + } + + private int countTodos(final ChallengerAuthData challenger) { + EntityDefinition todo = todoDefinition(); + ThingRepository repository = repositoryFor(challenger); + if (todo == null || repository == null) { + return -1; + } + return repository.countInstances(todo); + } + + private ThingRepository repositoryFor(final ChallengerAuthData challenger) { + return thingifier.getRepository(challenger.getXChallenger()); + } + + private EntityDefinition todoDefinition() { + return thingifier.getDefinitionNamed("todo"); + } + String collate(Collection strings){ StringBuilder collated = new StringBuilder(); for(String string : strings){ diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java index c84eb160..7cda1a77 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java @@ -12,7 +12,6 @@ import uk.co.compendiumdev.thingifier.api.response.ApiResponseError; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; import java.util.*; @@ -294,14 +293,13 @@ public void configure(final Challengers challengers, challenger.touch(); result.header("content-type", "application/json"); - ERInstanceData instanceData = challengers.getErModel().getInstanceData(xChallengerGuid); - if(instanceData==null){ + if(challengers.getErModel().getRepository(xChallengerGuid)==null){ result.status(404); return ApiResponseAsJson.getErrorMessageJson("Challenger database not instantiated " + xChallengerGuid); } result.status(200); - return challengers.getErModel().getInstanceData(xChallengerGuid).asJson(); + return challengers.getErModel().exportInstanceDataAsJson(xChallengerGuid); }else{ result.status(404); return ApiResponseAsJson.getErrorMessageJson("Challenger not found " + xChallengerGuid); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java index 76de136d..46bd37ac 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java @@ -314,7 +314,8 @@ function setMenuNavBasedOnUrl(){ //reportOn = new ChallengesPayload(challengeDefinitions, challengers.SINGLE_PLAYER).getAsChallenges(); String json = "{}"; if(challengers.getErModel().getDatabaseNames().contains(EntityRelModel.DEFAULT_DATABASE_NAME)){ - json = challengers.getErModel().getInstanceData(EntityRelModel.DEFAULT_DATABASE_NAME).asJson(); + json = challengers.getErModel(). + exportInstanceDataAsJson(EntityRelModel.DEFAULT_DATABASE_NAME); } html.append(outputChallengeDataAsJS(challengers.SINGLE_PLAYER, json)); html.append(renderChallengeData(challengeDefinitions, challengers.SINGLE_PLAYER)); @@ -413,7 +414,7 @@ function setMenuNavBasedOnUrl(){ String json = "{}"; if(challengers.getErModel().getDatabaseNames().contains(xChallenger)){ - json = challengers.getErModel().getInstanceData(xChallenger).asJson(); + json = challengers.getErModel().exportInstanceDataAsJson(xChallenger); } html.append(outputChallengeDataAsJS(challenger, json)); diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java new file mode 100644 index 00000000..19c54ac3 --- /dev/null +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java @@ -0,0 +1,218 @@ +package uk.co.compendiumdev.challenge.challengehooks; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import uk.co.compendiumdev.challenge.CHALLENGE; +import uk.co.compendiumdev.challenge.ChallengerAuthData; +import uk.co.compendiumdev.challenge.challengers.Challengers; +import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; +import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; +import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; +import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; +import uk.co.compendiumdev.thingifier.api.response.ApiResponse; +import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static uk.co.compendiumdev.thingifier.api.http.HttpApiRequest.VERB.DELETE; +import static uk.co.compendiumdev.thingifier.api.http.HttpApiRequest.VERB.GET; +import static uk.co.compendiumdev.thingifier.api.http.HttpApiRequest.VERB.POST; + +public class ChallengerApiResponseHookTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("repositoryProviders") + public void filteredTodosChallengeCompletesWhenDoneAndNotDoneTodosExist( + final String repositoryName, + final Supplier providerFactory) { + + try (HookFixture fixture = new HookFixture(providerFactory.get())) { + fixture.addTodo("done", "true"); + fixture.addTodo("not done", "false"); + + fixture.hook.run( + fixture.request("todos", GET). + setQueryParams(Map.of("doneStatus", "true")), + fixture.response(200), + fixture.thingifier.apiConfig()); + + Assertions.assertTrue(fixture.challenger.statusOfChallenge(CHALLENGE.GET_TODOS_FILTERED)); + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("repositoryProviders") + public void filteredTodosChallengeDoesNotCompleteWithoutMixedDoneStatusTodos( + final String repositoryName, + final Supplier providerFactory) { + + try (HookFixture fixture = new HookFixture(providerFactory.get())) { + fixture.addTodo("done", "true"); + + fixture.hook.run( + fixture.request("todos", GET). + setQueryParams(Map.of("doneStatus", "true")), + fixture.response(200), + fixture.thingifier.apiConfig()); + + Assertions.assertFalse(fixture.challenger.statusOfChallenge(CHALLENGE.GET_TODOS_FILTERED)); + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("repositoryProviders") + public void postMaxContentChallengeReadsCreatedTodoThroughRepository( + final String repositoryName, + final Supplier providerFactory) { + + try (HookFixture fixture = new HookFixture(providerFactory.get())) { + EntityInstance todo = fixture.addTodo( + "2*4*6*8*11*14*17*20*23*26*29*32*35*38*41*44*47*50*", + "true", + "*3*5*7*9*12*15*18*21*24*27*30*33*36*39*42*45*48*51*" + + "54*57*60*63*66*69*72*75*78*81*84*87*90*93*96*100*" + + "104*108*112*116*120*124*128*132*136*140*144*148*" + + "152*156*160*164*168*172*176*180*184*188*192*196*200*"); + + ApiResponse created = new ApiResponse(201). + setLocationHeader("/todos/" + todo.getPrimaryKeyValue()); + + fixture.hook.run( + fixture.request("todos", POST), + fixture.response(created), + fixture.thingifier.apiConfig()); + + Assertions.assertTrue(fixture.challenger. + statusOfChallenge(CHALLENGE.POST_MAX_OUT_TITLE_DESCRIPTION_LENGTH)); + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("repositoryProviders") + public void deleteAllTodosChallengeCompletesWhenRepositoryIsEmpty( + final String repositoryName, + final Supplier providerFactory) { + + try (HookFixture fixture = new HookFixture(providerFactory.get())) { + EntityInstance todo = fixture.addTodo("delete me", "false"); + fixture.repository.deleteEntityInstance(todo); + + fixture.hook.run( + fixture.request("todos/" + todo.getPrimaryKeyValue(), DELETE), + fixture.response(200), + fixture.thingifier.apiConfig()); + + Assertions.assertTrue(fixture.challenger.statusOfChallenge(CHALLENGE.DELETE_ALL_TODOS)); + } + } + + @ParameterizedTest(name = "{0}") + @MethodSource("repositoryProviders") + public void deleteAllTodosChallengeDoesNotCompleteWhenRepositoryStillHasTodos( + final String repositoryName, + final Supplier providerFactory) { + + try (HookFixture fixture = new HookFixture(providerFactory.get())) { + EntityInstance deletedTodo = fixture.addTodo("delete me", "false"); + fixture.addTodo("keep me", "false"); + fixture.repository.deleteEntityInstance(deletedTodo); + + fixture.hook.run( + fixture.request("todos/" + deletedTodo.getPrimaryKeyValue(), DELETE), + fixture.response(200), + fixture.thingifier.apiConfig()); + + Assertions.assertFalse(fixture.challenger.statusOfChallenge(CHALLENGE.DELETE_ALL_TODOS)); + } + } + + private static Stream repositoryProviders() { + return Stream.of( + Arguments.of( + "in-memory", + (Supplier) InMemoryThingRepositoryProvider::new), + Arguments.of( + "sqlite-memory", + (Supplier) SqliteThingRepositoryProvider::inMemory) + ); + } + + private static class HookFixture implements AutoCloseable { + private final Thingifier thingifier; + private final Challengers challengers; + private final ChallengerAuthData challenger; + private final ChallengerApiResponseHook hook; + private final EntityDefinition todo; + private final ThingRepository repository; + + HookFixture(final ThingRepositoryProvider provider) { + thingifier = new Thingifier(new EntityRelModel(provider)); + todo = thingifier.defineThing("todo", "todos"); + todo.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); + todo.addFields( + Field.is("title", FieldType.STRING), + Field.is("doneStatus", FieldType.BOOLEAN).withDefaultValue("false"), + Field.is("description", FieldType.STRING)); + + challengers = new Challengers( + thingifier.getERmodel(), + Arrays.asList(CHALLENGE.values())); + challengers.setMultiPlayerMode(); + challenger = challengers.createNewChallenger(); + thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed(challenger.getXChallenger()); + repository = thingifier.getRepository(challenger.getXChallenger()); + hook = new ChallengerApiResponseHook(challengers, thingifier); + } + + EntityInstance addTodo(final String title, final String doneStatus) { + return addTodo(title, doneStatus, ""); + } + + EntityInstance addTodo( + final String title, + final String doneStatus, + final String description) { + return repository.addInstance(new EntityInstance(todo). + setValue("title", title). + setValue("doneStatus", doneStatus). + setValue("description", description)); + } + + HttpApiRequest request(final String path, final HttpApiRequest.VERB verb) { + return new HttpApiRequest(path). + setVerb(verb). + addHeader("X-CHALLENGER", challenger.getXChallenger()); + } + + HttpApiResponse response(final int statusCode) { + return response(new ApiResponse(statusCode)); + } + + HttpApiResponse response(final ApiResponse apiResponse) { + return new HttpApiResponse( + new HttpHeadersBlock(), + apiResponse, + new JsonThing(thingifier.apiConfig().jsonOutput()), + thingifier.apiConfig()); + } + + @Override + public void close() { + thingifier.close(); + } + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java index 1e469f75..8c242929 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java @@ -88,6 +88,22 @@ public ERInstanceData getInstanceData(String databaseKey) { return repository.getInstanceData(); } + public String exportInstanceDataAsJson(final String databaseKey) { + ThingRepository repository = repositories.getRepository(databaseKey); + if (repository == null) { + return "{}"; + } + return repository.exportDataAsJson(schema); + } + + public String reportAsMarkdown(final String databaseKey) { + ThingRepository repository = repositories.getRepository(databaseKey); + if (repository == null) { + return ""; + } + return repository.reportAsMarkdown(schema); + } + public Set getDatabaseNames(){ return repositories.getRepositoryNames(); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/RepositoryBackedSimpleQuery.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQuery.java similarity index 94% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/RepositoryBackedSimpleQuery.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQuery.java index 2f1a35df..ef8544f5 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/RepositoryBackedSimpleQuery.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQuery.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; -public class RepositoryBackedSimpleQuery implements QueryResult { +public class RepositoryUrlQuery implements UrlQueryResult { private final ERSchema schema; private final ThingRepository repository; @@ -22,7 +22,7 @@ public class RepositoryBackedSimpleQuery implements QueryResult { private EntityInstance currentInstance; private List foundItems = new ArrayList<>(); - public RepositoryBackedSimpleQuery( + public RepositoryUrlQuery( final ERSchema schema, final ThingRepository repository, final String query) { @@ -35,6 +35,10 @@ public RepositoryBackedSimpleQuery( } } + public RepositoryUrlQuery performQuery() { + return performQuery(new QueryFilterParams()); + } + public static boolean canHandle(final ERSchema schema, final String query) { String[] terms = termsFrom(query); if (terms.length == 0 || terms.length > 3) { @@ -63,7 +67,7 @@ public static boolean canHandle(final ERSchema schema, final String query) { return entity.related().hasRelationship(terms[2]); } - public RepositoryBackedSimpleQuery performQuery(final QueryFilterParams queryParams) { + public RepositoryUrlQuery performQuery(final QueryFilterParams queryParams) { String[] terms = termsFrom(query); EntityDefinition entity = entityForTerm(schema, terms[0]); resultContainsDefinition = entity; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java index c43b34d2..7b8ae465 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java @@ -32,7 +32,7 @@ * Keep this class only for compatibility/fallback paths until replacement is complete. */ @Deprecated -public final class SimpleQuery implements QueryResult { +public final class SimpleQuery implements UrlQueryResult { private final ERInstanceData database; private final ERSchema schema; // all the definitions diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryResult.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/UrlQueryResult.java similarity index 93% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryResult.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/UrlQueryResult.java index 25fbbb81..1d3ff21d 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/QueryResult.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/UrlQueryResult.java @@ -5,7 +5,7 @@ import java.util.List; -public interface QueryResult { +public interface UrlQueryResult { boolean wasQueryIntendedToMatchAnInstance(); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/ERModelReport.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/ERModelReport.java index 299c59b5..bbd1da97 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/ERModelReport.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/ERModelReport.java @@ -5,27 +5,35 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class ERModelReport { - private final EntityRelModel erModel; + private final ERSchema schema; + private final ThingRepository repository; public ERModelReport(final EntityRelModel erModel) { - this.erModel = erModel; + this(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME)); + } + + public ERModelReport( + final ERSchema schema, + final ThingRepository repository) { + this.schema = schema; + this.repository = repository; } public String asMarkdown() { StringBuilder output = new StringBuilder(); - output.append(schemaAsMarkdown(erModel.getSchema())); + output.append(schemaAsMarkdown(schema)); output.append("\n# Instances\n"); - for (EntityInstanceCollection instances : erModel.getInstanceData().getAllInstanceCollections()) { + for (EntityDefinition entity : schema.getEntityDefinitions()) { - output.append("## Of " + instances.definition().getName() + "\n"); + output.append("## Of " + entity.getName() + "\n"); - for (EntityInstance anInstance : instances.getInstances()) { + for (EntityInstance anInstance : repository.listInstances(entity)) { output.append(anInstance); } } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/RepositoryJsonExporter.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/RepositoryJsonExporter.java new file mode 100644 index 00000000..256cc969 --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/reporting/RepositoryJsonExporter.java @@ -0,0 +1,97 @@ +package uk.co.compendiumdev.thingifier.core.reporting; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +public class RepositoryJsonExporter { + + private final ERSchema schema; + private final ThingRepository repository; + + public RepositoryJsonExporter( + final ERSchema schema, + final ThingRepository repository) { + this.schema = schema; + this.repository = repository; + } + + public String asJson() { + StringBuilder json = new StringBuilder(); + json.append("{"); + + String entitySeparator = ""; + for (EntityDefinition entity : schema.getEntityDefinitions()) { + json.append(entitySeparator). + append(quoted(entity.getPlural())). + append(" : ["); + + String instanceSeparator = ""; + for (EntityInstance instance : repository.listInstances(entity)) { + json.append(instanceSeparator); + appendInstanceJson(json, entity, instance); + instanceSeparator = ", "; + } + + json.append("]"); + entitySeparator = ", "; + } + + json.append("}"); + return json.toString(); + } + + public static void appendInstanceJson( + final StringBuilder json, + final EntityDefinition entity, + final EntityInstance instance) { + json.append("{"); + + String fieldSeparator = ""; + for (String fieldName : entity.getFieldNames()) { + Field field = entity.getField(fieldName); + String fieldJsonValue = fieldJsonValue(field, instance); + if (fieldJsonValue == null) { + continue; + } + + json.append(fieldSeparator). + append(quoted(field.getName())). + append(": "). + append(fieldJsonValue); + fieldSeparator = ", "; + } + + json.append("}"); + } + + public static String fieldJsonValue( + final Field field, + final EntityInstance instance) { + if (instance.hasInstantiatedFieldNamed(field.getName())) { + return instance.getFieldValue(field.getName()).asJsonValue(); + } + + if (field.isMandatory()) { + return field.getDefaultValue().asJsonValue(); + } + + return null; + } + + public static String fieldJsonValue(final Field field, final String value) { + if (value == null) { + if (field.isMandatory()) { + return field.getDefaultValue().asJsonValue(); + } + return null; + } + return field.valueFor(value).asJsonValue(); + } + + public static String quoted(final String value) { + return "\"" + value.replaceAll("\"", "\\\\\"") + "\""; + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java index d5c05165..4b83b379 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java @@ -122,6 +122,15 @@ public List listInstances( return new EntityInstanceListSorter(params).sort(instances); } + @Override + public int countInstances(final EntityDefinition entity) { + EntityInstanceCollection collection = getInstanceCollectionForEntityNamed(entity.getName()); + if (collection == null) { + return 0; + } + return collection.countInstances(); + } + @Override public EntityInstance findInstanceByQueryIdentifier( final EntityDefinition entity, final String identifier) { diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java index 624feceb..8c7351d2 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java @@ -18,6 +18,7 @@ import uk.co.compendiumdev.thingifier.core.query.FilterBy; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.SortByFieldName; +import uk.co.compendiumdev.thingifier.core.reporting.RepositoryJsonExporter; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.nio.file.Path; @@ -88,6 +89,29 @@ public boolean hasLoadedCompatibilitySnapshot() { return legacySnapshotLoaded; } + @Override + public String exportDataAsJson(final ERSchema schema) { + ensureSchemaReady(); + + StringBuilder json = new StringBuilder(); + json.append("{"); + + String entitySeparator = ""; + for (EntityDefinition entity : schema.getEntityDefinitions()) { + json.append(entitySeparator). + append(RepositoryJsonExporter.quoted(entity.getPlural())). + append(" : ["); + + appendEntityRowsAsJson(json, entity); + + json.append("]"); + entitySeparator = ", "; + } + + json.append("}"); + return json.toString(); + } + @Override public void refreshSchema(final ERSchema schema) { this.schema = schema; @@ -167,6 +191,22 @@ public List listInstances( return queryEntityRows(entity, sqlQuery.sql, sqlQuery.parameters); } + @Override + public int countInstances(final EntityDefinition entity) { + ensureSchemaReady(); + String sql = "SELECT COUNT(*) FROM " + table(entity); + try (PreparedStatement statement = connection.prepareStatement(sql); + ResultSet resultSet = statement.executeQuery()) { + if (resultSet.next()) { + return resultSet.getInt(1); + } + return 0; + } catch (SQLException e) { + throw new IllegalStateException( + "Could not count SQLite entity rows for " + entity.getName(), e); + } + } + @Override public EntityInstance findInstanceByQueryIdentifier( final EntityDefinition entity, final String identifierValue) { @@ -749,6 +789,50 @@ private List queryEntityRows( return instances; } + private void appendEntityRowsAsJson( + final StringBuilder json, + final EntityDefinition entity) { + String sql = "SELECT * FROM " + table(entity); + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + String instanceSeparator = ""; + while (resultSet.next()) { + json.append(instanceSeparator); + appendEntityRowAsJson(json, entity, resultSet); + instanceSeparator = ", "; + } + } catch (SQLException e) { + throw new IllegalStateException( + "Could not export SQLite entity rows for " + entity.getName(), e); + } + } + + private void appendEntityRowAsJson( + final StringBuilder json, + final EntityDefinition entity, + final ResultSet resultSet) throws SQLException { + json.append("{"); + + String fieldSeparator = ""; + for (String fieldName : entity.getFieldNames()) { + Field field = entity.getField(fieldName); + String fieldJsonValue = + RepositoryJsonExporter.fieldJsonValue( + field, resultSet.getString(fieldName)); + if (fieldJsonValue == null) { + continue; + } + + json.append(fieldSeparator). + append(RepositoryJsonExporter.quoted(field.getName())). + append(": "). + append(fieldJsonValue); + fieldSeparator = ", "; + } + + json.append("}"); + } + private EntityInstance instanceFromRow( final EntityDefinition entity, final ResultSet resultSet) throws SQLException { String internalId = resultSet.getString(INTERNAL_ID_COLUMN); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java index 049e30bb..16d49faf 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java @@ -8,6 +8,8 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; +import uk.co.compendiumdev.thingifier.core.reporting.ERModelReport; +import uk.co.compendiumdev.thingifier.core.reporting.RepositoryJsonExporter; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.Collection; @@ -34,6 +36,14 @@ public interface ThingRepository extends AutoCloseable { @Deprecated EntityInstanceCollection getInstanceCollectionForEntityNamed(String entityName); + default String exportDataAsJson(final ERSchema schema) { + return new RepositoryJsonExporter(schema, this).asJson(); + } + + default String reportAsMarkdown(final ERSchema schema) { + return new ERModelReport(schema, this).asMarkdown(); + } + EntityInstance findEntityInstanceByGUID(String thingGUID); EntityInstance findInstanceByPrimaryKey(EntityDefinition entity, String primaryKeyValue); @@ -44,6 +54,8 @@ public interface ThingRepository extends AutoCloseable { List listInstances(EntityDefinition entity, QueryFilterParams queryParams); + int countInstances(EntityDefinition entity); + EntityInstance findInstanceByQueryIdentifier(EntityDefinition entity, String identifier); default List listRelatedInstances( diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java index 406de338..45c12ecd 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java @@ -13,8 +13,7 @@ import java.util.List; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class QueryFiltersBooleanTest { @@ -22,7 +21,7 @@ public class QueryFiltersBooleanTest { // e.g https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/ // https://softwareengineering.stackexchange.com/questions/233164/how-do-searches-fit-into-a-restful-interface - // TODO: risk that Spark does not pass in args in a way that flow through to simple query + // TODO: risk that Spark does not pass in args in a way that flow through to repository URL query // so test this at an HTTP level as well EntityRelModel erModel; @@ -49,7 +48,7 @@ public void canFilterBooleanMatchesTrue() { QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "true"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -63,7 +62,7 @@ public void canFilterBooleanMatchesFalse() { QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "false"); - SimpleQuery queryResults = queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -76,7 +75,7 @@ public void canFilterBooleanMatchesNotFalse() { QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "!false"); - SimpleQuery queryResults = queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -89,7 +88,7 @@ public void canFilterBooleanMatchesNotTrue(){ QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "!true"); - SimpleQuery queryResults = queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -102,7 +101,7 @@ public void canSortBooleanMatchesAsc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "+truefalse"); - SimpleQuery queryResults = queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -120,7 +119,7 @@ public void canSortBooleanMatchesDesc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-truefalse"); - SimpleQuery queryResults = queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java index 898ab37d..49755cdb 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java @@ -13,8 +13,7 @@ import java.util.List; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class QueryFiltersFloatTest { @@ -43,7 +42,7 @@ public void canFilterFloatEquals() { QueryFilterParams params = new QueryFilterParams(); params.put("float", "1.1"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(1, instances.size(), "expected 1 value"); @@ -57,7 +56,7 @@ public void canFilterFloatNotEquals() { params.put("float", "!1.1"); params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(3, instances.size(), "expected 3 value"); @@ -76,7 +75,7 @@ public void canFilterFloatCombinationOfConditions() { params.put("float", "!3.3"); // and not equal to 3 params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(2, instances.size(), "expected 2 value"); @@ -90,7 +89,7 @@ public void canFilterFloatGreaterThan() { params.put("float", ">1.1"); params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -106,7 +105,7 @@ public void canFilterFloatLessThan() { params.put("float", "<2.0"); params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -120,7 +119,7 @@ public void canFilterFloatLessThanNotMatching() { params.put("float", "<1.1"); params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -133,7 +132,7 @@ public void canFilterFloatGreaterThanEquals(){ params.put("float", ">=3.3"); params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -149,7 +148,7 @@ public void canFilterFloatLessThanEquals(){ params.put("float", "<=3.3"); params.put("sortby", "+float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -166,7 +165,7 @@ public void canFilterFloatLessThanEqualsSortDesc(){ params.put("float", "<=3.3"); params.put("sortby", "-float"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java index 83a9f860..8a860760 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java @@ -13,8 +13,7 @@ import java.util.List; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class QueryFiltersIdTest { @@ -45,7 +44,7 @@ public void canFilterAndSortIdAsc() { params.put("id", ">=3"); params.put("sortBy", "+id"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -62,7 +61,7 @@ public void canFilterAndSortIdDesc() { params.put("id", "<3"); params.put("sortBy", "-id"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java index 579a989b..f5396930 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java @@ -13,8 +13,7 @@ import java.util.List; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class QueryFiltersIntegerTest { @@ -47,7 +46,7 @@ public void canFilterIntegerEquals() { QueryFilterParams params = new QueryFilterParams(); params.put("int", "1"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(1, instances.size(), "expected 1 value"); @@ -61,7 +60,7 @@ public void canFilterIntegerNotEquals() { params.put("int", "!1"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(3, instances.size(), "expected 3 value"); @@ -79,7 +78,7 @@ public void canFilterIntegerCombinationOfConditions() { params.put("int", "!3"); // and not equal to 3 params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(2, instances.size(), "expected 2 value"); @@ -93,7 +92,7 @@ public void canFilterIntegerGreaterThan() { params.put("int", ">1"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -109,7 +108,7 @@ public void canFilterIntegerLessThan() { params.put("int", "<2"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -123,7 +122,7 @@ public void canFilterIntegerLessThanNotMatching() { params.put("int", "<1"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things").performQuery(params); + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -136,7 +135,7 @@ public void canFilterIntegerGreaterThanEquals(){ params.put("int", ">=3"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -152,7 +151,7 @@ public void canFilterIntegerLessThanEquals(){ params.put("int", "<=3"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -169,7 +168,7 @@ public void canFilterIntegerLessThanSortedDesc(){ params.put("int", "<3"); params.put("sortby", "-int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -185,7 +184,7 @@ public void canIntegerSortedDesc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortby", "-int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -202,7 +201,7 @@ public void canIntegerSortedAsc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -221,7 +220,7 @@ public void canRegexFilterInteger(){ params.put("int", "~=[1,2]"); params.put("sortby", "+int"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java index 5ee4671b..88a15c2a 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java @@ -13,8 +13,7 @@ import java.util.List; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class QueryFiltersStringTest { @@ -42,7 +41,7 @@ public void canSortStringAsc() { QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "+string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -60,7 +59,7 @@ public void canSortStringDesc() { QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -79,7 +78,7 @@ public void canFilterStringEquals() { QueryFilterParams params = new QueryFilterParams(); params.put("string", "=one"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -95,7 +94,7 @@ public void canFilterAndSortStringUsingRegexAsc() { params.put("string", "~=.*o.*"); params.put("sortBy", "+string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -114,7 +113,7 @@ public void canFilterAndSortStringUsingRegexDesc() { params.put("string", "~=.*o.*"); params.put("sortBy", "-string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -134,7 +133,7 @@ public void canFilterAndSortStringUsingWildcardAsc() { params.put("string", "*=*e"); params.put("sortBy", "+string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -152,7 +151,7 @@ public void canFilterAndSortStringUsingWildcardAscForT() { params.put("string", "*=t*"); params.put("sortBy", "+string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -170,7 +169,7 @@ public void canFilterAndSortStringUsingWildcardAscForTBit() { params.put("string", "*=t?*"); params.put("sortBy", "+string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -188,7 +187,7 @@ public void canFilterAndSortStringUsingWildcardAscForTBitO() { params.put("string", "*=t?o"); params.put("sortBy", "+string"); - SimpleQuery queryResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java new file mode 100644 index 00000000..7cbba89d --- /dev/null +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java @@ -0,0 +1,16 @@ +package uk.co.compendiumdev.thingifier.core.query; + +import uk.co.compendiumdev.thingifier.core.EntityRelModel; + +final class RepositoryUrlQueryTestSupport { + + private RepositoryUrlQueryTestSupport() { + } + + static RepositoryUrlQuery query(final EntityRelModel model, final String query) { + return new RepositoryUrlQuery( + model.getSchema(), + model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + query); + } +} diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java index c59fa16d..7ce59a45 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java @@ -12,8 +12,7 @@ import java.util.List; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class SortingViaQueryFiltersTest { @@ -49,7 +48,7 @@ public void canSortIntViaAQuery(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-int"); - SimpleQuery ascSortedResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery ascSortedResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); Assertions.assertTrue(ascSortedResults.isResultACollection(), "result should be a collection"); @@ -63,7 +62,7 @@ public void canSortIntViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "+int"); - SimpleQuery descSortedResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery descSortedResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); final List descInstances = descSortedResults.getListEntityInstances(); @@ -76,7 +75,7 @@ public void canSortIntViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "int"); - SimpleQuery defaultSortedResults = new SimpleQuery(erModel.getSchema(), erModel.getInstanceData(), "things"). + RepositoryUrlQuery defaultSortedResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). performQuery(params); final List defaultSortedInstances = defaultSortedResults.getListEntityInstances(); @@ -104,7 +103,7 @@ public void canSortViaAQuery(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-truefalse"); - SimpleQuery ascSortedResults = new SimpleQuery(aThingifier.getSchema(), aThingifier.getInstanceData(), "things"). + RepositoryUrlQuery ascSortedResults = RepositoryUrlQueryTestSupport.query(aThingifier, "things"). performQuery(params); Assertions.assertTrue(ascSortedResults.isResultACollection(), "result should be a collection"); @@ -118,7 +117,7 @@ public void canSortViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "+truefalse"); - SimpleQuery descSortedResults = new SimpleQuery(aThingifier.getSchema(), aThingifier.getInstanceData(), "things"). + RepositoryUrlQuery descSortedResults = RepositoryUrlQueryTestSupport.query(aThingifier, "things"). performQuery(params); final List descInstances = descSortedResults.getListEntityInstances(); @@ -130,7 +129,7 @@ public void canSortViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "truefalse"); - SimpleQuery defaultSortedResults = new SimpleQuery(aThingifier.getSchema(), aThingifier.getInstanceData(), "things"). + RepositoryUrlQuery defaultSortedResults = RepositoryUrlQueryTestSupport.query(aThingifier, "things"). performQuery(params); final List defaultSortedInstances = defaultSortedResults.getListEntityInstances(); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java index 7f9ef35e..1b509e13 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java @@ -16,8 +16,7 @@ import static uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType.STRING; /** - * Legacy compatibility coverage for {@link SimpleQuery}. Runtime API paths - * should use repository-backed query resolution. + * Repository-backed URL query coverage for API-style entity reads. */ public class TodoManagerQueryEngineTest { @@ -84,7 +83,7 @@ public void createDefinitions(){ public void canGetListOfEntityInstancesViaName(){ // to do - final SimpleQuery query = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), "todo"); + final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todo"); List queryResults = query.performQuery().getListEntityInstances(); @@ -99,7 +98,7 @@ public void canGetListOfEntityInstancesViaName(){ @Test public void canGetListOfEntityInstancesViaPluralName(){ // todos - final SimpleQuery query = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), "todos"); + final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todos"); List queryResults = query.performQuery().getListEntityInstances(); @@ -117,7 +116,7 @@ public void canGetSpecificEntityInstanceUsingGUID(){ // to do/_GUID_ - final SimpleQuery query = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), "todo/" + paperwork.getPrimaryKeyValue()); + final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todo/" + paperwork.getPrimaryKeyValue()); queryResults = query.performQuery().getListEntityInstances(); @@ -136,11 +135,12 @@ public void canGetSpecificEntityInstanceUsingGUIDOnPlural(){ // to do/_GUID_ - final SimpleQuery query = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), "todos/" + paperwork.getPrimaryKeyValue()); + final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todos/" + paperwork.getPrimaryKeyValue()); queryResults = query.performQuery().getListEntityInstances(); - Assertions.assertTrue(query.isResultACollection()); // plural should always report itself as a collection even on instance + Assertions.assertTrue(query.wasQueryIntendedToMatchAnInstance()); + Assertions.assertFalse(query.isResultACollection()); Assertions.assertEquals(1, queryResults.size()); Assertions.assertTrue(queryResults.contains(paperwork)); @@ -156,13 +156,14 @@ public void cannotGetGuidThatDoesNotExist(){ // to do/_GUID_ - final SimpleQuery query = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), "todo/" + paperwork.getPrimaryKeyValue() + "bob"); + final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todo/" + paperwork.getPrimaryKeyValue() + "bob"); queryResults = query.performQuery().getListEntityInstances(); - // even though it doesn not match anything I should know what type of thing this empty collection is - Assertions.assertTrue(query.isResultACollection()); - Assertions.assertEquals(todoManager.getInstanceData().getInstanceCollectionForEntityNamed("todo").definition(), query.resultContainsDefn()); + // even though it does not match anything I should know what type of thing this empty collection is + Assertions.assertTrue(query.wasQueryIntendedToMatchAnInstance()); + Assertions.assertTrue(query.lastMatchWasNothing()); + Assertions.assertEquals(todoManager.getSchema().getEntityDefinitionNamed("todo"), query.resultContainsDefn()); Assertions.assertEquals(0, queryResults.size()); } @@ -174,13 +175,14 @@ public void cannotGetGuidThatDoesNotExistWithPlural(){ // to do/_GUID_ - final SimpleQuery query = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), "todos/" + paperwork.getPrimaryKeyValue() + "bob"); + final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todos/" + paperwork.getPrimaryKeyValue() + "bob"); queryResults = query.performQuery().getListEntityInstances(); - // even though it doesn not match anything I should know what type of thing this empty collection is - Assertions.assertTrue(query.isResultACollection()); - Assertions.assertEquals(todoManager.getInstanceData().getInstanceCollectionForEntityNamed("todo").definition(), query.resultContainsDefn()); + // even though it does not match anything I should know what type of thing this empty collection is + Assertions.assertTrue(query.wasQueryIntendedToMatchAnInstance()); + Assertions.assertTrue(query.lastMatchWasNothing()); + Assertions.assertEquals(todoManager.getSchema().getEntityDefinitionNamed("todo"), query.resultContainsDefn()); Assertions.assertEquals(0, queryResults.size()); @@ -206,7 +208,7 @@ public void connectionTesting() { // match on relationships // project/_GUID_/tasks - queryResults = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), String.format("project/%s/tasks", officeWork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); + queryResults = RepositoryUrlQueryTestSupport.query(todoManager, String.format("project/%s/tasks", officeWork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); Assertions.assertEquals(2, queryResults.size()); Assertions.assertTrue(queryResults.contains(paperwork)); @@ -215,33 +217,27 @@ public void connectionTesting() { // should be able to get projects for a task - queryResults = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), String.format("todo/%s/task-of", paperwork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); + queryResults = RepositoryUrlQueryTestSupport.query(todoManager, String.format("todo/%s/task-of", paperwork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); Assertions.assertEquals(1, queryResults.size()); Assertions.assertTrue(queryResults.contains(officeWork)); - // match on entity types - // project/_GUID_/to do - - queryResults = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), String.format("project/%s/todo", officeWork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); - - Assertions.assertEquals(2, queryResults.size()); - Assertions.assertTrue(queryResults.contains(paperwork)); - Assertions.assertTrue(queryResults.contains(filework)); - - // project/_GUID_/to do/category - - queryResults = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), String.format("project/%s/todo/category", officeWork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); + // Repository URL query handles explicit relationship names, not legacy + // entity-type traversal or multi-hop guesses. + Assertions.assertFalse(RepositoryUrlQuery.canHandle( + todoManager.getSchema(), + String.format("project/%s/todo", officeWork.getPrimaryKeyValue()))); - Assertions.assertEquals(1, queryResults.size()); - Assertions.assertTrue(queryResults.contains(officeCategory)); + Assertions.assertFalse(RepositoryUrlQuery.canHandle( + todoManager.getSchema(), + String.format("project/%s/todo/category", officeWork.getPrimaryKeyValue()))); // invalid query should match nothing there is no entity called task // project/_GUID_/task - queryResults = new SimpleQuery(todoManager.getSchema(), todoManager.getInstanceData(), String.format("project/%s/task", officeWork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); - - Assertions.assertEquals(0, queryResults.size()); + Assertions.assertFalse(RepositoryUrlQuery.canHandle( + todoManager.getSchema(), + String.format("project/%s/task", officeWork.getPrimaryKeyValue()))); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java index 1ee5966d..cd3ebc25 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java @@ -12,7 +12,7 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; -import uk.co.compendiumdev.thingifier.core.query.RepositoryBackedSimpleQuery; +import uk.co.compendiumdev.thingifier.core.query.RepositoryUrlQuery; import java.nio.file.Path; import java.util.List; @@ -159,6 +159,106 @@ public void sqliteFileBackedRepositoryOnlyHydratesCompatibilitySnapshotWhenReque } } + @Test + public void sqliteCountInstancesDoesNotHydrateCompatibilitySnapshot() { + ERSchema schema = todoSchema(); + EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); + Path databasePath = tempDir.resolve("count-lazy.sqlite"); + + try (ThingRepository repository = + SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { + repository.initializeFrom(schema); + + repository.addInstance(new EntityInstance(projectDefinition).setValue("title", "First")); + repository.addInstance(new EntityInstance(projectDefinition).setValue("title", "Second")); + } + + try (ThingRepository reopened = + SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { + reopened.initializeFrom(schema); + + EntityInstanceCollection cachedProjects = + reopened.getInstanceCollectionForEntityNamed("project"); + + Assertions.assertEquals(0, cachedProjects.countInstances()); + Assertions.assertEquals(2, reopened.countInstances(projectDefinition)); + Assertions.assertEquals(0, cachedProjects.countInstances()); + Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void repositoriesExportDataAsJsonThroughTheRepositoryContract() { + ERSchema schema = todoSchema(); + + ThingRepository memoryRepository = + new InMemoryThingRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + memoryRepository.initializeFrom(schema); + addProjectAndTask(memoryRepository, schema); + + try (ThingRepository sqliteRepository = + SqliteThingRepository.inMemory(EntityRelModel.DEFAULT_DATABASE_NAME)) { + sqliteRepository.initializeFrom(schema); + addProjectAndTask(sqliteRepository, schema); + + assertExportedJsonContainsProjectAndTask(memoryRepository.exportDataAsJson(schema)); + assertExportedJsonContainsProjectAndTask(sqliteRepository.exportDataAsJson(schema)); + Assertions.assertFalse(sqliteRepository.hasLoadedCompatibilitySnapshot()); + } + } + + @Test + public void sqliteJsonExportDoesNotHydrateCompatibilitySnapshot() { + ERSchema schema = todoSchema(); + Path databasePath = tempDir.resolve("export-lazy.sqlite"); + + try (ThingRepository repository = + SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { + repository.initializeFrom(schema); + addProjectAndTask(repository, schema); + } + + try (ThingRepository reopened = + SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { + reopened.initializeFrom(schema); + + EntityInstanceCollection cachedProjects = + reopened.getInstanceCollectionForEntityNamed("project"); + EntityInstanceCollection cachedTasks = + reopened.getInstanceCollectionForEntityNamed("task"); + + Assertions.assertEquals(0, cachedProjects.countInstances()); + Assertions.assertEquals(0, cachedTasks.countInstances()); + + String json = reopened.exportDataAsJson(schema); + + assertExportedJsonContainsProjectAndTask(json); + Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); + Assertions.assertEquals(0, cachedProjects.countInstances()); + Assertions.assertEquals(0, cachedTasks.countInstances()); + } + } + + @Test + public void repositoriesReportMarkdownThroughTheRepositoryContract() { + ERSchema schema = todoSchema(); + + try (ThingRepository repository = + SqliteThingRepository.inMemory(EntityRelModel.DEFAULT_DATABASE_NAME)) { + repository.initializeFrom(schema); + addProjectAndTask(repository, schema); + + String markdown = repository.reportAsMarkdown(schema); + + Assertions.assertTrue(markdown.contains("# Entity Definitions:")); + Assertions.assertTrue(markdown.contains("# Instances")); + Assertions.assertTrue(markdown.contains("## Of project")); + Assertions.assertTrue(markdown.contains("Repository project")); + Assertions.assertTrue(markdown.contains("Wire repository")); + Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); + } + } + @Test public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { ERSchema schema = todoSchema(); @@ -211,8 +311,8 @@ public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { regexFilteredTasks.get(0).getFieldValue("title").asString()); Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); - RepositoryBackedSimpleQuery query = - new RepositoryBackedSimpleQuery(schema, reopened, "project/1/tasks"). + RepositoryUrlQuery query = + new RepositoryUrlQuery(schema, reopened, "project/1/tasks"). performQuery(new QueryFilterParams()); Assertions.assertTrue(query.isResultACollection()); @@ -380,6 +480,8 @@ private void exerciseRepositoryContract(final ThingRepository repository) { Assertions.assertEquals(project, repository.findInstanceByFieldNameAndValue( projectDefinition, "title", "Repository project")); Assertions.assertEquals(1, repository.listInstances(projectDefinition).size()); + Assertions.assertEquals(1, repository.countInstances(projectDefinition)); + Assertions.assertEquals(0, repository.countInstances(taskDefinition)); EntityInstance duplicate = new EntityInstance(projectDefinition); duplicate.setValue("title", "Repository project"); @@ -388,6 +490,7 @@ private void exerciseRepositoryContract(final ThingRepository repository) { EntityInstance secondProject = new EntityInstance(projectDefinition); secondProject.setValue("title", "Another project"); repository.addInstance(secondProject); + Assertions.assertEquals(2, repository.countInstances(projectDefinition)); QueryFilterParams filteredParams = new QueryFilterParams(); filteredParams.put("id", ">=2"); @@ -458,11 +561,14 @@ private void exerciseRepositoryContract(final ThingRepository repository) { repository.deleteEntityInstance(secondTask); Assertions.assertEquals(0, repository.listInstances(taskDefinition).size()); + Assertions.assertEquals(0, repository.countInstances(taskDefinition)); repository.clearAllData(); Assertions.assertEquals(0, repository.listInstances(projectDefinition).size()); Assertions.assertEquals(0, repository.listInstances(taskDefinition).size()); + Assertions.assertEquals(0, repository.countInstances(projectDefinition)); + Assertions.assertEquals(0, repository.countInstances(taskDefinition)); } private void exerciseProviderIsolation(final ThingRepositoryProvider provider) { @@ -483,6 +589,30 @@ private void exerciseProviderIsolation(final ThingRepositoryProvider provider) { provider.getRepository("session-one").listInstances(projectDefinition).size()); } + private void addProjectAndTask( + final ThingRepository repository, + final ERSchema schema) { + EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); + EntityDefinition taskDefinition = schema.getEntityDefinitionNamed("task"); + + EntityInstance project = new EntityInstance(projectDefinition). + setValue("title", "Repository project"); + EntityInstance task = new EntityInstance(taskDefinition). + setValue("title", "Wire repository"); + + repository.addInstance(project); + repository.addInstance(task); + repository.connectRelationship(project, "tasks", task); + } + + private void assertExportedJsonContainsProjectAndTask(final String json) { + Assertions.assertTrue(json.contains("\"projects\"")); + Assertions.assertTrue(json.contains("\"tasks\"")); + Assertions.assertTrue(json.contains("\"id\": 1")); + Assertions.assertTrue(json.contains("\"title\": \"Repository project\"")); + Assertions.assertTrue(json.contains("\"title\": \"Wire repository\"")); + } + private ERSchema todoSchema() { ERSchema schema = new ERSchema(); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java index 86e1b7da..c69fb985 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java @@ -253,6 +253,14 @@ public ThingRepository getRepository(final String database) { return erm.getRepository(database); } + public String exportDataAsJson(final String database) { + return erm.exportInstanceDataAsJson(database); + } + + public String reportAsMarkdown(final String database) { + return erm.reportAsMarkdown(database); + } + @Override public void close() { erm.close(); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java index 01ccea64..b869dd18 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiGetHandler.java @@ -4,9 +4,9 @@ import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.query.QueryResult; +import uk.co.compendiumdev.thingifier.core.query.UrlQueryResult; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; -import uk.co.compendiumdev.thingifier.core.query.RepositoryBackedSimpleQuery; +import uk.co.compendiumdev.thingifier.core.query.RepositoryUrlQuery; import java.util.List; @@ -29,12 +29,12 @@ public ApiResponse handle(final String url, final QueryFilterParams queryParams, String instanceDatabaseName = SessionHeaderParser.getDatabaseNameFromHeaderValue(requestHeaders); - QueryResult queryResults; + UrlQueryResult queryResults; boolean allowFiltering = thingifier.apiConfig().forParams().willAllowFilteringThroughUrlParams(); QueryFilterParams effectiveQueryParams = allowFiltering ? queryParams : new QueryFilterParams(); - if (RepositoryBackedSimpleQuery.canHandle(thingifier.getERmodel().getSchema(), url)) { - queryResults = new RepositoryBackedSimpleQuery( + if (RepositoryUrlQuery.canHandle(thingifier.getERmodel().getSchema(), url)) { + queryResults = new RepositoryUrlQuery( thingifier.getERmodel().getSchema(), thingifier.getRepository(instanceDatabaseName), url). diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/reporting/ThingReporter.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/reporting/ThingReporter.java index 18cb0273..ead8a05b 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/reporting/ThingReporter.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/reporting/ThingReporter.java @@ -1,7 +1,7 @@ package uk.co.compendiumdev.thingifier.reporting; import uk.co.compendiumdev.thingifier.Thingifier; -import uk.co.compendiumdev.thingifier.core.reporting.ERModelReport; +import uk.co.compendiumdev.thingifier.core.EntityRelModel; public class ThingReporter { @@ -14,7 +14,6 @@ public ThingReporter(Thingifier thingifier) { public String basicReport() { - return new ERModelReport(thingifier.getERmodel()). - asMarkdown(); + return thingifier.reportAsMarkdown(EntityRelModel.DEFAULT_DATABASE_NAME); } } From bd2c2ef3c68ecc476d3694875dbc71e2d04be764 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 7 Jul 2026 21:14:04 +0100 Subject: [PATCH 04/22] Move population import paths onto repositories --- .../apimodel/TodoAPIDataPopulator.java | 21 +++++++++------ .../challenge/challengers/Challengers.java | 4 ++- .../persistence/ChallengerFileStorage.java | 8 ++---- .../DatabaseContentPersistenceMechanism.java | 4 +-- .../persistence/PersistenceLayer.java | 8 +++--- .../simpleapi/SimpleAPITestDataPopulator.java | 27 +++++++++---------- .../thingifier/core/EntityRelModel.java | 4 +-- .../LegacyDataPopulatorAdapter.java | 19 +++++++++++++ .../compendiumdev/thingifier/Thingifier.java | 7 +++-- .../api/ermodelconversion/JsonPopulator.java | 23 +++++++++------- 10 files changed, 74 insertions(+), 51 deletions(-) create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java index 40ffbb3e..33b48c4b 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java @@ -1,17 +1,22 @@ package uk.co.compendiumdev.challenge.apimodel; -import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; +import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; -public class TodoAPIDataPopulator implements DataPopulator { +public class TodoAPIDataPopulator implements RepositoryDataPopulator { @Override public void populate(final ERSchema schema, final ERInstanceData database) { + populate(schema, new InMemoryThingRepository("__todo-populator", database)); + } + @Override + public void populate(final ERSchema schema, final ThingRepository repository) { String [] todos={ "scan paperwork", "file paperwork", @@ -24,12 +29,12 @@ public void populate(final ERSchema schema, final ERInstanceData database) { "tidy meeting room", "install webcam"}; - EntityInstanceCollection todo = database.getInstanceCollectionForEntityNamed("todo"); + EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); for(String todoItem : todos){ - EntityInstance instance = new EntityInstance(todo.definition()); - todo.addInstance(instance); - instance.setValue("title", todoItem); + EntityInstance instance = new EntityInstance(todo). + setValue("title", todoItem); + repository.addInstance(instance); } } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java index 51c8dd01..49d7ac20 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java @@ -155,7 +155,9 @@ public void put(final ChallengerAuthData challenger) { public void persistChallengerState(final ChallengerAuthData challenger){ if (persistenceLayer != null) { String databaseName = challenger.getXChallenger(); - persistenceLayer.saveChallengerStatus(challenger, erModel.getInstanceData(databaseName)); + persistenceLayer.saveChallengerStatus( + challenger, + erModel.exportInstanceDataAsJson(databaseName)); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/ChallengerFileStorage.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/ChallengerFileStorage.java index d563c3ef..54fe06aa 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/ChallengerFileStorage.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/ChallengerFileStorage.java @@ -4,7 +4,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.co.compendiumdev.challenge.ChallengerAuthData; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import java.io.*; import java.nio.file.Files; @@ -71,7 +70,7 @@ public PersistenceResponse loadChallengerStatus(final String guid) { } } - public PersistenceResponse saveDatabaseContent(String guid, ERInstanceData todos) { + public PersistenceResponse saveDatabaseContent(String guid, String databaseContents) { File folder = new File(System.getProperty("User.dir"), "challengersessions"); folder.mkdirs(); @@ -83,10 +82,7 @@ public PersistenceResponse saveDatabaseContent(String guid, ERInstanceData todos } try(FileOutputStream out = new FileOutputStream(file)) { - String dataString = ""; - if(todos!=null){ - dataString=todos.asJson(); - } + String dataString = databaseContents == null ? "" : databaseContents; out.write(dataString.getBytes()); return new PersistenceResponse(). withSuccess(true); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/DatabaseContentPersistenceMechanism.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/DatabaseContentPersistenceMechanism.java index 442c37da..b5f5c445 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/DatabaseContentPersistenceMechanism.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/DatabaseContentPersistenceMechanism.java @@ -1,9 +1,7 @@ package uk.co.compendiumdev.challenge.persistence; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; - public interface DatabaseContentPersistenceMechanism { - PersistenceResponse saveDatabaseContent(String guid, ERInstanceData instanceCollection); + PersistenceResponse saveDatabaseContent(String guid, String databaseContents); PersistenceResponse loadDatabaseContent(String guid); } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/PersistenceLayer.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/PersistenceLayer.java index fba1b52c..0c75b8d4 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/PersistenceLayer.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/persistence/PersistenceLayer.java @@ -4,7 +4,6 @@ import uk.co.compendiumdev.challenge.ChallengerState; import uk.co.compendiumdev.challenge.challengers.Challengers; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonPopulator; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; public class PersistenceLayer { @@ -43,7 +42,7 @@ public PersistenceResponse tryToLoadChallenger(final Challengers challengers, if(!response.getDatabaseContents().isEmpty()){ new JsonPopulator(response.getDatabaseContents()).populate( challengers.getErModel().getSchema(), - challengers.getErModel().getInstanceData(databaseName) + challengers.getErModel().getRepository(databaseName) ); }else{ // set the database to default values @@ -76,11 +75,12 @@ public PersistenceLayer(StorageType storeWhere){ } } - public PersistenceResponse saveChallengerStatus(ChallengerAuthData data, ERInstanceData instanceData){ + public PersistenceResponse saveChallengerStatus(ChallengerAuthData data, String databaseContents){ if(storeOn== StorageType.LOCAL){ PersistenceResponse fileStoreChallenger = file.saveChallengerStatus(data); - PersistenceResponse fileStoreDatabase = dbfile.saveDatabaseContent(data.getXChallenger(), instanceData); + PersistenceResponse fileStoreDatabase = + dbfile.saveDatabaseContent(data.getXChallenger(), databaseContents); return new PersistenceResponse(). withSuccess(fileStoreChallenger.isSuccess() && fileStoreDatabase.isSuccess()). withErrorMessage(fileStoreChallenger.getErrorMessage() + fileStoreDatabase.getErrorMessage()). diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java index 83192aa8..4b584f49 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java @@ -1,18 +1,24 @@ package uk.co.compendiumdev.challenge.practicemodes.simpleapi; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.Random; -public class SimpleAPITestDataPopulator implements DataPopulator { +public class SimpleAPITestDataPopulator implements RepositoryDataPopulator { @Override public void populate(final ERSchema schema, final ERInstanceData database) { + populate(schema, new InMemoryThingRepository("__simple-api-populator", database)); + } + @Override + public void populate(final ERSchema schema, final ThingRepository repository) { String [] types={ "book", "book", @@ -23,26 +29,19 @@ public void populate(final ERSchema schema, final ERInstanceData database) { "dvd", "blu-ray"}; - EntityInstanceCollection items = database.getInstanceCollectionForEntityNamed("item"); + EntityDefinition item = schema.getEntityDefinitionNamed("item"); Random random = new Random(); for(String type : types){ - createManagedInstance(items). + EntityInstance instance = new EntityInstance(item). setValue("type", type). setValue("numberinstock", String.valueOf(random.nextInt(20))). setValue("isbn13", randomIsbn(random)). - setValue("price", String.valueOf(random.nextInt(99)) + "." + String.valueOf(random.nextInt(99)) ) - ; + setValue("price", String.valueOf(random.nextInt(99)) + "." + String.valueOf(random.nextInt(99)) ); + repository.addInstance(instance); } } - - private EntityInstance createManagedInstance(EntityInstanceCollection entityStorage) { - EntityInstance instance = new EntityInstance(entityStorage.definition()); - entityStorage.addInstance(instance); - return instance; - } - private String randomIsbn(Random random){ return RandomIsbnGenerator.generate(random); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java index 8c242929..3deacfa0 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java @@ -1,6 +1,7 @@ package uk.co.compendiumdev.thingifier.core; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.LegacyDataPopulatorAdapter; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.Cardinality; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; @@ -222,7 +223,6 @@ private void populateRepository(final ThingRepository repository) { return; } - dataPopulator.populate(getSchema(), repository.getInstanceData()); - repository.flush(); + LegacyDataPopulatorAdapter.populate(dataPopulator, getSchema(), repository); } } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java new file mode 100644 index 00000000..2dfc8719 --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java @@ -0,0 +1,19 @@ +package uk.co.compendiumdev.thingifier.core.domain.datapopulator; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +public final class LegacyDataPopulatorAdapter { + + private LegacyDataPopulatorAdapter() { + } + + @Deprecated + public static void populate( + final DataPopulator dataPopulator, + final ERSchema schema, + final ThingRepository repository) { + dataPopulator.populate(schema, repository.getInstanceData()); + repository.flush(); + } +} diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java index c69fb985..37300bf7 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java @@ -7,6 +7,7 @@ import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfigProfiles; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonPopulator; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.LegacyDataPopulatorAdapter; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; @@ -317,9 +318,8 @@ public void ensureCreatedAndPopulatedInstanceDatabaseFromJson(String databaseNam new JsonPopulator(jsonDatabaseContents).populate( getERmodel().getSchema(), - getERmodel().getRepository(databaseName).getInstanceData() + getERmodel().getRepository(databaseName) ); - getERmodel().getRepository(databaseName).flush(); } @@ -333,7 +333,6 @@ private void populateRepository(final ThingRepository repository) { return; } - dataPopulator.populate(erm.getSchema(), repository.getInstanceData()); - repository.flush(); + LegacyDataPopulatorAdapter.populate(dataPopulator, erm.getSchema(), repository); } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java index f8a8315e..f4dc98f0 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java @@ -4,24 +4,25 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.ArrayList; import java.util.Map; import java.util.UUID; -public class JsonPopulator implements DataPopulator { +public class JsonPopulator implements RepositoryDataPopulator { private final String jsonData; private ERSchema schema; - private ERInstanceData database; + private ThingRepository repository; public JsonPopulator(String jsonDatabaseContents) { this.jsonData = jsonDatabaseContents; @@ -29,9 +30,15 @@ public JsonPopulator(String jsonDatabaseContents) { @Override public void populate(ERSchema schema, ERInstanceData database) { + populate(schema, new InMemoryThingRepository("__json-import", database)); + } + + @Override + public void populate(ERSchema schema, ThingRepository repository) { this.schema = schema; - this.database = database; + this.repository = repository; + this.repository.refreshSchema(schema); JsonElement data = JsonParser.parseString(jsonData); @@ -41,6 +48,7 @@ public void populate(ERSchema schema, ERInstanceData database) { } JsonObject entities = data.getAsJsonObject(); + repository.clearAllData(); entities.entrySet().forEach(property -> { populateEntityInstances(property); }); @@ -57,8 +65,6 @@ private void populateEntityInstances(Map.Entry entity) { throw new RuntimeException(String.format("ERROR: Expected array of instances as %s value", entity.getKey())); } - database.clearAllData(); - JsonArray instances = entity.getValue().getAsJsonArray(); instances.forEach(instance -> {populateAsInstanceOf(entity.getKey(), instance);}); @@ -100,7 +106,6 @@ private void populateAsInstanceOf(String entityPluralName, JsonElement instance) } // instance is valid, so add it - EntityInstanceCollection instances = database.getInstanceCollectionForEntityNamed(entityDefn.getName()); - instances.addInstance(entityInstance); + repository.addInstance(entityInstance); } } From 2f41878c64046fa1a3d3e522dd90a6328f9b3ee4 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 7 Jul 2026 21:28:33 +0100 Subject: [PATCH 05/22] Remove internal legacy repository escape usage --- .../apimodel/ChallengeThingifier.java | 18 ++- .../apimodel/TodoAPIDataPopulator.java | 7 -- .../challengesrouting/AuthRoutes.java | 2 +- .../simpleapi/AddMoreItemsIfNecessary.java | 15 +-- .../ResetAutoIncrementWhenTooHigh.java | 30 ++--- .../simpleapi/SimpleAPITestDataPopulator.java | 7 -- .../simulation/SimulationRoutes.java | 48 ++++---- .../RepositoryMigrationGuardTest.java | 114 ++++++++++++++++++ .../thingifier/core/EntityRelModel.java | 17 ++- .../domain/datapopulator/DataPopulator.java | 5 + .../RepositoryDataPopulator.java | 13 ++ .../instances/EntityInstanceCollection.java | 2 + .../repository/InMemoryThingRepository.java | 14 +++ .../repository/SqliteThingRepository.java | 9 ++ .../core/repository/ThingRepository.java | 22 ++++ .../thingifier/core/EntityRelModelTest.java | 18 +++ .../ThingRepositoryContractTest.java | 8 ++ .../compendiumdev/thingifier/Thingifier.java | 18 +++ .../api/ermodelconversion/JsonPopulator.java | 7 -- .../BodyCreationValidator.java | 10 ++ .../BodyRelationshipValidator.java | 5 + .../api/restapihandlers/ThingCreation.java | 10 ++ 22 files changed, 317 insertions(+), 82 deletions(-) create mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java index 95267f41..d7e901c9 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java @@ -9,7 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class ChallengeThingifier { @@ -49,21 +49,16 @@ public void populateThingifierFrom(ChallengeDefinitions challengeDefinitions) { this.challengeDefinitions = challengeDefinitions; // create all instances from the definitions, then when we want to // set all the status codes to the specific challenger status + ThingRepository repository = challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); for (ChallengeDefinitionData challenge : challengeDefinitions.getChallenges()) { - EntityInstanceCollection instances = challengeThingifier.getThingInstancesNamed(challengeDefn.getName(), EntityRelModel.DEFAULT_DATABASE_NAME); - createManagedInstance(instances). + EntityInstance instance = new EntityInstance(challengeDefn). overrideValue("id", challenge.id). setValue("name", challenge.name). setValue("description", challenge.description); + repository.addInstance(instance); } } - private EntityInstance createManagedInstance(EntityInstanceCollection entityStorage) { - EntityInstance instance = new EntityInstance(entityStorage.definition()); - entityStorage.addInstance(instance); - return instance; - } - public void populateThingifierFromStatus(ChallengerAuthData challenger){ ChallengerAuthData challengerToUse = challenger; @@ -73,16 +68,17 @@ public void populateThingifierFromStatus(ChallengerAuthData challenger){ challengerToUse = new ChallengerAuthData(challengeDefinitions.getDefinedChallenges()); } + ThingRepository repository = challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); for (ChallengeDefinitionData challenge : challengeDefinitions.getChallenges()) { final EntityInstance instance = - challengeThingifier.getThingInstancesNamed(challengeDefn.getName(), EntityRelModel.DEFAULT_DATABASE_NAME) - .findInstanceByFieldNameAndValue("id", challenge.id); + repository.findInstanceByFieldNameAndValue(challengeDefn, "id", challenge.id); instance.setValue("status", challengerToUse.statusOfChallenge( challengeDefinitions. getChallenge(challenge.name) ).toString() ); + repository.updateInstance(instance); } } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java index 33b48c4b..2a21b7a4 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java @@ -3,18 +3,11 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoAPIDataPopulator implements RepositoryDataPopulator { - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { - populate(schema, new InMemoryThingRepository("__todo-populator", database)); - } - @Override public void populate(final ERSchema schema, final ThingRepository repository) { String [] todos={ diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java index c252839b..198b3dce 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java @@ -273,7 +273,7 @@ public void configure(final Challengers challengers, ApiResponse response=null; response = new ThingCreation(this.secretNoteStore).with( new BodyParser(myRequest, List.of("secretnote")), - this.secretNoteStore.getThingInstancesNamed("secretnote", EntityRelModel.DEFAULT_DATABASE_NAME), EntityRelModel.DEFAULT_DATABASE_NAME); + this.secretNote, EntityRelModel.DEFAULT_DATABASE_NAME); if (!response.isErrorResponse()) { EntityInstance returnedInstance = response.getReturnedInstance(); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/AddMoreItemsIfNecessary.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/AddMoreItemsIfNecessary.java index 5eedaecb..028bb540 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/AddMoreItemsIfNecessary.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/AddMoreItemsIfNecessary.java @@ -5,8 +5,8 @@ import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.application.httpapimessagehooks.HttpApiRequestHook; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class AddMoreItemsIfNecessary implements HttpApiRequestHook { @@ -19,13 +19,10 @@ public AddMoreItemsIfNecessary(EntityRelModel eRmodel){ @Override public HttpApiResponse run(HttpApiRequest request, ThingifierApiConfig config) { - ERInstanceData instanceData = erModel.getInstanceData(EntityRelModel.DEFAULT_DATABASE_NAME); - if(instanceData!=null){ - EntityInstanceCollection collection = instanceData. - getInstanceCollectionForEntityNamed("item"); - if(collection != null && collection.countInstances()<5) { - erModel.populateDatabase(EntityRelModel.DEFAULT_DATABASE_NAME); - } + EntityDefinition item = erModel.getSchema().getEntityDefinitionNamed("item"); + ThingRepository repository = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + if(repository != null && item != null && repository.countInstances(item)<5) { + erModel.populateDatabase(EntityRelModel.DEFAULT_DATABASE_NAME); } return null; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java index 6d3e2a27..c3d041af 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java @@ -5,9 +5,9 @@ import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.application.httpapimessagehooks.HttpApiRequestHook; import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class ResetAutoIncrementWhenTooHigh implements HttpApiRequestHook { @@ -20,19 +20,19 @@ public ResetAutoIncrementWhenTooHigh(EntityRelModel eRmodel){ @Override public HttpApiResponse run(HttpApiRequest request, ThingifierApiConfig config) { - ERInstanceData instanceData = erModel.getInstanceData(EntityRelModel.DEFAULT_DATABASE_NAME); - if(instanceData!=null){ - EntityInstanceCollection collection = instanceData. - getInstanceCollectionForEntityNamed("item"); - AutoIncrement idCounter = collection.getCounters().get("id"); - //if(idCounter.getCurrentValue()>2140000000){ - if(idCounter.getCurrentValue()>99999){ - // reset it - idCounter.incrementToNextAbove(0); - } - if(collection != null && collection.countInstances()<5) { - erModel.populateDatabase(EntityRelModel.DEFAULT_DATABASE_NAME); - } + EntityDefinition item = erModel.getSchema().getEntityDefinitionNamed("item"); + ThingRepository repository = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + if(repository == null || item == null) { + return null; + } + + AutoIncrement idCounter = repository.countersFor(item).get("id"); + //if(idCounter.getCurrentValue()>2140000000){ + if(idCounter != null && idCounter.getCurrentValue()>99999){ + repository.resetAutoIncrementCounter(item, "id"); + } + if(repository.countInstances(item)<5) { + erModel.populateDatabase(EntityRelModel.DEFAULT_DATABASE_NAME); } return null; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java index 4b584f49..3ac3c815 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java @@ -3,20 +3,13 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.Random; public class SimpleAPITestDataPopulator implements RepositoryDataPopulator { - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { - populate(schema, new InMemoryThingRepository("__simple-api-populator", database)); - } - @Override public void populate(final ERSchema schema, final ThingRepository repository) { String [] types={ diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java index 0f35f87a..4ca4421d 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java @@ -5,18 +5,20 @@ import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; -import uk.co.compendiumdev.thingifier.api.restapihandlers.RestApiGetHandler; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.application.httprouting.ThingifierAutoDocGenRouting; import uk.co.compendiumdev.thingifier.application.routehandlers.HttpApiRequestHandler; import uk.co.compendiumdev.thingifier.application.routehandlers.SparkApiRequestResponseHandler; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.validation.MaximumLengthValidationRule; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListFilter; +import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListSorter; +import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.htmlgui.htmlgen.DefaultGUIHTML; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; @@ -32,7 +34,7 @@ public class SimulationRoutes { private JsonThing jsonThing; public Thingifier simulation; public EntityDefinition entityDefn; - private EntityInstanceCollection entityStorage; + private ThingRepository entityRepository; private ThingifierApiDocumentationDefn apiDocDefn; private ThingifierAutoDocGenRouting simulatorDocsRouting; @@ -60,18 +62,14 @@ public void setUpData(){ withValidation(new MaximumLengthValidationRule(200)) ); - entityStorage = simulation.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + entityRepository = simulation.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); for(int id=1; id<=10; id++){ - createManagedInstance(entityStorage). - //setValue("id", String.valueOf(id)). - setValue("name", "entity number " +id); + createManagedEntityNamed("entity number " + id); } - createManagedInstance(entityStorage). - //setValue("id", String.valueOf(id)). - setValue("name", "bob"); + createManagedEntityNamed("bob"); // this gives us access to the common http processing functions httpApi = new ThingifierHttpApi(simulation); @@ -116,9 +114,9 @@ public void setUpData(){ } - private EntityInstance createManagedInstance(EntityInstanceCollection entityStorage) { - EntityInstance instance = new EntityInstance(entityStorage.definition()); - entityStorage.addInstance(instance); + private EntityInstance createManagedEntityNamed(final String name) { + EntityInstance instance = new EntityInstance(entityDefn).setValue("name", name); + entityRepository.addInstance(instance); return instance; } @@ -156,16 +154,20 @@ public void configure() { idsToRemove.add(11); // process it because the request validated - List instances = new ArrayList(); - for (EntityInstance possible : entityStorage.getInstances()) { + List instances = new ArrayList<>(); + for (EntityInstance possible : entityRepository.listInstances(entityDefn)) { if (!idsToRemove.contains( possible.getFieldValue("id").asInteger())) { instances.add(possible); } } - Thingifier cloned = simulation.cloneWithDifferentData(instances); - return new RestApiGetHandler(cloned).handle("entities", anHttpApiRequest.getFilterableQueryParams(), anHttpApiRequest.getHeaders()); + QueryFilterParams queryParams = anHttpApiRequest.getFilterableQueryParams(); + instances = new EntityInstanceListFilter(queryParams).filter(instances); + instances = new EntityInstanceListSorter(queryParams).sort(instances); + return ApiResponse.success(). + returnInstanceCollection(instances). + resultContainsType(entityDefn); }; @@ -187,7 +189,7 @@ public void configure() { // process it because the request validated String id = anHttpApiRequest.getUrlParam(":id"); - EntityInstance instance = entityStorage.findInstanceByPrimaryKey(id); + EntityInstance instance = entityRepository.findInstanceByPrimaryKey(entityDefn, id); if (instance == null) { response = ApiResponse.error404("Could not find Entity with ID " + id); } else { @@ -232,8 +234,8 @@ public void configure() { return new SparkApiRequestResponseHandler(request, result, simulation). usingHandler((anHttpApiRequest) -> { - return ApiResponse.created(entityStorage. - findInstanceByPrimaryKey("11"), + return ApiResponse.created(entityRepository. + findInstanceByPrimaryKey(entityDefn, "11"), simulation.apiConfig()); }).handle(); }); @@ -245,7 +247,7 @@ public void configure() { if (id.equals("11")) { // we can create id 11 response = ApiResponse.created( - entityStorage.findInstanceByPrimaryKey("11"), + entityRepository.findInstanceByPrimaryKey(entityDefn, "11"), simulation.apiConfig()); } else { if (id.equals("10")) { @@ -254,7 +256,7 @@ public void configure() { overrideValue("id", "10").setValue("name", "eris"); response = ApiResponse.success().returnSingleInstance(fake); } else { - final EntityInstance instance = entityStorage.findInstanceByPrimaryKey(id); + final EntityInstance instance = entityRepository.findInstanceByPrimaryKey(entityDefn, id); if (instance == null) { if (anHttpApiRequest.getVerb() == HttpApiRequest.VERB.POST) { response = ApiResponse.error404("Could not find Entity with ID " + id); @@ -293,7 +295,7 @@ public void configure() { // we can delete id 9 response = new ApiResponse(204); } else { - final EntityInstance instance = entityStorage.findInstanceByPrimaryKey(id); + final EntityInstance instance = entityRepository.findInstanceByPrimaryKey(entityDefn, id); if (instance == null) { response = ApiResponse.error404("Could not find Entity with ID " + id); } else { diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java new file mode 100644 index 00000000..834d47d7 --- /dev/null +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java @@ -0,0 +1,114 @@ +package uk.co.compendiumdev.challenge.repository; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public class RepositoryMigrationGuardTest { + + @Test + public void productionCodeDoesNotCallLegacySnapshotEscapesOutsideCompatibilityAdapters() throws IOException { + List violations = productionJavaLinesContaining( + List.of( + "getInstanceData(", + "getThingInstancesNamed(", + "getInstancesForSingularOrPluralNamedEntity(", + "getThings("), + List.of( + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java", + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java", + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java", + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java", + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java", + "thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java")); + + Assertions.assertTrue( + violations.isEmpty(), + "Production code should use repository-native APIs instead of legacy snapshot escapes:\n" + + String.join("\n", violations)); + } + + @Test + public void productionCodeDoesNotUseDeprecatedSimpleQueryOutsideItsOwnClass() throws IOException { + List violations = productionJavaLinesContaining( + List.of("SimpleQuery"), + List.of("ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java")); + + Assertions.assertTrue( + violations.isEmpty(), + "Production code should use RepositoryUrlQuery or repository APIs instead of SimpleQuery:\n" + + String.join("\n", violations)); + } + + private List productionJavaLinesContaining( + final List terms, + final List allowedFileSuffixes) throws IOException { + + Path root = repoRoot(); + List violations = new ArrayList<>(); + + for (String module : List.of("ercoremodel", "thingifier", "challenger")) { + Path sourceRoot = root.resolve(module).resolve("src/main/java"); + if (!Files.exists(sourceRoot)) { + continue; + } + + try (Stream paths = Files.walk(sourceRoot)) { + for (Path path : paths.filter(path -> path.toString().endsWith(".java")).toList()) { + String relativePath = root.relativize(path).toString().replace('\\', '/'); + if (isAllowed(relativePath, allowedFileSuffixes)) { + continue; + } + + List lines = Files.readAllLines(path); + for (int lineIndex = 0; lineIndex < lines.size(); lineIndex++) { + String line = lines.get(lineIndex); + if (containsAny(line, terms)) { + violations.add(relativePath + ":" + (lineIndex + 1) + ": " + line.trim()); + } + } + } + } + } + + return violations; + } + + private Path repoRoot() { + Path current = Paths.get("").toAbsolutePath(); + while (current != null) { + if (Files.exists(current.resolve("ercoremodel")) && + Files.exists(current.resolve("thingifier")) && + Files.exists(current.resolve("challenger"))) { + return current; + } + current = current.getParent(); + } + throw new IllegalStateException("Could not locate thingifier repository root"); + } + + private boolean isAllowed(final String relativePath, final List allowedFileSuffixes) { + for (String allowed : allowedFileSuffixes) { + if (relativePath.endsWith(allowed)) { + return true; + } + } + return false; + } + + private boolean containsAny(final String line, final List terms) { + for (String term : terms) { + if (line.contains(term)) { + return true; + } + } + return false; + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java index 3deacfa0..ccc83d26 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java @@ -71,16 +71,29 @@ public ERSchema getSchema(){ return schema; } - // TODO: use of this is basically deprecated since is refers to the default database - @Deprecated() // we should use the parameterised version + /** + * @deprecated Compatibility snapshot access. Use {@link #getRepository(String)} + * and repository-native reads/writes instead. + */ + @Deprecated public ERInstanceData getInstanceData(){ return getInstanceData(DEFAULT_DATABASE_NAME); } + /** + * @deprecated This returns compatibility instance data, not JSON. Use + * {@link #exportInstanceDataAsJson(String)} for JSON export. + */ + @Deprecated public ERInstanceData getInstanceDataAsJson(){ return getInstanceData(DEFAULT_DATABASE_NAME); } + /** + * @deprecated Compatibility snapshot access. Use {@link #getRepository(String)} + * and repository-native reads/writes instead. + */ + @Deprecated public ERInstanceData getInstanceData(String databaseKey) { ThingRepository repository = repositories.getRepository(databaseKey); if(repository==null){ diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java index 052acf1b..e8c814e0 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java @@ -5,5 +5,10 @@ public interface DataPopulator { + /** + * @deprecated Implement {@link RepositoryDataPopulator} so population writes + * through the configured repository without hydrating a compatibility snapshot. + */ + @Deprecated void populate(ERSchema schema, ERInstanceData database); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java index 8a30d2ff..e8ab7735 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java @@ -1,9 +1,22 @@ package uk.co.compendiumdev.thingifier.core.domain.datapopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; +import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public interface RepositoryDataPopulator extends DataPopulator { + /** + * @deprecated Compatibility bridge for older callers that still provide + * ERInstanceData directly. Runtime population should call + * {@link #populate(ERSchema, ThingRepository)}. + */ + @Override + @Deprecated + default void populate(final ERSchema schema, final ERInstanceData database) { + populate(schema, new InMemoryThingRepository("__repository-populator-compatibility", database)); + } + void populate(ERSchema schema, ThingRepository repository); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java index a53c6eb8..4898ea6b 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java @@ -66,6 +66,8 @@ public EntityInstanceCollection addInstances(List addInstances) public EntityInstance addInstance(EntityInstance instance) { + ensureCountersInitialized(); + if(instance.getEntity()!=definition){ throw new RuntimeException(String.format( "ERROR: Tried to add a %s instance to the %s", diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java index 4b83b379..b30b3b79 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java @@ -185,6 +185,20 @@ public Map countersFor(final EntityDefinition entity) { return getInstanceCollectionForEntityNamed(entity.getName()).getCounters(); } + @Override + public void resetAutoIncrementCounter(final EntityDefinition entity, final String fieldName) { + Field field = entity.getField(fieldName); + if (field == null || field.getType() != FieldType.AUTO_INCREMENT) { + throw new IllegalArgumentException( + String.format("%s is not an auto-increment field on %s", fieldName, entity.getName())); + } + + AutoIncrement counter = countersFor(entity).get(fieldName); + if (counter != null) { + counter.incrementToNextAbove(field.getDefaultValue().asInteger() - 1); + } + } + @Override public void setNextIdCountersToAccomodate( final EntityDefinition entity, final List fieldValues) { diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java index 8c7351d2..36a74074 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java @@ -410,6 +410,15 @@ public void flush() { }); } + @Override + public void resetAutoIncrementCounter(final EntityDefinition entity, final String fieldName) { + ensureSchemaReady(); + runInTransaction(() -> { + super.resetAutoIncrementCounter(entity, fieldName); + persistCountersFor(entity); + }); + } + @Override public void close() { closed = true; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java index 16d49faf..6a8fe5cf 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java @@ -24,15 +24,35 @@ public interface ThingRepository extends AutoCloseable { void refreshSchema(ERSchema schema); + /** + * @deprecated Compatibility snapshot access. Repository callers should use + * repository-native query and mutation methods instead. + */ @Deprecated ERInstanceData getInstanceData(); + /** + * @deprecated Compatibility collection creation. Repository implementations + * should initialize schema through {@link #initializeFrom(ERSchema)} or + * {@link #refreshSchema(ERSchema)}. + */ + @Deprecated EntityInstanceCollection createInstanceCollectionFor(EntityDefinition definition); void createInstanceCollectionsFrom(ERSchema schema); + /** + * @deprecated Compatibility collection access. Use schema entity definitions + * with {@link #listInstances(EntityDefinition)} or reporting/export APIs. + */ + @Deprecated List getAllInstanceCollections(); + /** + * @deprecated Compatibility collection access. Use repository-native methods + * such as {@link #listInstances(EntityDefinition)} and + * {@link #findInstanceByQueryIdentifier(EntityDefinition, String)}. + */ @Deprecated EntityInstanceCollection getInstanceCollectionForEntityNamed(String entityName); @@ -80,6 +100,8 @@ List listRelatedInstances( Map countersFor(EntityDefinition entity); + void resetAutoIncrementCounter(EntityDefinition entity, String fieldName); + void setNextIdCountersToAccomodate(EntityDefinition entity, List fieldValues); void connectRelationship(EntityInstance from, String relationshipName, EntityInstance to); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java index 7f39fb19..0b9571f7 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java @@ -78,6 +78,24 @@ public void canCreateAThingInAModel() { Assertions.assertTrue(erm.getEntityNames().contains("thing")); } + @Test + public void repositoryCanAddExplicitAutoIncrementValueWhenFieldAddedAfterCollectionCreated() { + + EntityRelModel erm = new EntityRelModel(); + EntityDefinition defn = erm.createEntityDefinition("challenge", "challenges"); + defn.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); + + ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityInstance explicitId = new EntityInstance(defn).overrideValue("id", "12"); + repository.addInstance(explicitId); + + EntityInstance nextId = new EntityInstance(defn); + repository.addInstance(nextId); + + Assertions.assertEquals("12", explicitId.getPrimaryKeyValue()); + Assertions.assertEquals("13", nextId.getPrimaryKeyValue()); + } + @Test public void canFindAThingInAModel() { diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java index cd3ebc25..3af1c1d0 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java @@ -569,6 +569,14 @@ private void exerciseRepositoryContract(final ThingRepository repository) { Assertions.assertEquals(0, repository.listInstances(taskDefinition).size()); Assertions.assertEquals(0, repository.countInstances(projectDefinition)); Assertions.assertEquals(0, repository.countInstances(taskDefinition)); + + repository.resetAutoIncrementCounter(projectDefinition, "id"); + + EntityInstance resetProject = new EntityInstance(projectDefinition); + resetProject.setValue("title", "Reset project"); + repository.addInstance(resetProject); + + Assertions.assertEquals("1", resetProject.getPrimaryKeyValue()); } private void exerciseProviderIsolation(final ThingRepositoryProvider provider) { diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java index 37300bf7..e3622880 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java @@ -126,6 +126,11 @@ public boolean hasRelationshipNamed(final String relationshipName) { // Instances + /** + * @deprecated Compatibility collection access. Use {@link #getRepository(String)} + * with schema entity definitions, or {@link #listThingInstancesNamed(String, String)}. + */ + @Deprecated public List getThings(final String database) { return erm.getRepository(database).getAllInstanceCollections(); } @@ -135,6 +140,10 @@ public EntityInstance findThingInstanceByGuid(final String thingGUID, final Stri return erm.getRepository(database).findEntityInstanceByGUID(thingGUID); } + /** + * @deprecated Compatibility collection access. Use {@link #getRepository(String)} + * or {@link #listThingInstancesNamed(String, String)}. + */ @Deprecated public EntityInstanceCollection getThingInstancesNamed(final String aName, final String database) { return erm.getRepository(database).getInstanceCollectionForEntityNamed(aName); @@ -162,6 +171,10 @@ public EntityInstance findThingInstanceByFieldNameAndValue( return repository.findInstanceByFieldNameAndValue(definition, fieldName, fieldValue); } + /** + * @deprecated Compatibility collection access. Use {@link #getRepository(String)} + * with {@link EntityDefinition} resolved from {@link #getERmodel()}. + */ @Deprecated public EntityInstanceCollection getInstancesForSingularOrPluralNamedEntity(final String term, final String database) { final EntityDefinition defn = erm.getSchema().getDefinitionWithSingularOrPluralNamed(term); @@ -285,6 +298,11 @@ public String getInitialParagraph() { return this.initialParagraph; } + /** + * @deprecated Compatibility helper backed by legacy instance data. Prefer a + * repository/provider configured with the desired data source. + */ + @Deprecated public Thingifier cloneWithDifferentData(final List instances) { return new Thingifier( this.getERmodel().cloneWithDifferentData(instances), this.apiConfig(), diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java index f4dc98f0..4cfc533b 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java @@ -8,9 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; @@ -28,11 +26,6 @@ public JsonPopulator(String jsonDatabaseContents) { this.jsonData = jsonDatabaseContents; } - @Override - public void populate(ERSchema schema, ERInstanceData database) { - populate(schema, new InMemoryThingRepository("__json-import", database)); - } - @Override public void populate(ERSchema schema, ThingRepository repository) { diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java index 43d1b17c..cb0f9a0a 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java @@ -19,6 +19,11 @@ public BodyCreationValidator(final Thingifier thingifier) { this.thingifier = thingifier; } + /** + * @deprecated Use {@link #validate(BodyParser, EntityDefinition)} to avoid + * requiring a compatibility collection. + */ + @Deprecated public ValidationReport validate(final BodyParser bodyargs, final EntityInstanceCollection thing) { final EntityDefinition thingDefinition = thing.definition(); return validate(bodyargs, thingDefinition); @@ -42,6 +47,11 @@ public ValidationReport validate(final BodyParser bodyargs, final EntityDefiniti return report; } + /** + * @deprecated Use {@link #areFieldsUnique(BodyParser, EntityDefinition, ThingRepository, List)} + * so uniqueness checks use the configured repository. + */ + @Deprecated public ValidationReport areFieldsUnique(final BodyParser bodyargs, final EntityInstanceCollection thing, List uniqueFields) { final ValidationReport report = new ValidationReport(); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java index 49ff580a..0c2b1cee 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java @@ -19,6 +19,11 @@ public BodyRelationshipValidator(final Thingifier thingifier) { this.thingifier = thingifier; } + /** + * @deprecated Use {@link #validate(BodyParser, EntityDefinition, String)} to + * avoid requiring a compatibility collection. + */ + @Deprecated public ValidationReport validate(final BodyParser bodyargs, final EntityInstanceCollection thing, final String database) { final EntityDefinition thingDefinition = thing.definition(); return validate(bodyargs, thingDefinition, database); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java index 4eb2d6b5..fdf9ff18 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java @@ -21,6 +21,11 @@ public ThingCreation(final Thingifier thingifier) { this.thingifier = thingifier; } + /** + * @deprecated Use {@link #with(BodyParser, EntityDefinition, String)} so + * creation can stay repository-native. + */ + @Deprecated public ApiResponse with(final BodyParser bodyargs, final EntityInstanceCollection thing, final String database) { return with(bodyargs, thing.definition(), database); } @@ -47,6 +52,11 @@ public ApiResponse with(final BodyParser bodyargs, final EntityDefinition thing, } // create with GUID and IDs is normally associated with PUT or 'insert' + /** + * @deprecated Use {@link #withPrimaryKey(String, BodyParser, EntityDefinition, String)} + * so creation can stay repository-native. + */ + @Deprecated public ApiResponse withPrimaryKey(final String primaryKey, final BodyParser bodyargs, final EntityInstanceCollection thing, final String database) { return withPrimaryKey(primaryKey, bodyargs, thing.definition(), database); From e7f84ceae57f2cb3ac80f5e0d0e162bfbea0bf57 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Tue, 7 Jul 2026 21:34:56 +0100 Subject: [PATCH 06/22] Tighten repository compatibility boundaries --- .../RepositoryMigrationGuardTest.java | 31 ++++++++++++- .../thingifier/core/EntityRelModel.java | 15 ++++--- .../domain/datapopulator/DataPopulator.java | 2 +- .../LegacyDataPopulatorAdapter.java | 2 +- .../RepositoryDataPopulator.java | 2 +- .../thingifier/core/query/SimpleQuery.java | 2 +- .../core/repository/ThingRepository.java | 8 ++-- .../compendiumdev/thingifier/Thingifier.java | 8 ++-- .../BodyCreationValidator.java | 43 ------------------- .../BodyRelationshipValidator.java | 11 ----- .../api/restapihandlers/ThingCreation.java | 20 --------- 11 files changed, 50 insertions(+), 94 deletions(-) diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java index 834d47d7..a73738cc 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java @@ -20,10 +20,16 @@ public void productionCodeDoesNotCallLegacySnapshotEscapesOutsideCompatibilityAd "getInstanceData(", "getThingInstancesNamed(", "getInstancesForSingularOrPluralNamedEntity(", - "getThings("), + "getThings(", + "cloneWithDifferentData(", + "createInstanceCollectionFor(", + "getAllInstanceCollections(", + "getInstanceCollectionForEntityNamed("), List.of( "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java", + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/ERInstanceData.java", "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java", + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java", "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java", "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java", "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java", @@ -35,6 +41,19 @@ public void productionCodeDoesNotCallLegacySnapshotEscapesOutsideCompatibilityAd String.join("\n", violations)); } + @Test + public void thingifierAndChallengerProductionCodeDoesNotDependOnCompatibilityCollections() throws IOException { + List violations = productionJavaLinesContaining( + List.of("EntityInstanceCollection"), + List.of("thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java"), + List.of("thingifier", "challenger")); + + Assertions.assertTrue( + violations.isEmpty(), + "Thingifier and Challenger runtime code should not use compatibility collections outside the deprecated public Thingifier API:\n" + + String.join("\n", violations)); + } + @Test public void productionCodeDoesNotUseDeprecatedSimpleQueryOutsideItsOwnClass() throws IOException { List violations = productionJavaLinesContaining( @@ -50,11 +69,19 @@ public void productionCodeDoesNotUseDeprecatedSimpleQueryOutsideItsOwnClass() th private List productionJavaLinesContaining( final List terms, final List allowedFileSuffixes) throws IOException { + return productionJavaLinesContaining( + terms, allowedFileSuffixes, List.of("ercoremodel", "thingifier", "challenger")); + } + + private List productionJavaLinesContaining( + final List terms, + final List allowedFileSuffixes, + final List modules) throws IOException { Path root = repoRoot(); List violations = new ArrayList<>(); - for (String module : List.of("ercoremodel", "thingifier", "challenger")) { + for (String module : modules) { Path sourceRoot = root.resolve(module).resolve("src/main/java"); if (!Files.exists(sourceRoot)) { continue; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java index ccc83d26..69db330d 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java @@ -61,9 +61,7 @@ public EntityDefinition createEntityDefinition(final String entityName, final St public EntityDefinition createEntityDefinition(final String entityName, final String pluralName, int maximumNumberOfInstances) { EntityDefinition defn = schema.defineEntity(entityName, pluralName, maximumNumberOfInstances); - for(String databaseKey : repositories.getRepositoryNames()){ - repositories.getRepository(databaseKey).createInstanceCollectionFor(defn); - } + refreshRepositorySchemas(); return defn; } @@ -75,7 +73,7 @@ public ERSchema getSchema(){ * @deprecated Compatibility snapshot access. Use {@link #getRepository(String)} * and repository-native reads/writes instead. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public ERInstanceData getInstanceData(){ return getInstanceData(DEFAULT_DATABASE_NAME); } @@ -84,7 +82,7 @@ public ERInstanceData getInstanceData(){ * @deprecated This returns compatibility instance data, not JSON. Use * {@link #exportInstanceDataAsJson(String)} for JSON export. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public ERInstanceData getInstanceDataAsJson(){ return getInstanceData(DEFAULT_DATABASE_NAME); } @@ -93,7 +91,7 @@ public ERInstanceData getInstanceDataAsJson(){ * @deprecated Compatibility snapshot access. Use {@link #getRepository(String)} * and repository-native reads/writes instead. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public ERInstanceData getInstanceData(String databaseKey) { ThingRepository repository = repositories.getRepository(databaseKey); if(repository==null){ @@ -136,6 +134,11 @@ public void close() { } // ERM Object Level + /** + * @deprecated Compatibility helper backed by legacy instance data. Prefer a + * repository/provider configured with the desired data source. + */ + @Deprecated(forRemoval = true, since = "1.5.6") public EntityRelModel cloneWithDifferentData(final List instances) { return new EntityRelModel(schema, new ERInstanceData(instances)); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java index e8c814e0..a6710e01 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java @@ -9,6 +9,6 @@ public interface DataPopulator { * @deprecated Implement {@link RepositoryDataPopulator} so population writes * through the configured repository without hydrating a compatibility snapshot. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") void populate(ERSchema schema, ERInstanceData database); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java index 2dfc8719..20edb97e 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java @@ -8,7 +8,7 @@ public final class LegacyDataPopulatorAdapter { private LegacyDataPopulatorAdapter() { } - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public static void populate( final DataPopulator dataPopulator, final ERSchema schema, diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java index e8ab7735..9323eeae 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java @@ -13,7 +13,7 @@ public interface RepositoryDataPopulator extends DataPopulator { * {@link #populate(ERSchema, ThingRepository)}. */ @Override - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") default void populate(final ERSchema schema, final ERInstanceData database) { populate(schema, new InMemoryThingRepository("__repository-populator-compatibility", database)); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java index 7b8ae465..b8d3b83a 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java @@ -31,7 +31,7 @@ * repositories such as SQLite can query without hydrating compatibility snapshots. * Keep this class only for compatibility/fallback paths until replacement is complete. */ -@Deprecated +@Deprecated(forRemoval = true, since = "1.5.6") public final class SimpleQuery implements UrlQueryResult { private final ERInstanceData database; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java index 6a8fe5cf..04e43845 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java @@ -28,7 +28,7 @@ public interface ThingRepository extends AutoCloseable { * @deprecated Compatibility snapshot access. Repository callers should use * repository-native query and mutation methods instead. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") ERInstanceData getInstanceData(); /** @@ -36,7 +36,7 @@ public interface ThingRepository extends AutoCloseable { * should initialize schema through {@link #initializeFrom(ERSchema)} or * {@link #refreshSchema(ERSchema)}. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") EntityInstanceCollection createInstanceCollectionFor(EntityDefinition definition); void createInstanceCollectionsFrom(ERSchema schema); @@ -45,7 +45,7 @@ public interface ThingRepository extends AutoCloseable { * @deprecated Compatibility collection access. Use schema entity definitions * with {@link #listInstances(EntityDefinition)} or reporting/export APIs. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") List getAllInstanceCollections(); /** @@ -53,7 +53,7 @@ public interface ThingRepository extends AutoCloseable { * such as {@link #listInstances(EntityDefinition)} and * {@link #findInstanceByQueryIdentifier(EntityDefinition, String)}. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") EntityInstanceCollection getInstanceCollectionForEntityNamed(String entityName); default String exportDataAsJson(final ERSchema schema) { diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java index e3622880..8bb1c069 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java @@ -130,7 +130,7 @@ public boolean hasRelationshipNamed(final String relationshipName) { * @deprecated Compatibility collection access. Use {@link #getRepository(String)} * with schema entity definitions, or {@link #listThingInstancesNamed(String, String)}. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public List getThings(final String database) { return erm.getRepository(database).getAllInstanceCollections(); } @@ -144,7 +144,7 @@ public EntityInstance findThingInstanceByGuid(final String thingGUID, final Stri * @deprecated Compatibility collection access. Use {@link #getRepository(String)} * or {@link #listThingInstancesNamed(String, String)}. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public EntityInstanceCollection getThingInstancesNamed(final String aName, final String database) { return erm.getRepository(database).getInstanceCollectionForEntityNamed(aName); } @@ -175,7 +175,7 @@ public EntityInstance findThingInstanceByFieldNameAndValue( * @deprecated Compatibility collection access. Use {@link #getRepository(String)} * with {@link EntityDefinition} resolved from {@link #getERmodel()}. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public EntityInstanceCollection getInstancesForSingularOrPluralNamedEntity(final String term, final String database) { final EntityDefinition defn = erm.getSchema().getDefinitionWithSingularOrPluralNamed(term); if(defn!=null){ @@ -302,7 +302,7 @@ public String getInitialParagraph() { * @deprecated Compatibility helper backed by legacy instance data. Prefer a * repository/provider configured with the desired data source. */ - @Deprecated + @Deprecated(forRemoval = true, since = "1.5.6") public Thingifier cloneWithDifferentData(final List instances) { return new Thingifier( this.getERmodel().cloneWithDifferentData(instances), this.apiConfig(), diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java index cb0f9a0a..9154c4c4 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyCreationValidator.java @@ -1,6 +1,5 @@ package uk.co.compendiumdev.thingifier.api.restapihandlers; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -19,16 +18,6 @@ public BodyCreationValidator(final Thingifier thingifier) { this.thingifier = thingifier; } - /** - * @deprecated Use {@link #validate(BodyParser, EntityDefinition)} to avoid - * requiring a compatibility collection. - */ - @Deprecated - public ValidationReport validate(final BodyParser bodyargs, final EntityInstanceCollection thing) { - final EntityDefinition thingDefinition = thing.definition(); - return validate(bodyargs, thingDefinition); - } - public ValidationReport validate(final BodyParser bodyargs, final EntityDefinition thingDefinition) { final ValidationReport report = new ValidationReport(); @@ -47,38 +36,6 @@ public ValidationReport validate(final BodyParser bodyargs, final EntityDefiniti return report; } - /** - * @deprecated Use {@link #areFieldsUnique(BodyParser, EntityDefinition, ThingRepository, List)} - * so uniqueness checks use the configured repository. - */ - @Deprecated - public ValidationReport areFieldsUnique(final BodyParser bodyargs, final EntityInstanceCollection thing, - List uniqueFields) { - final ValidationReport report = new ValidationReport(); - - for (Map.Entry entry : bodyargs.getFlattenedStringMap()) { - - if (uniqueFields.contains(entry.getKey())) { - String existingValue = entry.getValue(); - - if (existingValue != null && existingValue.trim().length() > 0) { - final EntityInstance foundInstance = thing.findInstanceByFieldNameAndValue( - entry.getKey(), - entry.getValue() - ); - - if (foundInstance != null) { - report.setValid(false); - report.addErrorMessage( - String.format("Found Existing item with %s of %s", - entry.getKey(), entry.getValue())); - } - } - } - } - return report; - } - public ValidationReport areFieldsUnique( final BodyParser bodyargs, final EntityDefinition thingDefinition, diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java index 0c2b1cee..fef4d067 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyRelationshipValidator.java @@ -1,6 +1,5 @@ package uk.co.compendiumdev.thingifier.api.restapihandlers; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -19,16 +18,6 @@ public BodyRelationshipValidator(final Thingifier thingifier) { this.thingifier = thingifier; } - /** - * @deprecated Use {@link #validate(BodyParser, EntityDefinition, String)} to - * avoid requiring a compatibility collection. - */ - @Deprecated - public ValidationReport validate(final BodyParser bodyargs, final EntityInstanceCollection thing, final String database) { - final EntityDefinition thingDefinition = thing.definition(); - return validate(bodyargs, thingDefinition, database); - } - public ValidationReport validate(final BodyParser bodyargs, final EntityDefinition thingDefinition, final String database) { final ValidationReport report = new ValidationReport(); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java index fdf9ff18..3d36e431 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java @@ -2,7 +2,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -21,15 +20,6 @@ public ThingCreation(final Thingifier thingifier) { this.thingifier = thingifier; } - /** - * @deprecated Use {@link #with(BodyParser, EntityDefinition, String)} so - * creation can stay repository-native. - */ - @Deprecated - public ApiResponse with(final BodyParser bodyargs, final EntityInstanceCollection thing, final String database) { - return with(bodyargs, thing.definition(), database); - } - public ApiResponse with(final BodyParser bodyargs, final EntityDefinition thing, final String database) { ValidationReport validated = new BodyRelationshipValidator(thingifier).validate(bodyargs, thing, database); @@ -52,16 +42,6 @@ public ApiResponse with(final BodyParser bodyargs, final EntityDefinition thing, } // create with GUID and IDs is normally associated with PUT or 'insert' - /** - * @deprecated Use {@link #withPrimaryKey(String, BodyParser, EntityDefinition, String)} - * so creation can stay repository-native. - */ - @Deprecated - public ApiResponse withPrimaryKey(final String primaryKey, final BodyParser bodyargs, - final EntityInstanceCollection thing, final String database) { - return withPrimaryKey(primaryKey, bodyargs, thing.definition(), database); - } - public ApiResponse withPrimaryKey(final String primaryKey, final BodyParser bodyargs, final EntityDefinition thing, final String database) { From 6bdd458e4e885591dde07cb03d74cebeedc67e0a Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 8 Jul 2026 17:03:40 +0100 Subject: [PATCH 07/22] Replace legacy query paths with repository API --- .../ChallengeApiModelRepositoryTest.java | 8 +- .../challengers/ChallengersTest.java | 4 +- .../RepositoryMigrationGuardTest.java | 23 +- .../RepositoryBackedTestCollection.java | 46 ++ .../ThingifierRepositoryTestSupport.java | 21 + .../ChallengeCompleteTest.java | 23 +- checkstyle-project-fqn.xml | 14 + .../thingifier/core/EntityRelModel.java | 62 +-- .../domain/datapopulator/DataPopulator.java | 14 - .../LegacyDataPopulatorAdapter.java | 19 - .../RepositoryDataPopulator.java | 15 +- .../domain/definitions/EntityDefinition.java | 4 +- .../field/instance/FieldValue.java | 4 +- .../definitions/relationship/Optionality.java | 6 +- .../TypeValidationFailedMessageGenerator.java | 2 +- .../thingifier/core/query/SimpleQuery.java | 335 ------------ .../repository/InMemoryThingRepository.java | 21 +- .../InMemoryThingRepositoryProvider.java | 5 - .../repository/SqliteThingRepository.java | 497 +++++++++++------- .../core/repository/ThingRepository.java | 42 -- .../thingifier/core/EntityRelModelTest.java | 246 ++++----- .../MultipleEntityInstancesDatabasesTest.java | 119 ++--- .../core/query/QueryFiltersBooleanTest.java | 11 +- .../core/query/QueryFiltersFloatTest.java | 11 +- .../core/query/QueryFiltersIdTest.java | 11 +- .../core/query/QueryFiltersIntegerTest.java | 11 +- .../core/query/QueryFiltersStringTest.java | 11 +- .../query/RepositoryUrlQueryTestSupport.java | 12 + .../query/SortingViaQueryFiltersTest.java | 36 +- .../query/TodoManagerQueryEngineTest.java | 29 +- .../ThingRepositoryContractTest.java | 65 +-- .../data/TodoAPIDataPopulator.java | 13 - .../data/TodoManagerAPIDataPopulator.java | 33 -- pmd-ruleset.xml | 16 + pom.xml | 49 ++ .../swaggerizer/TodoAPITestDataPopulator.java | 15 +- .../compendiumdev/thingifier/Thingifier.java | 65 +-- .../java/uk/co/compendiumdev/UsageTest.java | 9 +- ...leVerbPutEntityInstanceApiNonHttpTest.java | 21 +- .../casestudy/todomanager/ModelsTest.java | 9 +- .../RelationshipApiNonHttpTest.java | 17 +- .../RelationshipApiSqliteRepositoryTest.java | 8 - ...erbDeleteEntityInstanceApiNonHttpTest.java | 11 +- .../VerbGetEntityInstanceApiNonHttpTest.java | 11 +- .../VerbPostEntityInstanceApiNonHttpTest.java | 11 +- .../VerbPutEntityInstanceApiNonHttpTest.java | 15 +- .../http_api/DeleteRequestTest.java | 11 +- .../http_api/JsonRequestResponseTest.java | 11 +- .../http_api/RelationshipHttpTest.java | 23 +- .../http_api/XmlRequestResponseTest.java | 11 +- .../unit/BasicTodoManagerTest.java | 15 +- .../unit/OptionalityRelationshipTest.java | 19 +- .../ThingifierHttpApiRequestHandlingTest.java | 52 +- .../non_http/IdBasedInstanceNonHttpTest.java | 11 +- .../api/non_http/NestedObjectsApiTest.java | 7 +- .../api/response/ApiResponseAsJsonTest.java | 7 +- .../api/response/ApiResponseAsXmlTest.java | 7 +- .../api/response/ApiResponseTest.java | 7 +- .../DefaultGuiHtmlPagesRepositoryTest.java | 2 - .../RepositoryBackedTestCollection.java | 56 ++ .../ThingifierRepositoryTestSupport.java | 28 + 61 files changed, 965 insertions(+), 1332 deletions(-) create mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java create mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java create mode 100644 checkstyle-project-fqn.xml delete mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java delete mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java delete mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java create mode 100644 pmd-ruleset.xml create mode 100644 thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java create mode 100644 thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java index 1db0df1b..8ca9f75a 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java @@ -1,5 +1,7 @@ package uk.co.compendiumdev.challenge.apimodel; + +import uk.co.compendiumdev.challenge.testsupport.ThingifierRepositoryTestSupport; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.Thingifier; @@ -30,7 +32,7 @@ public void challengeApiCanUseSqliteInMemoryRepository() { instanceof SqliteThingRepository); Assertions.assertEquals( 10, - thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME). + ThingifierRepositoryTestSupport.collection(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME, "todo"). countInstances()); } } @@ -135,7 +137,6 @@ public void simpleEntityWritesUseSqliteRepositoryWithoutLoadingCompatibilitySnap new HttpHeadersBlock()); Assertions.assertEquals(201, create.getStatusCode()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); Assertions.assertEquals(1, repository.listInstances(note).size()); ApiResponse postAmend = thingifier.api().post( @@ -148,7 +149,6 @@ public void simpleEntityWritesUseSqliteRepositoryWithoutLoadingCompatibilitySnap "posted", repository.findInstanceByQueryIdentifier(note, "1"). getFieldValue("title").asString()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); ApiResponse putAmend = thingifier.api().put( "/notes/1", @@ -160,14 +160,12 @@ public void simpleEntityWritesUseSqliteRepositoryWithoutLoadingCompatibilitySnap "put", repository.findInstanceByQueryIdentifier(note, "1"). getFieldValue("title").asString()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); ApiResponse delete = thingifier.api().delete( "/notes/1", new HttpHeadersBlock()); Assertions.assertEquals(200, delete.getStatusCode()); Assertions.assertEquals(0, repository.listInstances(note).size()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengers/ChallengersTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengers/ChallengersTest.java index 21433822..3e8b1c66 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengers/ChallengersTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengers/ChallengersTest.java @@ -5,8 +5,6 @@ import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import java.lang.reflect.Field; import java.util.Arrays; @@ -16,7 +14,7 @@ public class ChallengersTest { @Test void canPurgeUserData() throws NoSuchFieldException, IllegalAccessException { - EntityRelModel erModel = new EntityRelModel( new ERSchema(), new ERInstanceData()); + EntityRelModel erModel = new EntityRelModel(); erModel.createInstanceDatabaseIfNotExisting("an-active-user"); int originalNumberOfDatabases = erModel.getDatabaseNames().size(); diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java index a73738cc..fc4ce7cb 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java @@ -26,14 +26,8 @@ public void productionCodeDoesNotCallLegacySnapshotEscapesOutsideCompatibilityAd "getAllInstanceCollections(", "getInstanceCollectionForEntityNamed("), List.of( - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java", "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/ERInstanceData.java", - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java", - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java", - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java", - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java", - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java", - "thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java")); + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java")); Assertions.assertTrue( violations.isEmpty(), @@ -42,23 +36,28 @@ public void productionCodeDoesNotCallLegacySnapshotEscapesOutsideCompatibilityAd } @Test - public void thingifierAndChallengerProductionCodeDoesNotDependOnCompatibilityCollections() throws IOException { + public void thingifierAndChallengerProductionCodeDoesNotDependOnInstanceCollections() throws IOException { List violations = productionJavaLinesContaining( List.of("EntityInstanceCollection"), - List.of("thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java"), + List.of(), List.of("thingifier", "challenger")); Assertions.assertTrue( violations.isEmpty(), - "Thingifier and Challenger runtime code should not use compatibility collections outside the deprecated public Thingifier API:\n" + + "Thingifier and Challenger runtime code should use ThingRepository instead of EntityInstanceCollection:\n" + String.join("\n", violations)); } @Test - public void productionCodeDoesNotUseDeprecatedSimpleQueryOutsideItsOwnClass() throws IOException { + public void productionCodeDoesNotUseSimpleQuery() throws IOException { + Assertions.assertFalse( + Files.exists(repoRoot().resolve( + "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java")), + "SimpleQuery should be deleted; use RepositoryUrlQuery or repository APIs"); + List violations = productionJavaLinesContaining( List.of("SimpleQuery"), - List.of("ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java")); + List.of()); Assertions.assertTrue( violations.isEmpty(), diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java new file mode 100644 index 00000000..cd6c82ec --- /dev/null +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java @@ -0,0 +1,46 @@ +package uk.co.compendiumdev.challenge.testsupport; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +import java.util.Collection; +import java.util.List; + +public final class RepositoryBackedTestCollection { + + private final EntityDefinition definition; + private final ThingRepository repository; + + RepositoryBackedTestCollection( + final EntityDefinition definition, + final ThingRepository repository) { + this.definition = definition; + this.repository = repository; + } + + public EntityDefinition definition() { + return definition; + } + + public EntityInstance addInstance(final EntityInstance instance) { + return repository.addInstance(instance); + } + + public int countInstances() { + return repository.countInstances(definition); + } + + public Collection getInstances() { + return repository.listInstances(definition); + } + + public EntityInstance findInstanceByPrimaryKey(final String primaryKeyValue) { + return repository.findInstanceByPrimaryKey(definition, primaryKeyValue); + } + + public List deleteInstance(final EntityInstance instance) { + repository.deleteEntityInstance(instance); + return List.of(); + } +} diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java new file mode 100644 index 00000000..3e7089f2 --- /dev/null +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java @@ -0,0 +1,21 @@ +package uk.co.compendiumdev.challenge.testsupport; + +import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +public final class ThingifierRepositoryTestSupport { + + private ThingifierRepositoryTestSupport() { + } + + public static RepositoryBackedTestCollection collection( + final Thingifier thingifier, + final String databaseName, + final String entityName) { + EntityDefinition definition = + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); + ThingRepository repository = thingifier.getRepository(databaseName); + return new RepositoryBackedTestCollection(definition, repository); + } +} diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java index dd35c28e..b9b7c8fa 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java @@ -13,7 +13,8 @@ import uk.co.compendiumdev.challenger.payloads.Todo; import uk.co.compendiumdev.challenger.payloads.Todos; import uk.co.compendiumdev.sparkstart.Environment; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.challenge.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.challenge.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import java.util.ArrayList; @@ -240,7 +241,7 @@ public void canPutTodos400FailCreatePass() { @Test public void canPutTodosFull200AmendPass() { - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -266,7 +267,7 @@ public void canPutTodosFull200AmendPass() { @Test public void canPutTodosPartial200AmendPass() { - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -290,7 +291,7 @@ public void canPutTodosPartial200AmendPass() { @Test public void canPutTodos200MissingTitleAmendPass() { - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -314,7 +315,7 @@ public void canPutTodos200MissingTitleAmendPass() { @Test public void canNotPutTodos400ChangeIdAmendPass() { - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -486,7 +487,7 @@ public void canPostTodosUpdatePass() { ensureAtMostXTodoAvailable(10); - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); final EntityInstance todo = todos.addInstance( new EntityInstance(todos.definition())); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -620,7 +621,7 @@ public void canPostToAddMaxTodosPass() { } public void ensureAtMostXTodoAvailable(int x){ - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); if(todos.countInstances()>x){ for(int delCount = todos.countInstances()-x; delCount > 0; delCount--) { todos.deleteInstance((EntityInstance) (todos.getInstances().toArray()[0])); @@ -633,7 +634,7 @@ public void canDeleteTodosPass() { ensureAtMostXTodoAvailable(10); - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); final EntityInstance todo = todos.addInstance( new EntityInstance(todos.definition())); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -652,7 +653,7 @@ public void canDeleteTodosPass() { @Test public void canGetSpecificTodoPass() { - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); final EntityInstance todo = todos.addInstance(new EntityInstance(todos.definition())); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -767,7 +768,7 @@ public void canFilterTodoPass() { Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); todos.addInstance(new EntityInstance(todos.definition())).setValue("doneStatus", "true"); todos.addInstance(new EntityInstance(todos.definition())).setValue("doneStatus", "false"); @@ -1107,7 +1108,7 @@ public void canDeleteAllTodos() { Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); - final EntityInstanceCollection todos = ChallengeMain.getChallenger().getThingifier().getThingInstancesNamed("todo", challenger.getXChallenger()); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); for(EntityInstance instance : todos.getInstances()){ final HttpResponseDetails response = diff --git a/checkstyle-project-fqn.xml b/checkstyle-project-fqn.xml new file mode 100644 index 00000000..1cfd3da7 --- /dev/null +++ b/checkstyle-project-fqn.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java index 69db330d..c74ab668 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java @@ -1,14 +1,10 @@ package uk.co.compendiumdev.thingifier.core; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.LegacyDataPopulatorAdapter; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.Cardinality; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; @@ -27,7 +23,7 @@ public class EntityRelModel implements AutoCloseable { // a provider so that key, database can be backed by memory, files, SQLite, etc. private final ERSchema schema; // all the definitions private final ThingRepositoryProvider repositories; - private DataPopulator dataPopulator; + private RepositoryDataPopulator dataPopulator; public EntityRelModel(){ schema = new ERSchema(); @@ -35,12 +31,6 @@ public EntityRelModel(){ dataPopulator = null; } - public EntityRelModel(final ERSchema schema, final ERInstanceData erInstanceData) { - this.schema = schema; - this.repositories = new InMemoryThingRepositoryProvider(erInstanceData); - dataPopulator = null; - } - public EntityRelModel(final ThingRepositoryProvider repositories) { this.schema = new ERSchema(); this.repositories = repositories; @@ -69,37 +59,6 @@ public ERSchema getSchema(){ return schema; } - /** - * @deprecated Compatibility snapshot access. Use {@link #getRepository(String)} - * and repository-native reads/writes instead. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public ERInstanceData getInstanceData(){ - return getInstanceData(DEFAULT_DATABASE_NAME); - } - - /** - * @deprecated This returns compatibility instance data, not JSON. Use - * {@link #exportInstanceDataAsJson(String)} for JSON export. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public ERInstanceData getInstanceDataAsJson(){ - return getInstanceData(DEFAULT_DATABASE_NAME); - } - - /** - * @deprecated Compatibility snapshot access. Use {@link #getRepository(String)} - * and repository-native reads/writes instead. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public ERInstanceData getInstanceData(String databaseKey) { - ThingRepository repository = repositories.getRepository(databaseKey); - if(repository==null){ - return null; - } - return repository.getInstanceData(); - } - public String exportInstanceDataAsJson(final String databaseKey) { ThingRepository repository = repositories.getRepository(databaseKey); if (repository == null) { @@ -133,16 +92,6 @@ public void close() { repositories.close(); } - // ERM Object Level - /** - * @deprecated Compatibility helper backed by legacy instance data. Prefer a - * repository/provider configured with the desired data source. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public EntityRelModel cloneWithDifferentData(final List instances) { - return new EntityRelModel(schema, new ERInstanceData(instances)); - } - // Schema methods // TODO: consider inlining all of these public boolean hasEntityNamed(final String aName) { @@ -223,7 +172,7 @@ public boolean populateDatabase(String databaseKey){ return true; } - public void setDataGenerator(DataPopulator dataPopulator) { + public void setDataGenerator(RepositoryDataPopulator dataPopulator) { this.dataPopulator = dataPopulator; } @@ -234,11 +183,6 @@ private void refreshRepositorySchemas() { } private void populateRepository(final ThingRepository repository) { - if(dataPopulator instanceof RepositoryDataPopulator) { - ((RepositoryDataPopulator) dataPopulator).populate(getSchema(), repository); - return; - } - - LegacyDataPopulatorAdapter.populate(dataPopulator, getSchema(), repository); + dataPopulator.populate(getSchema(), repository); } } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java deleted file mode 100644 index a6710e01..00000000 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/DataPopulator.java +++ /dev/null @@ -1,14 +0,0 @@ -package uk.co.compendiumdev.thingifier.core.domain.datapopulator; - -import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; - -public interface DataPopulator { - - /** - * @deprecated Implement {@link RepositoryDataPopulator} so population writes - * through the configured repository without hydrating a compatibility snapshot. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - void populate(ERSchema schema, ERInstanceData database); -} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java deleted file mode 100644 index 20edb97e..00000000 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/LegacyDataPopulatorAdapter.java +++ /dev/null @@ -1,19 +0,0 @@ -package uk.co.compendiumdev.thingifier.core.domain.datapopulator; - -import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; - -public final class LegacyDataPopulatorAdapter { - - private LegacyDataPopulatorAdapter() { - } - - @Deprecated(forRemoval = true, since = "1.5.6") - public static void populate( - final DataPopulator dataPopulator, - final ERSchema schema, - final ThingRepository repository) { - dataPopulator.populate(schema, repository.getInstanceData()); - repository.flush(); - } -} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java index 9323eeae..7aaa8588 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/datapopulator/RepositoryDataPopulator.java @@ -1,22 +1,9 @@ package uk.co.compendiumdev.thingifier.core.domain.datapopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; -public interface RepositoryDataPopulator extends DataPopulator { - - /** - * @deprecated Compatibility bridge for older callers that still provide - * ERInstanceData directly. Runtime population should call - * {@link #populate(ERSchema, ThingRepository)}. - */ - @Override - @Deprecated(forRemoval = true, since = "1.5.6") - default void populate(final ERSchema schema, final ERInstanceData database) { - populate(schema, new InMemoryThingRepository("__repository-populator-compatibility", database)); - } +public interface RepositoryDataPopulator { void populate(ERSchema schema, ThingRepository repository); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/EntityDefinition.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/EntityDefinition.java index 31558d2c..72134866 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/EntityDefinition.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/EntityDefinition.java @@ -23,7 +23,7 @@ public class EntityDefinition { private static final int NO_INSTANCE_LIMIT = -1; public EntityDefinition(String name, String plural) { - this(name,plural,EntityDefinition.NO_INSTANCE_LIMIT); + this(name, plural, NO_INSTANCE_LIMIT); } public EntityDefinition(String name, String plural, int maxInstanceCount) { @@ -125,7 +125,7 @@ public boolean hasMaxInstanceLimit() { } public void setNoMaxInstanceLimit() { - maxInstanceCount = EntityDefinition.NO_INSTANCE_LIMIT; + maxInstanceCount = NO_INSTANCE_LIMIT; } public boolean hasPrimaryKeyField() { diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/field/instance/FieldValue.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/field/instance/FieldValue.java index 88cd7b93..9855c847 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/field/instance/FieldValue.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/field/instance/FieldValue.java @@ -66,9 +66,9 @@ public String getName() { public FieldValue cloned() { if(objectValue!=null){ - return FieldValue.is(forField, objectValue.cloned()); + return is(forField, objectValue.cloned()); }else{ - return FieldValue.is(forField, valueOfField); + return is(forField, valueOfField); } } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/relationship/Optionality.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/relationship/Optionality.java index 033aac4c..ff88c9bd 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/relationship/Optionality.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/relationship/Optionality.java @@ -8,11 +8,11 @@ public enum Optionality { public static Optionality from(final String textRepresentation) { if(textRepresentation==null){ - return Optionality.OPTIONAL_RELATIONSHIP; + return OPTIONAL_RELATIONSHIP; } return textRepresentation.equalsIgnoreCase("M") ? - Optionality.MANDATORY_RELATIONSHIP : - Optionality.OPTIONAL_RELATIONSHIP; + MANDATORY_RELATIONSHIP : + OPTIONAL_RELATIONSHIP; } } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/validation/TypeValidationFailedMessageGenerator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/validation/TypeValidationFailedMessageGenerator.java index 811ebfd4..ff69d4dd 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/validation/TypeValidationFailedMessageGenerator.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/definitions/validation/TypeValidationFailedMessageGenerator.java @@ -8,7 +8,7 @@ public class TypeValidationFailedMessageGenerator { public static String thisValueDoesNotMatchType(final FieldValue value, FieldType type) { - return TypeValidationFailedMessageGenerator.thisValueDoesNotMatchType(value, type, List.of()); + return thisValueDoesNotMatchType(value, type, List.of()); } public static String thisValueDoesNotMatchType( FieldValue value, FieldType type, diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java deleted file mode 100644 index b8d3b83a..00000000 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java +++ /dev/null @@ -1,335 +0,0 @@ -package uk.co.compendiumdev.thingifier.core.query; - -import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; -import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; -import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; -import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; - -import java.util.ArrayList; -import java.util.List; - -import static uk.co.compendiumdev.thingifier.core.query.SimpleQuery.LastMatchValue.*; - -/** - * Legacy ERInstanceData-backed query engine. - * - * Note this is not the same as a GET e.g. - * - /item will always mark the query as not a collection - * - /items will always mark the query as a collection - * - * This is a simple query to then build more complex or specific query - * processing on top. - * - * Use the isResultACollection, wasIntentToMatchACollection, lastMatchWasInstance - * in the calling method. - * - * @deprecated Runtime API paths should use repository-backed query resolution so - * repositories such as SQLite can query without hydrating compatibility snapshots. - * Keep this class only for compatibility/fallback paths until replacement is complete. - */ -@Deprecated(forRemoval = true, since = "1.5.6") -public final class SimpleQuery implements UrlQueryResult { - - private final ERInstanceData database; - private final ERSchema schema; // all the definitions - - private final String query; - - private boolean isCollection = false; - private boolean pluralMatch = false; - private boolean wasIntentToMatchInstance = false; - - - enum LastMatchValue {NOTHING, CURRENT_THING, CURRENT_INSTANCE, CURRENT_ITEMS, CURRENT_RELATIONSHIP} - - LastMatchValue lastMatch = NOTHING; - - // populated during search - EntityInstanceCollection currentCollection = null; - EntityInstance currentInstance = null; - List foundItems = new ArrayList<>(); - RelationshipVectorDefinition lastRelationshipFound = null; - List lastRelationshipsFound = null; - EntityInstanceCollection parentCollection = null; - private EntityInstance parentInstance = null; - - // TODO: this should be an object with FoundItem objects which have getAsRelationshipDefinition etc. - List foundItemsHistoryList = new ArrayList<>(); - private EntityDefinition resultContainsDefinition; - - public SimpleQuery(ERSchema aSchema, ERInstanceData aDatabase, String query) { - - this.schema = aSchema; - this.database = aDatabase; - - if(query.startsWith("/")){ - this.query = query.substring(1); - }else{ - this.query = query; - } - } - - - public SimpleQuery performQuery() { - // a simple query is a URL based REST query - // e.g. THING/_GUID_/RELATIONSHIP/THING - // e.g. THING/_ID_/RELATIONSHIP/THING - // THING/RELATIONSHIP - - String[] terms = query.split("/"); - - - lastMatch = NOTHING; - - for (String term : terms) { - - // if we have a parent thing then we want to check for relationships before we check for things - // if it matches a relationship then get the instances identified by the relationship - //if(currentThing != null && currentThing.definition().hasRelationship(term)){ - if (parentCollection !=null && schema.hasRelationshipNamed(term)) { - - // what I want to store is the relationship between the parent Thing and the relationship name - EntityInstanceCollection thingToCheckForRelationship = currentCollection == null ? parentCollection : currentCollection; - lastRelationshipsFound = thingToCheckForRelationship.definition().related().getRelationships(term); - lastRelationshipFound = lastRelationshipsFound.get(0); - - foundItemsHistoryList.add(lastRelationshipFound); - - - if (foundItems != null && !foundItems.isEmpty()) { - resultContainsDefinition = foundItems.get(0).getRelationships().getTypeOfConnectableItems(term); - } - - List newitems = new ArrayList<>(); - if (foundItems != null) { - for (EntityInstance instance : foundItems) { - newitems.addAll(instance.getRelationships().getConnectedItems(term)); - } - } - - // relationships is always a collection - isCollection = true; - - foundItems = newitems; - parentInstance = currentInstance; - parentCollection = currentCollection; - currentCollection = null; - currentInstance = null; - lastMatch = CURRENT_RELATIONSHIP; - continue; - } - - // if matches an entity type - if (schema.hasEntityNamed(term) || schema.hasEntityWithPluralNamed(term)) { - if (currentCollection == null && foundItems.isEmpty()) { - // first thing - find it - currentCollection = database.getInstanceCollectionForEntityNamed(term); - pluralMatch = false; - - if (currentCollection == null) { - // was it the plural? - final EntityDefinition defn = schema.getEntityDefinitionWithPluralNamed(term); - currentCollection = database.getInstanceCollectionForEntityNamed(defn.getName()); - pluralMatch = true; - } - - // entity type is always a collection - isCollection = true; - - resultContainsDefinition = currentCollection.definition(); - foundItemsHistoryList.add(currentCollection); - parentCollection = currentCollection; - currentInstance = null; - lastMatch = CURRENT_THING; - foundItems = new ArrayList<>(currentCollection.getInstances()); - - } else { - // related to another type of thing - foundItemsHistoryList.add(database.getInstanceCollectionForEntityNamed(term)); - - if (foundItems != null && !foundItems.isEmpty()) { - resultContainsDefinition = foundItems.get(0).getRelationships().getTypeOfConnectableItems(term); - } - - List newitems = new ArrayList<>(); - if (foundItems != null) { - for (EntityInstance instance : foundItems) { - List matchedInstances = instance.getRelationships().getConnectedItemsOfType(term); - newitems.addAll(matchedInstances); - } - } - - // relationship is a collection - foundItems = newitems; - lastMatch = CURRENT_ITEMS; - parentCollection = currentCollection; - currentCollection = null; - currentInstance = null; - } - continue; - } - - - - // is it a GUID or ID? - // this should be based on the EntityDefinition Primary Identifier Field - // TODO: create a PrimaryIdentifierField to allow finding via simple query - boolean found = false; - for (EntityInstance instance : foundItems) { - - boolean matchBasedOnIdOrGUID = false; - - // found based on ID ? - final List idFields = instance.getEntity(). - getFieldsOfType(FieldType.AUTO_INCREMENT); - if(!idFields.isEmpty()){ - final String idValue = instance.getFieldValue( - idFields.get(0).getName()).asString(); - if(idValue.contentEquals(term)){ - matchBasedOnIdOrGUID=true; - } - } - - if (instance.getPrimaryKeyValue().contentEquals(term)) { - matchBasedOnIdOrGUID = true; - } - - if(matchBasedOnIdOrGUID){ - - foundItemsHistoryList.add(instance); - - if (currentCollection != null) { - parentCollection = currentCollection; - } - - // because we matched based on primary key - wasIntentToMatchInstance = true; - isCollection = pluralMatch; - - currentCollection = null; - - currentInstance = instance; - foundItems = new ArrayList<>(); - foundItems.add(instance); - lastMatch = CURRENT_INSTANCE; - found = true; - } - if (found) { - break; - } - } - if (found) { - // it was a GUID or id - continue; - } - - // is it a field? - // is it a filter query? e.g. ?title="name" - lastMatch = NOTHING; - } - - return this; - } - - // i.e. did the query end with an identifier which was a primary key - @Override - public boolean wasQueryIntendedToMatchAnInstance(){ - return wasIntentToMatchInstance; - } - - @Override - public boolean isResultACollection() { - return isCollection; - } - - // Should not use map from query processing, instead parse to filters using our parser UrlParamParser - - public SimpleQuery performQuery(final QueryFilterParams queryParams) { - - performQuery(); - //filter the results based on the query - // todo: should we filter single instances? - if(!isCollection){ - return this; - } - - final EntityInstanceListFilter filterer = new EntityInstanceListFilter(queryParams); - - foundItems = filterer.filter(foundItems); - - // support sorting after filtering - final EntityInstanceListSorter sorter = new EntityInstanceListSorter(queryParams); - foundItems = sorter.sort(foundItems); - - return this; - } - - @Override - public List getListEntityInstances() { - List returnThis = new ArrayList<>(); - - if (lastMatch == CURRENT_THING) { - // if not allow filtering then... - //returnThis.addAll(currentThing.getInstances()); - // if allow filtering then... - returnThis.addAll(foundItems); - } - - if (lastMatch == CURRENT_INSTANCE) { - returnThis.add(currentInstance); - } - - if (lastMatch == CURRENT_ITEMS || lastMatch == CURRENT_RELATIONSHIP) { - returnThis.addAll(foundItems); - } - - //if(lastMatch==NOTHING){ // then the array is already empty} - - return returnThis; - } - - public boolean lastMatchWasRelationship() { - return lastMatch == CURRENT_RELATIONSHIP; - } - - public String getLastRelationshipName() { - return lastRelationshipFound.getName(); - } - - public EntityInstance getParentInstance() { - return parentInstance; - } - - public boolean wasItemFoundUnderARelationship() { - - // not enough in the history list to do the check, so no it wasn't - if (foundItemsHistoryList.size() - 2 < 0) { - return false; - } - return foundItemsHistoryList.get(foundItemsHistoryList.size() - 2) instanceof RelationshipVectorDefinition; - } - - @Override - public EntityInstance getLastInstance() { - return currentInstance; - } - - @Override - public boolean lastMatchWasInstance() { - return lastMatch == CURRENT_INSTANCE; - } - - @Override - public boolean lastMatchWasNothing() { - return lastMatch == NOTHING; - } - - @Override - public EntityDefinition resultContainsDefn() { - return resultContainsDefinition; - } -} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java index b30b3b79..2cc1a7e4 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java @@ -28,7 +28,7 @@ public InMemoryThingRepository(final String databaseKey) { this(databaseKey, new ERInstanceData()); } - public InMemoryThingRepository(final String databaseKey, final ERInstanceData instanceData) { + InMemoryThingRepository(final String databaseKey, final ERInstanceData instanceData) { this.databaseKey = databaseKey; this.instanceData = instanceData; } @@ -48,13 +48,7 @@ public void refreshSchema(final ERSchema schema) { createInstanceCollectionsFrom(schema); } - @Override - public ERInstanceData getInstanceData() { - return instanceData; - } - - @Override - public EntityInstanceCollection createInstanceCollectionFor(final EntityDefinition definition) { + private EntityInstanceCollection createInstanceCollectionFor(final EntityDefinition definition) { EntityInstanceCollection existing = getInstanceCollectionForEntityNamed(definition.getName()); if (existing != null) { return existing; @@ -62,20 +56,13 @@ public EntityInstanceCollection createInstanceCollectionFor(final EntityDefiniti return instanceData.createInstanceCollectionFor(definition); } - @Override - public void createInstanceCollectionsFrom(final ERSchema schema) { + private void createInstanceCollectionsFrom(final ERSchema schema) { for (EntityDefinition defn : schema.getEntityDefinitions()) { createInstanceCollectionFor(defn); } } - @Override - public List getAllInstanceCollections() { - return instanceData.getAllInstanceCollections(); - } - - @Override - public EntityInstanceCollection getInstanceCollectionForEntityNamed(final String entityName) { + private EntityInstanceCollection getInstanceCollectionForEntityNamed(final String entityName) { return instanceData.getInstanceCollectionForEntityNamed(entityName); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java index a44e07c9..75d19c7a 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java @@ -2,7 +2,6 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import java.util.HashMap; import java.util.HashSet; @@ -17,10 +16,6 @@ public InMemoryThingRepositoryProvider() { this(new InMemoryThingRepository(EntityRelModel.DEFAULT_DATABASE_NAME)); } - public InMemoryThingRepositoryProvider(final ERInstanceData defaultInstanceData) { - this(new InMemoryThingRepository(EntityRelModel.DEFAULT_DATABASE_NAME, defaultInstanceData)); - } - public InMemoryThingRepositoryProvider(final ThingRepository defaultRepository) { repositories = new HashMap<>(); repositories.put(EntityRelModel.DEFAULT_DATABASE_NAME, defaultRepository); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java index 36a74074..795f86d7 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java @@ -6,13 +6,11 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; -import uk.co.compendiumdev.thingifier.core.domain.instances.RelationshipVectorInstance; import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListSorter; import uk.co.compendiumdev.thingifier.core.query.EntityListSortParamParser; import uk.co.compendiumdev.thingifier.core.query.FilterBy; @@ -40,22 +38,26 @@ import java.util.logging.Logger; import java.util.regex.Pattern; -public class SqliteThingRepository extends InMemoryThingRepository { +public class SqliteThingRepository implements ThingRepository { private static final Logger LOGGER = Logger.getLogger(SqliteThingRepository.class.getName()); private static final String INTERNAL_ID_COLUMN = "__internal_id"; private static final String COUNTERS_TABLE = "__thingifier_counters"; + private final String databaseKey; private final String jdbcUrl; + private final Map materializedInstances; + private final Map> countersByEntity; private ERSchema schema; private Connection connection; - private boolean legacySnapshotLoaded; private boolean closed; public SqliteThingRepository(final String databaseKey, final String jdbcUrl) { - super(databaseKey, new ERInstanceData()); + this.databaseKey = databaseKey; this.jdbcUrl = jdbcUrl; + this.materializedInstances = new HashMap<>(); + this.countersByEntity = new HashMap<>(); } public static SqliteThingRepository inMemory(final String databaseKey) { @@ -67,6 +69,11 @@ public static SqliteThingRepository fileBacked(final String databaseKey, final P return new SqliteThingRepository(databaseKey, "jdbc:sqlite:" + path); } + @Override + public String databaseKey() { + return databaseKey; + } + @Override public void initializeFrom(final ERSchema schema) { this.schema = schema; @@ -75,20 +82,6 @@ public void initializeFrom(final ERSchema schema) { refreshSchema(schema); } - @Override - public ERInstanceData getInstanceData() { - if (!legacySnapshotLoaded) { - loadExistingData(); - legacySnapshotLoaded = true; - } - return super.getInstanceData(); - } - - @Override - public boolean hasLoadedCompatibilitySnapshot() { - return legacySnapshotLoaded; - } - @Override public String exportDataAsJson(final ERSchema schema) { ensureSchemaReady(); @@ -116,24 +109,16 @@ public String exportDataAsJson(final ERSchema schema) { public void refreshSchema(final ERSchema schema) { this.schema = schema; openConnection(); - super.refreshSchema(schema); for (EntityDefinition entity : schema.getEntityDefinitions()) { + initializeCountersFor(entity); createEntityTable(entity); + restoreCountersFor(entity); } for (RelationshipVectorDefinition vector : relationshipVectors()) { createRelationshipTable(vector); } } - @Override - public EntityInstanceCollection createInstanceCollectionFor(final EntityDefinition definition) { - EntityInstanceCollection collection = super.createInstanceCollectionFor(definition); - if (connection != null) { - createEntityTable(definition); - } - return collection; - } - @Override public EntityInstance findEntityInstanceByGUID(final String thingGUID) { ensureSchemaReady(); @@ -243,8 +228,9 @@ public EntityInstance findInstanceByQueryIdentifier( public EntityInstance addInstance(final EntityInstance instance) { ensureSchemaReady(); runInTransaction(() -> { - super.addInstance(instance); + prepareInstanceForInsert(instance); persistInstance(instance); + materializedInstances.put(instance.getInternalId(), instance); persistCountersFor(instance.getEntity()); }); return instance; @@ -255,6 +241,7 @@ public EntityInstance updateInstance(final EntityInstance instance) { ensureSchemaReady(); runInTransaction(() -> { persistInstance(instance); + materializedInstances.put(instance.getInternalId(), instance); persistCountersFor(instance.getEntity()); }); return instance; @@ -263,52 +250,38 @@ public EntityInstance updateInstance(final EntityInstance instance) { @Override public void deleteEntityInstance(final EntityInstance instance) { ensureSchemaReady(); - if (!legacySnapshotLoaded) { - runInTransaction(() -> { - super.deleteEntityInstance(instance); - deletePersistedInstance(instance.getInternalId()); - }); - return; - } - - Set idsBeforeDelete = currentInternalIds(); - super.deleteEntityInstance(instance); - Set idsAfterDelete = currentInternalIds(); - - runInTransaction(() -> { - idsBeforeDelete.removeAll(idsAfterDelete); - for (String internalId : idsBeforeDelete) { - deletePersistedInstance(internalId); - } - replaceAllRelationshipRowsFromCache(); - }); + runInTransaction(() -> deleteEntityInstanceAndMandatoryRelated(instance, new HashSet<>())); } @Override public void clearAllData() { ensureSchemaReady(); - super.clearAllData(); runInTransaction(() -> { for (EntityDefinition entity : schema.getEntityDefinitions()) { executeSql("DELETE FROM " + table(entity)); + resetCountersFor(entity); persistCountersFor(entity); } clearRelationshipRows(); + materializedInstances.clear(); }); } @Override public void clearInstanceDataFor(final String entityName) { ensureSchemaReady(); - EntityInstanceCollection collection = getInstanceCollectionForEntityNamed(entityName); - if (collection == null) { + EntityDefinition entity = schema.getEntityDefinitionNamed(entityName); + if (entity == null) { return; } - super.clearInstanceDataFor(entityName); runInTransaction(() -> { - executeSql("DELETE FROM " + table(collection.definition())); - persistCountersFor(collection.definition()); - replaceAllRelationshipRowsFromCache(); + Set internalIds = internalIdsFor(entity); + executeSql("DELETE FROM " + table(entity)); + deleteRelationshipRowsInvolving(internalIds); + resetCountersFor(entity); + persistCountersFor(entity); + materializedInstances.entrySet(). + removeIf(entry -> entry.getValue().getEntity() == entity); }); } @@ -316,7 +289,7 @@ public void clearInstanceDataFor(final String entityName) { public void connectRelationship( final EntityInstance from, final String relationshipName, final EntityInstance to) { ensureSchemaReady(); - super.connectRelationship(from, relationshipName, to); + from.getRelationships().connect(relationshipName, to); runInTransaction(() -> persistRelationship(from, relationshipName, to)); } @@ -326,7 +299,9 @@ public List removeRelationshipsInvolving( final EntityInstance child, final String relationshipName) { ensureSchemaReady(); - List removed = super.removeRelationshipsInvolving(parent, child, relationshipName); + materializeRelationshipsInvolving(parent, child, relationshipName); + List removed = parent.getRelationships(). + removeRelationshipsInvolving(child, relationshipName); runInTransaction(() -> deleteRelationshipRowsInvolving(parent, child, relationshipName)); return removed; } @@ -334,7 +309,8 @@ public List removeRelationshipsInvolving( @Override public List removeAllRelationships(final EntityInstance instance) { ensureSchemaReady(); - List removed = super.removeAllRelationships(instance); + materializeRelationshipsFor(instance); + List removed = instance.getRelationships().removeAllRelationships(); runInTransaction(() -> deleteRelationshipRowsInvolving(instance)); return removed; } @@ -357,9 +333,6 @@ public Collection getConnectedItems( } } - if (connected.isEmpty() && legacySnapshotLoaded) { - return super.getConnectedItems(instance, relationshipName); - } return connected; } @@ -388,33 +361,73 @@ public List listRelatedInstances( } @Override - public void flush() { - if (!legacySnapshotLoaded) { - return; - } - refreshSchema(schema); - runInTransaction(() -> { - for (EntityDefinition entity : schema.getEntityDefinitions()) { - executeSql("DELETE FROM " + table(entity)); + public ValidationReport checkFieldsForUniqueNess( + final EntityInstance instance, final boolean isAmendment) { + ensureSchemaReady(); + + ValidationReport report = new ValidationReport(); + for (String fieldName : instance.getEntity().getFieldNames()) { + Field field = instance.getEntity().getField(fieldName); + if (!field.mustBeUnique()) { + continue; } - clearRelationshipRows(); - for (EntityInstanceCollection collection : getAllInstanceCollections()) { - for (EntityInstance instance : collection.getInstances()) { - persistInstance(instance); + FieldValue value = instance.getFieldValue(fieldName); + if (value == null) { + continue; + } + + String sql = "SELECT * FROM " + table(instance.getEntity()) + + " WHERE " + identifier(fieldName) + " = ?"; + List matching = queryEntityRows( + instance.getEntity(), + sql, + List.of(field.getActualValueToAdd(value))); + String uniqueValue = value.asUniqueComparisonString(); + for (EntityInstance existing : matching) { + if (isAmendment && + existing.getPrimaryKeyValue().equals(instance.getPrimaryKeyValue())) { + continue; + } + + FieldValue existingValue = existing.getFieldValue(fieldName); + if (existingValue != null && + uniqueValue.equals(existingValue.asUniqueComparisonString())) { + report.setValid(false); + report.addErrorMessage("Field %s Value is not unique".formatted(fieldName)); + return report; } - persistCountersFor(collection.definition()); } + } + return report; + } - replaceAllRelationshipRowsFromCache(); - }); + @Override + public Map countersFor(final EntityDefinition entity) { + initializeCountersFor(entity); + return countersByEntity.get(entity.getName()); + } + + @Override + public void setNextIdCountersToAccomodate( + final EntityDefinition entity, final List fieldValues) { + initializeCountersFor(entity); + for (NamedValue fieldNameValue : fieldValues) { + Field field = entity.getField(fieldNameValue.getName()); + if (field != null && field.getType() == FieldType.AUTO_INCREMENT) { + AutoIncrement counter = counterFor(entity, field); + counter.incrementToNextAbove(Integer.parseInt(fieldNameValue.value)); + } + } + ensureSchemaReady(); + persistCountersFor(entity); } @Override public void resetAutoIncrementCounter(final EntityDefinition entity, final String fieldName) { ensureSchemaReady(); runInTransaction(() -> { - super.resetAutoIncrementCounter(entity, fieldName); + resetAutoIncrementCounterInCache(entity, fieldName); persistCountersFor(entity); }); } @@ -433,6 +446,211 @@ public void close() { } } + private void prepareInstanceForInsert(final EntityInstance instance) { + EntityDefinition entity = instance.getEntity(); + initializeCountersFor(entity); + + if (entity.hasMaxInstanceLimit() && countInstances(entity) >= entity.getMaxInstanceLimit()) { + throw new RuntimeException( + String.format( + "ERROR: Cannot add instance, maximum limit of %d reached", + entity.getMaxInstanceLimit())); + } + + List explicitAutoIncrementFields = new ArrayList<>(); + for (Field field : entity.getFieldsOfType(FieldType.AUTO_GUID, FieldType.AUTO_INCREMENT)) { + if (!instance.hasInstantiatedFieldNamed(field.getName())) { + if (field.getType() == FieldType.AUTO_GUID) { + instance.setValue(field.getName(), UUID.randomUUID().toString()); + } + if (field.getType() == FieldType.AUTO_INCREMENT) { + instance.overrideValue( + field.getName(), + String.valueOf(counterFor(entity, field).getNextValueAndUpdate())); + } + } else if (field.getType() == FieldType.AUTO_INCREMENT) { + explicitAutoIncrementFields.add(field.getName()); + } + } + + if (entity.hasPrimaryKeyField()) { + Field primaryField = entity.getPrimaryKeyField(); + if (!instance.hasInstantiatedFieldNamed(primaryField.getName())) { + throw new RuntimeException( + String.format( + "ERROR: Cannot add instance, primary key field %s not set", + primaryField.getName())); + } + + EntityInstance existing = findInstanceByPrimaryKey(entity, instance.getPrimaryKeyValue()); + if (existing != null && !existing.getInternalId().equals(instance.getInternalId())) { + throw new RuntimeException( + "ERROR: Cannot add instance, another instance with primary key value exists: " + + existing.getPrimaryKeyValue()); + } + } + + for (String fieldName : explicitAutoIncrementFields) { + AutoIncrement counter = countersFor(entity).get(fieldName); + int value = instance.getFieldValue(fieldName).asInteger(); + if (counter.getCurrentValue() < value) { + counter.incrementToNextAbove(value); + } + } + } + + private void initializeCountersFor(final EntityDefinition entity) { + Map counters = + countersByEntity.computeIfAbsent(entity.getName(), ignored -> new HashMap<>()); + for (Field field : entity.getFieldsOfType(FieldType.AUTO_INCREMENT)) { + counters.computeIfAbsent( + field.getName(), + ignored -> new AutoIncrement(field.getName(), field.getDefaultValue().asInteger())); + } + } + + private AutoIncrement counterFor(final EntityDefinition entity, final Field field) { + initializeCountersFor(entity); + return countersByEntity.get(entity.getName()). + computeIfAbsent( + field.getName(), + ignored -> new AutoIncrement(field.getName(), field.getDefaultValue().asInteger())); + } + + private void resetCountersFor(final EntityDefinition entity) { + countersByEntity.remove(entity.getName()); + initializeCountersFor(entity); + } + + private void resetAutoIncrementCounterInCache( + final EntityDefinition entity, final String fieldName) { + Field field = entity.getField(fieldName); + if (field == null || field.getType() != FieldType.AUTO_INCREMENT) { + throw new IllegalArgumentException( + String.format("%s is not an auto-increment field on %s", fieldName, entity.getName())); + } + + AutoIncrement counter = counterFor(entity, field); + counter.incrementToNextAbove(field.getDefaultValue().asInteger() - 1); + } + + private void deleteEntityInstanceAndMandatoryRelated( + final EntityInstance instance, final Set alreadyDeleting) { + if (!alreadyDeleting.add(instance.getInternalId())) { + return; + } + + materializeRelationshipsFor(instance); + List alsoDelete = + instance.getRelationships().removeAllRelationships(); + deletePersistedInstance(instance.getInternalId()); + materializedInstances.remove(instance.getInternalId()); + + for (EntityInstance deleteMe : alsoDelete) { + if (!deleteMe.getInternalId().equals(instance.getInternalId())) { + deleteEntityInstanceAndMandatoryRelated(deleteMe, alreadyDeleting); + } + } + } + + private void materializeRelationshipsInvolving( + final EntityInstance parent, + final EntityInstance child, + final String relationshipName) { + for (RelationshipVectorDefinition vector : relationshipVectors()) { + if (!vector.getRelationshipDefinition().isKnownAs(relationshipName)) { + continue; + } + if (!relationshipRowExists(parent, child, vector)) { + continue; + } + EntityInstance from = parent.getEntity() == vector.getFrom() ? parent : child; + EntityInstance to = from == parent ? child : parent; + if (!from.getRelationships().getConnectedItems(vector.getName()).contains(to)) { + from.getRelationships().connect(vector.getName(), to); + } + } + } + + private void materializeRelationshipsFor(final EntityInstance instance) { + for (RelationshipVectorDefinition vector : relationshipVectors()) { + String sql = "SELECT from_internal_id, to_internal_id FROM " + + relationshipTable(vector) + + " WHERE from_internal_id = ? OR to_internal_id = ?"; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setString(1, instance.getInternalId()); + statement.setString(2, instance.getInternalId()); + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + EntityInstance from = findInstanceByInternalId( + vector.getFrom(), + resultSet.getString("from_internal_id")); + EntityInstance to = findInstanceByInternalId( + vector.getTo(), + resultSet.getString("to_internal_id")); + if (from != null && to != null && + !from.getRelationships().getConnectedItems(vector.getName()).contains(to)) { + from.getRelationships().connect(vector.getName(), to); + } + } + } + } catch (SQLException e) { + throw new IllegalStateException("Could not materialize SQLite relationship rows", e); + } + } + } + + private boolean relationshipRowExists( + final EntityInstance parent, + final EntityInstance child, + final RelationshipVectorDefinition vector) { + String sql = "SELECT 1 FROM " + relationshipTable(vector) + + " WHERE (from_internal_id = ? AND to_internal_id = ?)" + + " OR (from_internal_id = ? AND to_internal_id = ?)" + + " LIMIT 1"; + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setString(1, parent.getInternalId()); + statement.setString(2, child.getInternalId()); + statement.setString(3, child.getInternalId()); + statement.setString(4, parent.getInternalId()); + try (ResultSet resultSet = statement.executeQuery()) { + return resultSet.next(); + } + } catch (SQLException e) { + throw new IllegalStateException("Could not query SQLite relationship row", e); + } + } + + private EntityInstance findInstanceByInternalId( + final EntityDefinition entity, final String internalId) { + if (internalId == null) { + return null; + } + EntityInstance existing = materializedInstances.get(internalId); + if (existing != null) { + return existing; + } + + String sql = "SELECT * FROM " + table(entity) + + " WHERE " + identifier(INTERNAL_ID_COLUMN) + " = ? LIMIT 1"; + List instances = queryEntityRows(entity, sql, List.of(internalId)); + return instances.isEmpty() ? null : instances.get(0); + } + + private Set internalIdsFor(final EntityDefinition entity) { + Set ids = new HashSet<>(); + String sql = "SELECT " + identifier(INTERNAL_ID_COLUMN) + " FROM " + table(entity); + try (Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(sql)) { + while (resultSet.next()) { + ids.add(resultSet.getString(INTERNAL_ID_COLUMN)); + } + } catch (SQLException e) { + throw new IllegalStateException("Could not query SQLite ids for " + entity.getName(), e); + } + return ids; + } + private void openConnection() { if (closed) { throw new IllegalStateException("SQLite repository has been closed"); @@ -539,68 +757,14 @@ private void createRelationshipIndex( " (" + identifier(columnName) + ")"); } - private void loadExistingData() { - for (EntityDefinition entity : schema.getEntityDefinitions()) { - loadEntityRows(entity); - restoreCountersFor(entity); - } - loadRelationshipRows(); - } - - private void loadEntityRows(final EntityDefinition entity) { - EntityInstanceCollection collection = getInstanceCollectionForEntityNamed(entity.getName()); - String sql = "SELECT * FROM " + table(entity); - try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(sql)) { - while (resultSet.next()) { - String internalId = resultSet.getString(INTERNAL_ID_COLUMN); - if (collection.findInstanceByInternalID(internalId) != null) { - continue; - } - - EntityInstance instance = new EntityInstance(entity, UUID.fromString(internalId)); - for (String fieldName : entity.getFieldNames()) { - String value = resultSet.getString(fieldName); - if (value != null) { - instance.overrideValue(fieldName, value); - } - } - collection.addInstance(instance); - } - } catch (SQLException e) { - throw new IllegalStateException("Could not load SQLite entity rows for " + entity.getName(), e); - } - } - - private void loadRelationshipRows() { - Map instances = instancesByInternalId(); - for (RelationshipVectorDefinition vector : relationshipVectors()) { - String sql = "SELECT from_internal_id, to_internal_id FROM " + relationshipTable(vector); - try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(sql)) { - while (resultSet.next()) { - EntityInstance from = instances.get(resultSet.getString("from_internal_id")); - EntityInstance to = instances.get(resultSet.getString("to_internal_id")); - if (from != null && to != null && - !from.getRelationships().getConnectedItems(vector.getName()).contains(to)) { - from.getRelationships().connect(vector.getName(), to); - } - } - } catch (SQLException e) { - throw new IllegalStateException("Could not load SQLite relationship rows", e); - } - } - } - private void restoreCountersFor(final EntityDefinition entity) { - EntityInstanceCollection collection = getInstanceCollectionForEntityNamed(entity.getName()); String sql = "SELECT field_name, next_value FROM " + identifier(COUNTERS_TABLE) + " WHERE entity_name = ?"; try (PreparedStatement statement = connection.prepareStatement(sql)) { statement.setString(1, entity.getName()); try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { - AutoIncrement counter = collection.getCounters().get(resultSet.getString("field_name")); + AutoIncrement counter = countersFor(entity).get(resultSet.getString("field_name")); if (counter != null) { int nextValue = resultSet.getInt("next_value"); if (counter.getCurrentValue() < nextValue) { @@ -845,22 +1009,29 @@ private void appendEntityRowAsJson( private EntityInstance instanceFromRow( final EntityDefinition entity, final ResultSet resultSet) throws SQLException { String internalId = resultSet.getString(INTERNAL_ID_COLUMN); - EntityInstanceCollection collection = getInstanceCollectionForEntityNamed(entity.getName()); - EntityInstance existing = collection.findInstanceByInternalID(internalId); + EntityInstance existing = materializedInstances.get(internalId); if (existing != null) { + hydrateInstanceFromRow(existing, resultSet); return existing; } EntityInstance instance = new EntityInstance( entity, UUID.fromString(internalId)); + hydrateInstanceFromRow(instance, resultSet); + materializedInstances.put(internalId, instance); + return instance; + } + + private void hydrateInstanceFromRow( + final EntityInstance instance, + final ResultSet resultSet) throws SQLException { + EntityDefinition entity = instance.getEntity(); for (String fieldName : entity.getFieldNames()) { String value = resultSet.getString(fieldName); if (value != null) { instance.overrideValue(fieldName, value); } } - collection.addInstance(instance); - return instance; } private String sqlLikeWildcard(final String wildcard) { @@ -1050,28 +1221,13 @@ private void deleteRelationshipRowsInvolving(final EntityInstance instance) { } } - private void replaceAllRelationshipRowsFromCache() { - clearRelationshipRows(); - Set persisted = new HashSet<>(); - - String sql = "INSERT OR IGNORE INTO %s (from_internal_id, to_internal_id) VALUES (?, ?)"; - for (EntityInstance instance : instancesByInternalId().values()) { - for (RelationshipVectorInstance relationship : instance.getRelationships().getRelationshipInstances()) { - RelationshipVectorDefinition vector = relationship.getDefinition(); - String key = vector.getName() + "|" + - relationship.getFrom().getInternalId() + "|" + - relationship.getTo().getInternalId(); - if (!persisted.add(key)) { - continue; - } - try (PreparedStatement statement = connection.prepareStatement( - String.format(sql, relationshipTable(vector)))) { - statement.setString(1, relationship.getFrom().getInternalId()); - statement.setString(2, relationship.getTo().getInternalId()); - statement.executeUpdate(); - } catch (SQLException e) { - throw new IllegalStateException("Could not persist SQLite relationship", e); - } + private void deleteRelationshipRowsInvolving(final Set internalIds) { + for (String internalId : internalIds) { + for (RelationshipVectorDefinition vector : relationshipVectors()) { + executePreparedSql( + "DELETE FROM " + relationshipTable(vector) + + " WHERE from_internal_id = ? OR to_internal_id = ?", + List.of(internalId, internalId)); } } } @@ -1082,20 +1238,6 @@ private void clearRelationshipRows() { } } - private Set currentInternalIds() { - return instancesByInternalId().keySet(); - } - - private Map instancesByInternalId() { - Map instances = new HashMap<>(); - for (EntityInstanceCollection collection : getAllInstanceCollections()) { - for (EntityInstance instance : collection.getInstances()) { - instances.put(instance.getInternalId(), instance); - } - } - return instances; - } - private List relationshipVectors() { List vectors = new ArrayList<>(); if (schema == null) { @@ -1150,9 +1292,12 @@ private void runInTransaction(final Runnable operation) { try { operation.run(); connection.commit(); - } catch (RuntimeException e) { + } catch (Exception e) { connection.rollback(); - throw e; + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new IllegalStateException("Could not run SQLite transaction", e); } finally { connection.setAutoCommit(originalAutoCommit); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java index 04e43845..1a28f6d9 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java @@ -4,9 +4,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.reporting.ERModelReport; import uk.co.compendiumdev.thingifier.core.reporting.RepositoryJsonExporter; @@ -24,38 +22,6 @@ public interface ThingRepository extends AutoCloseable { void refreshSchema(ERSchema schema); - /** - * @deprecated Compatibility snapshot access. Repository callers should use - * repository-native query and mutation methods instead. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - ERInstanceData getInstanceData(); - - /** - * @deprecated Compatibility collection creation. Repository implementations - * should initialize schema through {@link #initializeFrom(ERSchema)} or - * {@link #refreshSchema(ERSchema)}. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - EntityInstanceCollection createInstanceCollectionFor(EntityDefinition definition); - - void createInstanceCollectionsFrom(ERSchema schema); - - /** - * @deprecated Compatibility collection access. Use schema entity definitions - * with {@link #listInstances(EntityDefinition)} or reporting/export APIs. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - List getAllInstanceCollections(); - - /** - * @deprecated Compatibility collection access. Use repository-native methods - * such as {@link #listInstances(EntityDefinition)} and - * {@link #findInstanceByQueryIdentifier(EntityDefinition, String)}. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - EntityInstanceCollection getInstanceCollectionForEntityNamed(String entityName); - default String exportDataAsJson(final ERSchema schema) { return new RepositoryJsonExporter(schema, this).asJson(); } @@ -113,14 +79,6 @@ List removeRelationshipsInvolving( Collection getConnectedItems(EntityInstance instance, String relationshipName); - default boolean hasLoadedCompatibilitySnapshot() { - return true; - } - - default void flush() { - // Allows persistent repositories to sync legacy direct collection mutations. - } - @Override default void close() { // Most repository implementations do not own closeable resources. diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java index 0b9571f7..f147303b 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java @@ -2,7 +2,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.Cardinality; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; @@ -10,9 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.Optionality; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; @@ -21,22 +18,13 @@ public class EntityRelModelTest { - // Core needs a central class which 'manages' the Entities (Things) - // and the Relationships, and the data gen - // - this should be separate from the - // Thingifier (which also has the API and the app) - // the main class will be built by TDD, and refactoring in - // code from the Thingifier - @Test - public void canCreateAnEntityRelModel(){ - + public void canCreateAnEntityRelModel() { EntityRelModel erm = new EntityRelModel(); - Assertions.assertEquals(0, erm.getInstanceData().getAllInstanceCollections().size()); Assertions.assertFalse(erm.hasEntityNamed("bob")); - Assertions.assertNull(erm.getInstanceData().findEntityInstanceByGUID("bob")); - Assertions.assertNull(erm.getInstanceData().getInstanceCollectionForEntityNamed("bob")); + Assertions.assertNull(erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). + findEntityInstanceByGUID("bob")); Assertions.assertFalse(erm.hasEntityWithPluralNamed("bob")); Assertions.assertNull(erm.getSchema().getEntityDefinitionWithPluralNamed("bob")); Assertions.assertNull(erm.getSchema().getDefinitionWithSingularOrPluralNamed("bob")); @@ -45,42 +33,32 @@ public void canCreateAnEntityRelModel(){ @Test public void nothingHappensWhenTryToDeleteThingThatDoesNotExist() { - EntityRelModel erm = new EntityRelModel(); - final EntityInstance anEntityInstance = new EntityInstance( - new EntityDefinition("no", "nos")); - erm.getInstanceData().deleteEntityInstance(anEntityInstance); + EntityInstance missing = new EntityInstance(new EntityDefinition("no", "nos")); + + Assertions.assertDoesNotThrow(() -> + erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).deleteEntityInstance(missing)); } @Test public void canCreateAThingInAModel() { - EntityRelModel erm = new EntityRelModel(); - erm.createEntityDefinition("thing", "things"); - - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - - Assertions.assertEquals(1, erm.getInstanceData().getAllInstanceCollections().size()); - Assertions.assertTrue(erm.getInstanceData().getAllInstanceCollections().contains(thing)); + EntityDefinition thing = erm.createEntityDefinition("thing", "things"); Assertions.assertTrue(erm.hasEntityNamed("thing")); - - Assertions.assertNotNull(erm.getInstanceData().getInstanceCollectionForEntityNamed("thing")); - Assertions.assertEquals(thing, erm.getInstanceData().getInstanceCollectionForEntityNamed("thing")); Assertions.assertTrue(erm.hasEntityWithPluralNamed("things")); - Assertions.assertNotNull(erm.getSchema().getEntityDefinitionWithPluralNamed("things")); - Assertions.assertEquals(thing.definition(), erm.getSchema().getEntityDefinitionWithPluralNamed("things")); - Assertions.assertEquals(thing.definition(), erm.getSchema().getDefinitionWithSingularOrPluralNamed("thing")); - Assertions.assertEquals(thing.definition(), erm.getSchema().getDefinitionWithSingularOrPluralNamed("things")); + Assertions.assertEquals(thing, erm.getSchema().getEntityDefinitionWithPluralNamed("things")); + Assertions.assertEquals(thing, erm.getSchema().getDefinitionWithSingularOrPluralNamed("thing")); + Assertions.assertEquals(thing, erm.getSchema().getDefinitionWithSingularOrPluralNamed("things")); Assertions.assertEquals(1, erm.getEntityNames().size()); - Assertions.assertTrue(erm.getEntityNames().contains("thing")); + Assertions.assertEquals(0, + erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thing)); } @Test public void repositoryCanAddExplicitAutoIncrementValueWhenFieldAddedAfterCollectionCreated() { - EntityRelModel erm = new EntityRelModel(); EntityDefinition defn = erm.createEntityDefinition("challenge", "challenges"); defn.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); @@ -98,74 +76,58 @@ public void repositoryCanAddExplicitAutoIncrementValueWhenFieldAddedAfterCollect @Test public void canFindAThingInAModel() { - EntityRelModel erm = new EntityRelModel(); EntityDefinition defn = erm.createEntityDefinition("thing", "things"); defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); + ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityInstance instance = repository.addInstance(new EntityInstance(defn)); - final EntityInstance instance = thing.addInstance(new EntityInstance(thing.definition())); + String thingGuid = instance.getPrimaryKeyValue(); - final String thingGUID1 = instance.getPrimaryKeyValue(); - Assertions.assertNotNull( - erm.getInstanceData().findEntityInstanceByGUID(thingGUID1)); - final String thingGUID = instance.getPrimaryKeyValue(); - Assertions.assertEquals(instance, - erm.getInstanceData().findEntityInstanceByGUID(thingGUID)); + Assertions.assertEquals(instance, repository.findEntityInstanceByGUID(thingGuid)); } @Test public void canDeleteAThingInAModel() { - EntityRelModel erm = new EntityRelModel(); - erm.createEntityDefinition("thing", "things"); - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); + EntityDefinition defn = erm.createEntityDefinition("thing", "things"); + defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); - final EntityInstance instance = thing.addInstance(new EntityInstance(thing.definition())); - erm.getInstanceData().deleteEntityInstance(instance); + ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityInstance instance = repository.addInstance(new EntityInstance(defn)); + String thingGuid = instance.getPrimaryKeyValue(); - final String thingGUID = instance.getPrimaryKeyValue(); - Assertions.assertNull( - erm.getInstanceData().findEntityInstanceByGUID(thingGUID)); - final String entityName = instance.getEntity().getName(); - Assertions.assertEquals(0, - erm.getInstanceData().getInstanceCollectionForEntityNamed(entityName).countInstances()); + repository.deleteEntityInstance(instance); + + Assertions.assertNull(repository.findEntityInstanceByGUID(thingGuid)); + Assertions.assertEquals(0, repository.countInstances(defn)); } @Test public void canClearAllDataInAModel() { - EntityRelModel erm = new EntityRelModel(); - erm.createEntityDefinition("thing", "things"); - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - erm.createEntityDefinition("thing2", "thing2"); - EntityInstanceCollection thing2 = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing2"); - - thing.addInstance(new EntityInstance(thing.definition())); - thing.addInstance(new EntityInstance(thing.definition())); - thing2.addInstance(new EntityInstance(thing2.definition())); - thing2.addInstance(new EntityInstance(thing2.definition())); - thing2.addInstance(new EntityInstance(thing2.definition())); - - Assertions.assertEquals(2, erm.getInstanceData().getAllInstanceCollections().size()); - Assertions.assertEquals(2, - erm.getInstanceData().getInstanceCollectionForEntityNamed("thing").getInstances().size()); - Assertions.assertEquals(3, - erm.getInstanceData().getInstanceCollectionForEntityNamed("thing2").getInstances().size()); - - erm.getInstanceData().clearAllData(); - - Assertions.assertEquals(2, erm.getInstanceData().getAllInstanceCollections().size()); - Assertions.assertEquals(0, - erm.getInstanceData().getInstanceCollectionForEntityNamed("thing").getInstances().size()); - Assertions.assertEquals(0, - erm.getInstanceData().getInstanceCollectionForEntityNamed("thing2").getInstances().size()); + EntityDefinition thing = erm.createEntityDefinition("thing", "things"); + EntityDefinition thing2 = erm.createEntityDefinition("thing2", "thing2"); + ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + + repository.addInstance(new EntityInstance(thing)); + repository.addInstance(new EntityInstance(thing)); + repository.addInstance(new EntityInstance(thing2)); + repository.addInstance(new EntityInstance(thing2)); + repository.addInstance(new EntityInstance(thing2)); + + Assertions.assertEquals(2, repository.countInstances(thing)); + Assertions.assertEquals(3, repository.countInstances(thing2)); + + repository.clearAllData(); + + Assertions.assertEquals(0, repository.countInstances(thing)); + Assertions.assertEquals(0, repository.countInstances(thing2)); } @Test public void canCreateWithNoRelationships() { - EntityRelModel erm = new EntityRelModel(); Assertions.assertNotNull(erm.getRelationshipDefinitions()); @@ -175,30 +137,24 @@ public void canCreateWithNoRelationships() { @Test public void canCreateRelationships() { - EntityRelModel erm = new EntityRelModel(); - EntityDefinition t1d = erm.createEntityDefinition("thing1", "thing1"); - final EntityInstanceCollection thing1 = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing1"); - EntityDefinition t2d = erm.createEntityDefinition("thing2", "thing2"); - final EntityInstanceCollection thing2 = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing1"); - erm.createRelationshipDefinition(t1d, t2d, "things", Cardinality.ONE_TO_MANY()); + EntityDefinition thing1 = erm.createEntityDefinition("thing1", "thing1"); + EntityDefinition thing2 = erm.createEntityDefinition("thing2", "thing2"); + + erm.createRelationshipDefinition(thing1, thing2, "things", Cardinality.ONE_TO_MANY()); - Assertions.assertNotNull(erm.getRelationshipDefinitions()); Assertions.assertEquals(1, erm.getRelationshipDefinitions().size()); Assertions.assertTrue(erm.hasRelationshipNamed("things")); } @Test public void canFindAReversedRelationship() { - EntityRelModel erm = new EntityRelModel(); - final EntityDefinition td = erm.createEntityDefinition("thing", "things"); - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - final EntityDefinition dpd = erm.createEntityDefinition("dependantthing", "dthings"); - EntityInstanceCollection dependent = erm.getInstanceData().getInstanceCollectionForEntityNamed("dependantthing"); + EntityDefinition thing = erm.createEntityDefinition("thing", "things"); + EntityDefinition dependant = erm.createEntityDefinition("dependantthing", "dthings"); - erm.createRelationshipDefinition(td, dpd, "things", Cardinality.ONE_TO_MANY()) - .whenReversed(Cardinality.ONE_TO_ONE(), "idiewithoutyou"). + erm.createRelationshipDefinition(thing, dependant, "things", Cardinality.ONE_TO_MANY()). + whenReversed(Cardinality.ONE_TO_ONE(), "idiewithoutyou"). getReversedRelationship(). setOptionality(Optionality.MANDATORY_RELATIONSHIP); @@ -207,89 +163,70 @@ public void canFindAReversedRelationship() { @Test public void canDeleteAThingWithRelationships() { - EntityRelModel erm = new EntityRelModel(); - final EntityDefinition thingdefn = erm.createEntityDefinition("thing", "things"); - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - final EntityDefinition depdefn = erm.createEntityDefinition("dependantthing", "dthings"); - EntityInstanceCollection dependent = erm.getInstanceData().getInstanceCollectionForEntityNamed("dependantthing"); - - erm.createRelationshipDefinition(thingdefn, depdefn, "things", Cardinality.ONE_TO_MANY()) - .whenReversed(Cardinality.ONE_TO_ONE(),"idiewithoutyou"). - getReversedRelationship(). - setOptionality(Optionality.MANDATORY_RELATIONSHIP); - - final EntityInstance mainThing = thing.addInstance(new EntityInstance(thing.definition())); - mainThing.getRelationships().connect("things", - dependent.addInstance(new EntityInstance(dependent.definition()))); - mainThing.getRelationships().connect("things", - dependent.addInstance(new EntityInstance(dependent.definition()))); - mainThing.getRelationships().connect("things", - dependent.addInstance(new EntityInstance(dependent.definition()))); - - Assertions.assertEquals(2, erm.getInstanceData().getAllInstanceCollections().size()); - Assertions.assertEquals(3, mainThing.getRelationships(). - getConnectedItems("things").size()); - Assertions.assertEquals(3, erm.getInstanceData().getInstanceCollectionForEntityNamed("dependantthing"). - getInstances().size()); - - erm.getInstanceData().deleteEntityInstance(mainThing); - - final String thingGUID = mainThing.getPrimaryKeyValue(); - Assertions.assertNull( - erm.getInstanceData().findEntityInstanceByGUID(thingGUID)); - Assertions.assertEquals(0, - erm.getInstanceData().getInstanceCollectionForEntityNamed("thing").countInstances()); - Assertions.assertEquals(0, - erm.getInstanceData().getInstanceCollectionForEntityNamed("dependantthing").countInstances()); + EntityDefinition thing = erm.createEntityDefinition("thing", "things"); + thing.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + EntityDefinition dependant = erm.createEntityDefinition("dependantthing", "dthings"); + erm.createRelationshipDefinition(thing, dependant, "things", Cardinality.ONE_TO_MANY()). + whenReversed(Cardinality.ONE_TO_ONE(), "idiewithoutyou"). + getReversedRelationship(). + setOptionality(Optionality.MANDATORY_RELATIONSHIP); + + ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityInstance mainThing = repository.addInstance(new EntityInstance(thing)); + EntityInstance first = repository.addInstance(new EntityInstance(dependant)); + EntityInstance second = repository.addInstance(new EntityInstance(dependant)); + EntityInstance third = repository.addInstance(new EntityInstance(dependant)); + + repository.connectRelationship(mainThing, "things", first); + repository.connectRelationship(mainThing, "things", second); + repository.connectRelationship(mainThing, "things", third); + + Assertions.assertEquals(3, repository.getConnectedItems(mainThing, "things").size()); + Assertions.assertEquals(3, repository.countInstances(dependant)); + + String thingGuid = mainThing.getPrimaryKeyValue(); + repository.deleteEntityInstance(mainThing); + + Assertions.assertNull(repository.findEntityInstanceByGUID(thingGuid)); + Assertions.assertEquals(0, repository.countInstances(thing)); + Assertions.assertEquals(0, repository.countInstances(dependant)); } @Test - public void canFunctionWithoutADataGenerator(){ + public void canFunctionWithoutADataGenerator() { EntityRelModel erm = new EntityRelModel(); - Assertions.assertEquals(0, erm.getInstanceData().getAllInstanceCollections().size()); + + Assertions.assertEquals(0, erm.getEntityNames().size()); } @Test - public void canSetAndUseADataGenerator(){ + public void canSetAndUseARepositoryDataGenerator() { EntityRelModel erm = new EntityRelModel(); + EntityDefinition thing = erm.createEntityDefinition("thing", "things"); - DataPopulator dataPopulator = new DataPopulator() { - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { + RepositoryDataPopulator dataPopulator = (ERSchema schema, ThingRepository repository) -> + repository.addInstance(new EntityInstance(schema.getEntityDefinitionNamed("thing"))); - // Normally a populate would deal with the database instance, not the entitiesd - schema.defineEntity("thing", "things", -1); - database.createInstanceCollectionFrom(schema); - } - }; + erm.setDataGenerator(dataPopulator); - dataPopulator.populate(erm.getSchema(), erm.getInstanceData()); - - Assertions.assertEquals(1, erm.getInstanceData().getAllInstanceCollections().size()); + Assertions.assertTrue(erm.populateDatabase(EntityRelModel.DEFAULT_DATABASE_NAME)); + Assertions.assertEquals(1, + erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thing)); } @Test - public void populateDatabaseUsesRepositoryDataPopulatorWithoutCompatibilitySnapshot(){ + public void populateDatabaseUsesRepositoryDataPopulator() { try (EntityRelModel erm = new EntityRelModel(SqliteThingRepositoryProvider.inMemory())) { EntityDefinition thing = erm.createEntityDefinition("thing", "things"); thing.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); thing.addField(Field.is("name", FieldType.STRING)); - RepositoryDataPopulator dataPopulator = new RepositoryDataPopulator() { - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { - Assertions.fail("Repository-aware data populators should not use ERInstanceData"); - } - - @Override - public void populate(final ERSchema schema, final ThingRepository repository) { + RepositoryDataPopulator dataPopulator = (ERSchema schema, ThingRepository repository) -> repository.addInstance(new EntityInstance( schema.getEntityDefinitionNamed("thing")). setValue("name", "repository")); - } - }; erm.setDataGenerator(dataPopulator); @@ -300,7 +237,6 @@ public void populate(final ERSchema schema, final ThingRepository repository) { Assertions.assertEquals(1, instances.size()); Assertions.assertEquals("repository", instances.get(0).getFieldValue("name").asString()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java index 6ebfd41a..f1fcdd33 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java @@ -5,126 +5,95 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class MultipleEntityInstancesDatabasesTest { - @Test - public void byDefaultASingleDatabaseModelIsCreatedAndUsed(){ - + public void byDefaultASingleDatabaseModelIsCreatedAndUsed() { EntityRelModel erm = new EntityRelModel(); - EntityDefinition thingDefn = erm.createEntityDefinition("thing", "things"); - thingDefn.addField(Field.is("Title", FieldType.STRING)); - thingDefn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); - - EntityInstance thing1 = new EntityInstance(thingDefn); - thing1.addAutoGUIDstoInstance(); - thing1.setValue("Title", "Thing 1"); + EntityDefinition thingDefn = defineThing(erm); - EntityInstanceCollection thing = erm.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - thing.addInstance(thing1); + ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityInstance thing = repository.addInstance( + new EntityInstance(thingDefn).setValue("Title", "Thing 1")); - final String thingGUID = thing1.getPrimaryKeyValue(); - EntityInstance foundThing = erm.getInstanceData().findEntityInstanceByGUID(thingGUID); + EntityInstance foundThing = repository.findEntityInstanceByGUID(thing.getPrimaryKeyValue()); Assertions.assertEquals("Thing 1", foundThing.getFieldValue("Title").asString()); - Assertions.assertEquals(foundThing, thing1); + Assertions.assertEquals(foundThing, thing); } @Test - public void weCanAddANewNamedDatabaseToTheErm(){ - + public void weCanAddANewNamedDatabaseToTheErm() { EntityRelModel erm = new EntityRelModel(); - EntityDefinition thingDefn = erm.createEntityDefinition("thing", "things"); - thingDefn.addField(Field.is("Title", FieldType.STRING)); - thingDefn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + EntityDefinition thingDefn = defineThing(erm); erm.createInstanceDatabase("other_things"); - EntityInstance thing1 = new EntityInstance(thingDefn); - thing1.addAutoGUIDstoInstance(); - thing1.setValue("Title", "Thing 1"); + ThingRepository otherRepository = erm.getRepository("other_things"); + EntityInstance thing = otherRepository.addInstance( + new EntityInstance(thingDefn).setValue("Title", "Thing 1")); - EntityInstanceCollection thing = erm.getInstanceData("other_things").getInstanceCollectionForEntityNamed("thing"); - thing.addInstance(thing1); - - String guid = thing1.getPrimaryKeyValue(); - EntityInstance foundThing = erm.getInstanceData("other_things").findEntityInstanceByGUID(guid); + EntityInstance foundThing = otherRepository.findEntityInstanceByGUID(thing.getPrimaryKeyValue()); Assertions.assertEquals("Thing 1", foundThing.getFieldValue("Title").asString()); - Assertions.assertEquals(foundThing, thing1); - - Assertions.assertEquals(0, erm.getInstanceData().getInstanceCollectionForEntityNamed("thing").countInstances()); + Assertions.assertEquals(foundThing, thing); + Assertions.assertEquals(0, + erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thingDefn)); } @Test - public void cannotCreateDuplicateNamedDatabases(){ - + public void cannotCreateDuplicateNamedDatabases() { EntityRelModel erm = new EntityRelModel(); - EntityDefinition thingDefn = erm.createEntityDefinition("thing", "things"); - thingDefn.addField(Field.is("Title", FieldType.STRING)); + defineThing(erm); erm.createInstanceDatabase("other_things"); - Exception e = Assertions.assertThrows(IllegalStateException.class, ()->{ - erm.createInstanceDatabase("other_things"); - }); + Exception e = Assertions.assertThrows( + IllegalStateException.class, + () -> erm.createInstanceDatabase("other_things")); - Assertions.assertEquals("ERM Database Already Exists with name other_things", e.getMessage()); + Assertions.assertEquals("ERM Database Already Exists with name other_things", e.getMessage()); } @Test - public void canDeleteNamedDatabaseFromTheErm(){ - - // Given an ERM with a Model + public void canDeleteNamedDatabaseFromTheErm() { EntityRelModel erm = new EntityRelModel(); - EntityDefinition thingDefn = erm.createEntityDefinition("thing", "things"); - thingDefn.addField(Field.is("Title", FieldType.STRING)); - thingDefn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + EntityDefinition thingDefn = defineThing(erm); erm.createInstanceDatabase("other_things"); - EntityInstance thing1 = new EntityInstance(thingDefn); - thing1.addAutoGUIDstoInstance(); - thing1.setValue("Title", "Thing 1"); - - EntityInstanceCollection thing = erm.getInstanceData("other_things").getInstanceCollectionForEntityNamed("thing"); - thing.addInstance(thing1); + ThingRepository otherRepository = erm.getRepository("other_things"); + EntityInstance thing = otherRepository.addInstance( + new EntityInstance(thingDefn).setValue("Title", "Thing 1")); - String guid = thing1.getPrimaryKeyValue(); - EntityInstance foundThing = erm.getInstanceData("other_things").findEntityInstanceByGUID(guid); - - Assertions.assertEquals("Thing 1", foundThing.getFieldValue("Title").asString()); - - // WHEN we delete the database + Assertions.assertNotNull( + otherRepository.findEntityInstanceByGUID(thing.getPrimaryKeyValue())); erm.deleteInstanceDatabase("other_things"); - // There is no such database - ERInstanceData noDatabase = erm.getInstanceData("other_things"); - Assertions.assertNull(noDatabase); + Assertions.assertNull(erm.getRepository("other_things")); } @Test - public void cannotDeletDefaultDatabaseFromTheErm(){ - - // Given an ERM with a Model + public void cannotDeleteDefaultDatabaseFromTheErm() { EntityRelModel erm = new EntityRelModel(); - EntityDefinition thingDefn = erm.createEntityDefinition("thing", "things"); - thingDefn.addField(Field.is("Title", FieldType.STRING)); + defineThing(erm); - - // WE cannot delete default database - Exception e = Assertions.assertThrows(IllegalStateException.class, ()->{ - erm.deleteInstanceDatabase(EntityRelModel.DEFAULT_DATABASE_NAME); - }); + Exception e = Assertions.assertThrows( + IllegalStateException.class, + () -> erm.deleteInstanceDatabase(EntityRelModel.DEFAULT_DATABASE_NAME)); Assertions.assertEquals("Cannot delete default database", e.getMessage()); - // There is no such database - ERInstanceData isDatabase = erm.getInstanceData(EntityRelModel.DEFAULT_DATABASE_NAME); - Assertions.assertNotNull(isDatabase); + Assertions.assertNotNull(erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME)); + } + + private EntityDefinition defineThing(final EntityRelModel erm) { + EntityDefinition thingDefn = erm.createEntityDefinition("thing", "things"); + thingDefn.addField(Field.is("Title", FieldType.STRING)); + thingDefn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + return thingDefn; } } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java index 45c12ecd..70c43d00 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -33,12 +32,10 @@ public void setupCollectionTestData(){ .addFields( Field.is("truefalse", FieldType.BOOLEAN) ); - EntityInstanceCollection thing = erModel.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - - thing.addInstance( new EntityInstance(thing.definition())).setValue("truefalse", "true"); - thing.addInstance( new EntityInstance(thing.definition())).setValue("truefalse", "true"); - thing.addInstance( new EntityInstance(thing.definition())).setValue("truefalse", "true"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("truefalse", "false"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "true"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "true"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "true"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "false"); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java index 49755cdb..ec173e69 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java @@ -8,7 +8,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import java.util.List; @@ -27,12 +26,10 @@ public void setupCollectionTestData(){ Field.is("float", FieldType.FLOAT) ); - EntityInstanceCollection thing = erModel.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - - thing.addInstance(new EntityInstance(thing.definition())).setValue("float", "4.4"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("float", "1.1"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("float", "3.3"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("float", "2.2"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "4.4"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "1.1"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "3.3"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "2.2"); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java index 8a860760..bb115c71 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java @@ -8,7 +8,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import java.util.List; @@ -27,13 +26,11 @@ public void setupCollectionTestData(){ .addFields( Field.is("fakeid", FieldType.INTEGER) ); - EntityInstanceCollection thing = erModel.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - // fakeid is a proxy for the actual id which always starts at 1 and auto increments - thing.addInstance(new EntityInstance(thing.definition())).setValue("fakeid", "1"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("fakeid", "2"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("fakeid", "3"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("fakeid", "4"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "1"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "2"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "3"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "4"); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java index f5396930..2cc196de 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java @@ -8,7 +8,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import java.util.List; @@ -31,12 +30,10 @@ public void setupCollectionTestData(){ Field.is("int", FieldType.INTEGER) ); - EntityInstanceCollection thing = erModel.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - - thing.addInstance(new EntityInstance(thing.definition())).setValue("int", "3"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("int", "1"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("int", "4"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("int", "2"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "3"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "1"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "4"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "2"); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java index 88a15c2a..6cce0d86 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java @@ -8,7 +8,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import java.util.List; @@ -26,12 +25,10 @@ public void setupCollectionTestData(){ .addFields( Field.is("string", FieldType.STRING) ); - EntityInstanceCollection thing = erModel.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - - thing.addInstance(new EntityInstance(thing.definition())).setValue("string", "one"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("string", "two"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("string", "three"); - thing.addInstance(new EntityInstance(thing.definition())).setValue("string", "four"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "one"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "two"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "three"); + RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "four"); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java index 7cbba89d..4e0f3681 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java @@ -1,6 +1,8 @@ package uk.co.compendiumdev.thingifier.core.query; import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; final class RepositoryUrlQueryTestSupport { @@ -13,4 +15,14 @@ static RepositoryUrlQuery query(final EntityRelModel model, final String query) model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), query); } + + static EntityInstance add( + final EntityRelModel model, + final String entityName, + final String fieldName, + final String value) { + EntityDefinition definition = model.getSchema().getEntityDefinitionNamed(entityName); + EntityInstance instance = new EntityInstance(definition).setValue(fieldName, value); + return model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).addInstance(instance); + } } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java index 7ce59a45..e8cf3e36 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java @@ -4,10 +4,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import java.util.List; @@ -18,32 +18,30 @@ public class SortingViaQueryFiltersTest { // todo: lower level testing at the EntityInstanceListSorter level - EntityInstanceCollection thing; + EntityDefinition thing; EntityRelModel erModel; @BeforeEach public void setupThingifier(){ erModel = new EntityRelModel(); - erModel.createEntityDefinition("thing", "things") + thing = erModel.createEntityDefinition("thing", "things") .addFields(Field.is("truefalse", FieldType.BOOLEAN), Field.is("int", FieldType.INTEGER)); - thing = erModel.getInstanceData().getInstanceCollectionForEntityNamed("thing"); - } @Test public void canSortIntViaAQuery(){ - final EntityInstance thing1 = thing.addInstance(new EntityInstance(thing.definition())); - thing1.setValue("int", "1"); + final EntityInstance thing1 = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). + addInstance(new EntityInstance(thing).setValue("int", "1")); - final EntityInstance thing2 = thing.addInstance(new EntityInstance(thing.definition())); - thing2.setValue("int", "2"); + final EntityInstance thing2 = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). + addInstance(new EntityInstance(thing).setValue("int", "2")); - final EntityInstance thing3 = thing.addInstance(new EntityInstance(thing.definition())); - thing3.setValue("int", "3"); + final EntityInstance thing3 = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). + addInstance(new EntityInstance(thing).setValue("int", "3")); QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-int"); @@ -89,16 +87,16 @@ public void canSortIntViaAQuery(){ public void canSortViaAQuery(){ EntityRelModel aThingifier = new EntityRelModel(); - aThingifier.createEntityDefinition("thing", "things") - .addField(Field.is("truefalse", FieldType.BOOLEAN)); - - EntityInstanceCollection thing = aThingifier.getInstanceData().getInstanceCollectionForEntityNamed("thing"); + EntityDefinition thing = aThingifier.createEntityDefinition("thing", "things"); + thing.addField(Field.is("truefalse", FieldType.BOOLEAN)); - final EntityInstance trueThing = thing.addInstance(new EntityInstance(thing.definition())); - trueThing.setValue("truefalse", "true"); + final EntityInstance trueThing = + aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). + addInstance(new EntityInstance(thing).setValue("truefalse", "true")); - final EntityInstance falseThing = thing.addInstance(new EntityInstance(thing.definition())); - falseThing.setValue("truefalse", "false"); + final EntityInstance falseThing = + aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). + addInstance(new EntityInstance(thing).setValue("truefalse", "false")); QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-truefalse"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java index 1b509e13..115bd5e3 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java @@ -6,10 +6,10 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.domain.definitions.Cardinality; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.List; @@ -23,7 +23,6 @@ public class TodoManagerQueryEngineTest { private EntityRelModel todoManager; EntityInstance paperwork; EntityInstance filework; - EntityInstanceCollection projects; EntityInstance officeCategory; private EntityDefinition project; @@ -55,22 +54,22 @@ public void createDefinitions(){ todoManager.createRelationshipDefinition(category, project, "projects", Cardinality.ONE_TO_MANY()); todoManager.createRelationshipDefinition(todo, category, "categories", Cardinality.ONE_TO_MANY()); - final EntityInstanceCollection todos = todoManager.getInstanceData().getInstanceCollectionForEntityNamed("todo"); - final EntityInstanceCollection categories = todoManager.getInstanceData().getInstanceCollectionForEntityNamed("category"); - projects = todoManager.getInstanceData().getInstanceCollectionForEntityNamed("project"); + ThingRepository repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - paperwork = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "scan paperwork"); + paperwork = repository.addInstance( + new EntityInstance(todo).setValue("title", "scan paperwork")); //System.out.println(new Gson().toJson(JsonThing.asJsonObject(paperwork))); - filework = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "file paperwork"); + filework = repository.addInstance( + new EntityInstance(todo).setValue("title", "file paperwork")); - officeCategory = categories.addInstance(new EntityInstance(categories.definition())).setValue("title", "Office"); + officeCategory = repository.addInstance( + new EntityInstance(category).setValue("title", "Office")); - EntityInstance homeCategory = categories.addInstance(new EntityInstance(categories.definition())).setValue("title", "Home"); + repository.addInstance(new EntityInstance(category).setValue("title", "Home")); - - paperwork.getRelationships().connect("categories", officeCategory); + repository.connectRelationship(paperwork, "categories", officeCategory); } @@ -199,10 +198,12 @@ public void connectionTesting() { List queryResults; // - EntityInstance officeWork = projects.addInstance(new EntityInstance(projects.definition())).setValue("title", "Office Work"); + ThingRepository repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + EntityInstance officeWork = repository.addInstance( + new EntityInstance(project).setValue("title", "Office Work")); - officeWork.getRelationships().connect("tasks", paperwork); - officeWork.getRelationships().connect("tasks", filework); + repository.connectRelationship(officeWork, "tasks", paperwork); + repository.connectRelationship(officeWork, "tasks", filework); // match on relationships diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java index 3af1c1d0..0f9fef69 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java @@ -10,7 +10,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.RepositoryUrlQuery; @@ -124,7 +123,7 @@ public void sqliteRepositoryQuotesGeneratedSqlIdentifiers() { } @Test - public void sqliteFileBackedRepositoryOnlyHydratesCompatibilitySnapshotWhenRequested() { + public void sqliteFileBackedRepositoryFiltersAfterReopen() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); Path databasePath = tempDir.resolve("lazy.sqlite"); @@ -141,26 +140,19 @@ public void sqliteFileBackedRepositoryOnlyHydratesCompatibilitySnapshotWhenReque SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { reopened.initializeFrom(schema); - EntityInstanceCollection cachedProjects = - reopened.getInstanceCollectionForEntityNamed("project"); - Assertions.assertEquals(0, cachedProjects.countInstances()); - QueryFilterParams params = new QueryFilterParams(); params.put("title", "First"); List filtered = reopened.listInstances(projectDefinition, params); Assertions.assertEquals(1, filtered.size()); - Assertions.assertEquals(1, cachedProjects.countInstances()); - - reopened.getInstanceData(); - - Assertions.assertEquals(2, cachedProjects.countInstances()); + Assertions.assertEquals("First", filtered.get(0).getFieldValue("title").asString()); + Assertions.assertEquals(2, reopened.countInstances(projectDefinition)); } } @Test - public void sqliteCountInstancesDoesNotHydrateCompatibilitySnapshot() { + public void sqliteCountInstancesWorksAfterReopen() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); Path databasePath = tempDir.resolve("count-lazy.sqlite"); @@ -177,13 +169,7 @@ public void sqliteCountInstancesDoesNotHydrateCompatibilitySnapshot() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { reopened.initializeFrom(schema); - EntityInstanceCollection cachedProjects = - reopened.getInstanceCollectionForEntityNamed("project"); - - Assertions.assertEquals(0, cachedProjects.countInstances()); Assertions.assertEquals(2, reopened.countInstances(projectDefinition)); - Assertions.assertEquals(0, cachedProjects.countInstances()); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); } } @@ -203,12 +189,11 @@ public void repositoriesExportDataAsJsonThroughTheRepositoryContract() { assertExportedJsonContainsProjectAndTask(memoryRepository.exportDataAsJson(schema)); assertExportedJsonContainsProjectAndTask(sqliteRepository.exportDataAsJson(schema)); - Assertions.assertFalse(sqliteRepository.hasLoadedCompatibilitySnapshot()); } } @Test - public void sqliteJsonExportDoesNotHydrateCompatibilitySnapshot() { + public void sqliteJsonExportWorksAfterReopen() { ERSchema schema = todoSchema(); Path databasePath = tempDir.resolve("export-lazy.sqlite"); @@ -222,20 +207,9 @@ public void sqliteJsonExportDoesNotHydrateCompatibilitySnapshot() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { reopened.initializeFrom(schema); - EntityInstanceCollection cachedProjects = - reopened.getInstanceCollectionForEntityNamed("project"); - EntityInstanceCollection cachedTasks = - reopened.getInstanceCollectionForEntityNamed("task"); - - Assertions.assertEquals(0, cachedProjects.countInstances()); - Assertions.assertEquals(0, cachedTasks.countInstances()); - String json = reopened.exportDataAsJson(schema); assertExportedJsonContainsProjectAndTask(json); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); - Assertions.assertEquals(0, cachedProjects.countInstances()); - Assertions.assertEquals(0, cachedTasks.countInstances()); } } @@ -255,12 +229,11 @@ public void repositoriesReportMarkdownThroughTheRepositoryContract() { Assertions.assertTrue(markdown.contains("## Of project")); Assertions.assertTrue(markdown.contains("Repository project")); Assertions.assertTrue(markdown.contains("Wire repository")); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @Test - public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { + public void sqliteRelationshipReadsWorkAfterReopen() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); EntityDefinition taskDefinition = schema.getEntityDefinitionNamed("task"); @@ -285,8 +258,6 @@ public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { reopened.initializeFrom(schema); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); - EntityInstance project = reopened.findInstanceByPrimaryKey(projectDefinition, "1"); QueryFilterParams params = new QueryFilterParams(); @@ -298,7 +269,6 @@ public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { Assertions.assertEquals(1, filteredTasks.size()); Assertions.assertEquals("Wire repository", filteredTasks.get(0).getFieldValue("title").asString()); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); QueryFilterParams regexParams = new QueryFilterParams(); regexParams.put("title", "~=Wire [Rr]epository"); @@ -309,7 +279,6 @@ public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { Assertions.assertEquals(1, regexFilteredTasks.size()); Assertions.assertEquals("Wire repository", regexFilteredTasks.get(0).getFieldValue("title").asString()); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); RepositoryUrlQuery query = new RepositoryUrlQuery(schema, reopened, "project/1/tasks"). @@ -317,12 +286,11 @@ public void sqliteRelationshipReadsDoNotHydrateCompatibilitySnapshot() { Assertions.assertTrue(query.isResultACollection()); Assertions.assertEquals(2, query.getListEntityInstances().size()); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); } } @Test - public void sqliteConvertibleRegexFilterDoesNotHydrateCompatibilitySnapshot() { + public void sqliteConvertibleRegexFilterWorksAfterReopen() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); Path databasePath = tempDir.resolve("regex-like-lazy.sqlite"); @@ -352,14 +320,12 @@ public void sqliteConvertibleRegexFilterDoesNotHydrateCompatibilitySnapshot() { Assertions.assertEquals(1, filtered.size()); Assertions.assertEquals("Repository project", filtered.get(0).getFieldValue("title").asString()); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); - Assertions.assertEquals(1, - reopened.getInstanceCollectionForEntityNamed("project").countInstances()); + Assertions.assertEquals(3, reopened.countInstances(projectDefinition)); } } @Test - public void sqliteRegexpFilterDoesNotHydrateCompatibilitySnapshot() { + public void sqliteRegexpFilterWorksAfterReopen() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); Path databasePath = tempDir.resolve("regex-regexp-lazy.sqlite"); @@ -387,14 +353,12 @@ public void sqliteRegexpFilterDoesNotHydrateCompatibilitySnapshot() { Assertions.assertEquals(1, filtered.size()); Assertions.assertEquals("Repository project", filtered.get(0).getFieldValue("title").asString()); - Assertions.assertFalse(reopened.hasLoadedCompatibilitySnapshot()); - Assertions.assertEquals(1, - reopened.getInstanceCollectionForEntityNamed("project").countInstances()); + Assertions.assertEquals(2, reopened.countInstances(projectDefinition)); } } @Test - public void sqliteInMemoryRegexpFilterDoesNotHydrateCompatibilitySnapshot() { + public void sqliteInMemoryRegexpFilterWorks() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); @@ -416,12 +380,11 @@ public void sqliteInMemoryRegexpFilterDoesNotHydrateCompatibilitySnapshot() { Assertions.assertEquals(1, filtered.size()); Assertions.assertEquals("Repository project", filtered.get(0).getFieldValue("title").asString()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @Test - public void sqliteInvalidRegexFilterFailsWithoutHydratingCompatibilitySnapshot() { + public void sqliteInvalidRegexFilterFails() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); @@ -437,12 +400,11 @@ public void sqliteInvalidRegexFilterFailsWithoutHydratingCompatibilitySnapshot() Assertions.assertThrows( SqliteRegexFilterPolicy.UnsupportedRegexFilterException.class, () -> repository.listInstances(projectDefinition, regexParams)); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @Test - public void sqliteTooComplexRegexFilterFailsWithoutHydratingCompatibilitySnapshot() { + public void sqliteTooComplexRegexFilterFails() { ERSchema schema = todoSchema(); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); @@ -458,7 +420,6 @@ public void sqliteTooComplexRegexFilterFailsWithoutHydratingCompatibilitySnapsho Assertions.assertThrows( SqliteRegexFilterPolicy.UnsupportedRegexFilterException.class, () -> repository.listInstances(projectDefinition, regexParams)); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } diff --git a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java index 9f4e6ea4..e49380c6 100644 --- a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java +++ b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java @@ -3,9 +3,7 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoAPIDataPopulator implements RepositoryDataPopulator { @@ -20,17 +18,6 @@ public class TodoAPIDataPopulator implements RepositoryDataPopulator { "train staff", "schedule meeting"}; - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { - - EntityInstanceCollection todo = database.getInstanceCollectionForEntityNamed("todo"); - - for(String todoItem : TODOS){ - todo.addInstance(new EntityInstance(todo.definition())). - setValue("title", todoItem); - } - } - @Override public void populate(final ERSchema schema, final ThingRepository repository) { EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); diff --git a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java index d0146087..f21d2242 100644 --- a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java +++ b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java @@ -3,43 +3,10 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoManagerAPIDataPopulator implements RepositoryDataPopulator { - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { - - EntityInstanceCollection todo = database.getInstanceCollectionForEntityNamed("todo"); - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())). - setValue("title", "scan paperwork"); - - EntityInstance filework = todo.addInstance(new EntityInstance(todo.definition())). - setValue("title", "file paperwork"); - - EntityInstanceCollection category = database.getInstanceCollectionForEntityNamed("category"); - - EntityInstance officeCategory = category.addInstance(new EntityInstance(category.definition())). - setValue("title", "Office"); - - - EntityInstance homeCategory = category.addInstance(new EntityInstance(category.definition())). - setValue("title", "Home"); - - EntityInstanceCollection project = database.getInstanceCollectionForEntityNamed("project"); - - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())). - setValue("title", "Office Work"); - - officeWork.getRelationships().connect("tasks", paperwork); - officeWork.getRelationships().connect("tasks", filework); - - paperwork.getRelationships().connect("categories", officeCategory); - - } - @Override public void populate(final ERSchema schema, final ThingRepository repository) { EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); diff --git a/pmd-ruleset.xml b/pmd-ruleset.xml new file mode 100644 index 00000000..c6355198 --- /dev/null +++ b/pmd-ruleset.xml @@ -0,0 +1,16 @@ + + + + Focused static analysis rules for source style problems that are easy to miss during refactoring. + + + + + + + + + diff --git a/pom.xml b/pom.xml index 26950ade..ff189c65 100644 --- a/pom.xml +++ b/pom.xml @@ -22,9 +22,45 @@ 5.5.0 2.3.1 2.0.10 + 3.28.0 + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + project-fqn-check + verify + + check + + + ${maven.multiModuleProjectDirectory}/checkstyle-project-fqn.xml + true + true + true + warning + + + + + + org.apache.maven.plugins + maven-pmd-plugin + + + pmd-check + verify + + check + + + + + @@ -44,6 +80,19 @@ warning + + org.apache.maven.plugins + maven-pmd-plugin + ${maven-pmd-plugin.version} + + + ${maven.multiModuleProjectDirectory}/pmd-ruleset.xml + + true + true + ${java.version} + + diff --git a/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java b/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java index b51e2989..e2239421 100644 --- a/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java +++ b/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java @@ -1,15 +1,15 @@ package uk.co.compendiumdev.thingifier.swaggerizer; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; -public class TodoAPITestDataPopulator implements DataPopulator { +public class TodoAPITestDataPopulator implements RepositoryDataPopulator { @Override - public void populate(final ERSchema schema, final ERInstanceData database) { + public void populate(final ERSchema schema, final ThingRepository repository) { String [] todos={ "scan paperwork", @@ -21,11 +21,10 @@ public void populate(final ERSchema schema, final ERInstanceData database) { "train staff", "schedule meeting"}; - EntityInstanceCollection todo = database.getInstanceCollectionForEntityNamed("todo"); + EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); for(String todoItem : todos){ - todo.addInstance(new EntityInstance(todo.definition())). - setValue("title", todoItem); + repository.addInstance(new EntityInstance(todo).setValue("title", todoItem)); } } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java index 8bb1c069..1ad9c489 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/Thingifier.java @@ -7,10 +7,7 @@ import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfigProfiles; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonPopulator; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.LegacyDataPopulatorAdapter; import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.*; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -30,7 +27,7 @@ public final class Thingifier implements AutoCloseable { private final EntityRelModel erm; private ApiDocsConfig apiDocsConfig; - private DataPopulator dataPopulator; + private RepositoryDataPopulator dataPopulator; private String title; private String initialParagraph; private final ThingifierApiConfig apiConfig; @@ -126,29 +123,10 @@ public boolean hasRelationshipNamed(final String relationshipName) { // Instances - /** - * @deprecated Compatibility collection access. Use {@link #getRepository(String)} - * with schema entity definitions, or {@link #listThingInstancesNamed(String, String)}. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public List getThings(final String database) { - return erm.getRepository(database).getAllInstanceCollections(); - } - - public EntityInstance findThingInstanceByGuid(final String thingGUID, final String database) { return erm.getRepository(database).findEntityInstanceByGUID(thingGUID); } - /** - * @deprecated Compatibility collection access. Use {@link #getRepository(String)} - * or {@link #listThingInstancesNamed(String, String)}. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public EntityInstanceCollection getThingInstancesNamed(final String aName, final String database) { - return erm.getRepository(database).getInstanceCollectionForEntityNamed(aName); - } - public List listThingInstancesNamed(final String aName, final String database) { EntityDefinition definition = erm.getSchema().getDefinitionWithSingularOrPluralNamed(aName); ThingRepository repository = erm.getRepository(database); @@ -171,21 +149,6 @@ public EntityInstance findThingInstanceByFieldNameAndValue( return repository.findInstanceByFieldNameAndValue(definition, fieldName, fieldValue); } - /** - * @deprecated Compatibility collection access. Use {@link #getRepository(String)} - * with {@link EntityDefinition} resolved from {@link #getERmodel()}. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public EntityInstanceCollection getInstancesForSingularOrPluralNamedEntity(final String term, final String database) { - final EntityDefinition defn = erm.getSchema().getDefinitionWithSingularOrPluralNamed(term); - if(defn!=null){ - final String entityName = defn.getName(); - return erm.getRepository(database).getInstanceCollectionForEntityNamed(entityName); - } - - return null; - } - public void clearAllData() { // clear data in default database but keep database clearAllData(EntityRelModel.DEFAULT_DATABASE_NAME); @@ -218,7 +181,7 @@ public void generateData(final String database) { } } - public void setDataGenerator(DataPopulator dataPopulator) { + public void setDataGenerator(RepositoryDataPopulator dataPopulator) { this.dataPopulator = dataPopulator; erm.setDataGenerator(dataPopulator); } @@ -298,22 +261,7 @@ public String getInitialParagraph() { return this.initialParagraph; } - /** - * @deprecated Compatibility helper backed by legacy instance data. Prefer a - * repository/provider configured with the desired data source. - */ - @Deprecated(forRemoval = true, since = "1.5.6") - public Thingifier cloneWithDifferentData(final List instances) { - return new Thingifier( this.getERmodel().cloneWithDifferentData(instances), - this.apiConfig(), - this.apiConfigProfiles(), - this.title, - this.initialParagraph, - this.apiDocsConfig - ); - } - - public DataPopulator getDefaultDataPopulator() { + public RepositoryDataPopulator getDefaultDataPopulator() { return dataPopulator; } @@ -346,11 +294,6 @@ public ApiDocsConfig apidocsconfig() { } private void populateRepository(final ThingRepository repository) { - if (dataPopulator instanceof RepositoryDataPopulator) { - ((RepositoryDataPopulator) dataPopulator).populate(erm.getSchema(), repository); - return; - } - - LegacyDataPopulatorAdapter.populate(dataPopulator, erm.getSchema(), repository); + dataPopulator.populate(erm.getSchema(), repository); } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java b/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java index bcf76510..2b823239 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java @@ -5,7 +5,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -44,7 +45,7 @@ public void thingifierCanManageThings(){ addFields(Field.is("url", STRING),Field.is("name",STRING) ); - EntityInstanceCollection urls = things.getThingInstancesNamed("URL", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection urls = ThingifierRepositoryTestSupport.collection(things, "URL"); Assertions.assertTrue(urls.definition().hasFieldNameDefined("url")); Assertions.assertTrue(urls.definition().hasFieldNameDefined("name")); @@ -57,8 +58,8 @@ public void thingifierCanManageThings(){ user.addFields(Field.is("name", STRING)); - EntityInstanceCollection entityInstanceCollection = things.getThingInstancesNamed("USER", EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance alan = entityInstanceCollection.addInstance(new EntityInstance(entityInstanceCollection.definition())). + RepositoryBackedTestCollection RepositoryBackedTestCollection = ThingifierRepositoryTestSupport.collection(things, "USER"); + EntityInstance alan = RepositoryBackedTestCollection.addInstance(new EntityInstance(RepositoryBackedTestCollection.definition())). setValue("name","alan"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java index 13c3ccf2..57e66704 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java @@ -13,7 +13,8 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import java.util.HashMap; import java.util.Map; @@ -60,7 +61,7 @@ public void putCanAmendExistingInstance() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityInstanceCollection instances = thingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); // PUT @@ -95,7 +96,7 @@ public void putCanAmendExistingInstancesWhenOptionalFieldsAreMissing() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityInstanceCollection instances = thingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); // create something to amend with PUT EntityInstance officeWork = instances.addInstance(new EntityInstance(instances.definition())). @@ -124,7 +125,7 @@ public void putFailCanNotAmendExistingInstancesWhenMandatoryFieldsAreMissing() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityInstanceCollection instances = thingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); // create something to amend with PUT EntityInstance officeWork = instances.addInstance(new EntityInstance(instances.definition())). @@ -153,7 +154,7 @@ public void putFailCanNotAmendPrimaryKey() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityInstanceCollection instances = thingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); // PUT @@ -194,7 +195,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityInstanceCollection instances = thingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); // PUT @@ -238,7 +239,7 @@ public void putFailCanNotAmendAutoGuid() { entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); entityDefn.addField(Field.is("guid", FieldType.AUTO_GUID)); - EntityInstanceCollection myInstances = myThingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); // PUT @@ -286,7 +287,7 @@ public void putCanCreateAnInstanceWhenNoAutoFields() { entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - EntityInstanceCollection myInstances = myThingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); // PUT @@ -334,7 +335,7 @@ public void putCanCreateAnInstanceWhenNoAutoFieldsAndMissingPrimaryKeyFromBody() entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - EntityInstanceCollection myInstances = myThingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); // PUT @@ -383,7 +384,7 @@ public void putFailCanNotCreateAnInstanceWhenKeyDoesNotMatchBody() { entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - EntityInstanceCollection myInstances = myThingifier.getThingInstancesNamed("entity", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); // PUT diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java index 89985dd0..2f68ef1c 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java @@ -4,7 +4,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -36,7 +37,7 @@ public void createDefinitions(){ @Test public void createAndDelete(){ - final EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); for(int todoCount=0; todoCount < 100; todoCount++){ todos.addInstance(new EntityInstance(todos.definition())). @@ -53,14 +54,14 @@ public void createAndDelete(){ @Test public void createAndDeleteRelationships(){ - final EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); for(int todoCount=0; todoCount < 100; todoCount++){ todos.addInstance(new EntityInstance(todos.definition())). setValue("title", "title " + System.nanoTime()); } - final EntityInstanceCollection projects = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection projects = ThingifierRepositoryTestSupport.collection(todoManager, "project"); for(int todoCount=0; todoCount < 50; todoCount++){ projects.addInstance(new EntityInstance(projects.definition())). diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java index 636f1669..931cadc5 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java @@ -7,7 +7,8 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -26,8 +27,8 @@ public class RelationshipApiNonHttpTest { - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; private JsonThing jsonThing; @@ -36,8 +37,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); jsonThing = new JsonThing(todoManager.apiConfig().jsonOutput()); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } @@ -588,7 +589,7 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin EntityInstance myTodo = todo.addInstance(new EntityInstance(todo.definition())). setValue("title", "an estimated todo"); - final EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); int numberOfEstimates = estimates.countInstances(); Assertions.assertEquals(0, numberOfEstimates ); @@ -647,7 +648,7 @@ public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { EntityInstance myTodo = todo.addInstance(new EntityInstance(todo.definition())). setValue("title", "an estimated todo"); - final EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); int numberOfEstimates = estimates.countInstances(); Assertions.assertEquals(0, numberOfEstimates ); @@ -679,7 +680,7 @@ public void postCanCreateEstimateAMandatoryRelationshipUsingAPI() { 0, myTodo.getRelationships().getConnectedItems("estimates").size()); // there are no estimates at all - final EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); int numberOfEstimates = estimates.countInstances(); Assertions.assertEquals(0, numberOfEstimates ); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java index d69dcac6..1056ef46 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java @@ -59,7 +59,6 @@ public void getRelationshipPathsUseRepositoryWithoutLoadingCompatibilitySnapshot Assertions.assertEquals(1, tasksResponse.getReturnedInstanceCollection().size()); Assertions.assertEquals(task.getPrimaryKeyValue(), tasksResponse.getReturnedInstanceCollection().get(0).getPrimaryKeyValue()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); ApiResponse projectResponse = todoManager.api().get( String.format("todo/%s/task-of", task.getPrimaryKeyValue()), @@ -71,7 +70,6 @@ public void getRelationshipPathsUseRepositoryWithoutLoadingCompatibilitySnapshot Assertions.assertEquals(1, projectResponse.getReturnedInstanceCollection().size()); Assertions.assertEquals(projectInstance.getPrimaryKeyValue(), projectResponse.getReturnedInstanceCollection().get(0).getPrimaryKeyValue()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @@ -95,7 +93,6 @@ public void getUnsupportedRelationshipTraversalDoesNotUseCompatibilityFallback() new HttpHeadersBlock()); Assertions.assertEquals(404, response.getStatusCode()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @@ -128,7 +125,6 @@ public void postRelationshipPathConnectsExistingItemWithoutLoadingCompatibilityS task.getPrimaryKeyValue(), repository.listRelatedInstances(projectInstance, "tasks"). get(0).getPrimaryKeyValue()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @@ -161,7 +157,6 @@ public void postReverseRelationshipPathConnectsExistingItemWithoutLoadingCompati projectInstance.getPrimaryKeyValue(), repository.listRelatedInstances(task, "task-of"). get(0).getPrimaryKeyValue()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @@ -192,7 +187,6 @@ public void postRelationshipPathCreatesAndConnectsNewItemWithoutLoadingCompatibi "created through relationship path", relatedTasks.get(0).getFieldValue("title").asString()); Assertions.assertEquals(1, repository.listInstances(todo).size()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @@ -222,7 +216,6 @@ public void deleteRelationshipPathRemovesOnlyRelationshipWithoutLoadingCompatibi repository.listRelatedInstances(projectInstance, "tasks").isEmpty()); Assertions.assertNotNull( repository.findInstanceByQueryIdentifier(todo, task.getPrimaryKeyValue())); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } @@ -249,7 +242,6 @@ public void deleteMissingRelationshipPathReturns404WithoutLoadingCompatibilitySn Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertTrue( repository.listRelatedInstances(projectInstance, "tasks").isEmpty()); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java index c5de4e59..ca14251c 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java @@ -6,7 +6,8 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -18,8 +19,8 @@ public class VerbDeleteEntityInstanceApiNonHttpTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -32,8 +33,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java index 38a93c8b..63827290 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java @@ -6,7 +6,8 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -21,8 +22,8 @@ public class VerbGetEntityInstanceApiNonHttpTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -35,8 +36,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java index 98be8377..f61a64e7 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java @@ -7,7 +7,8 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -21,8 +22,8 @@ public class VerbPostEntityInstanceApiNonHttpTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -35,8 +36,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java index b9a2bfcf..df4a26f8 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java @@ -7,7 +7,8 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -23,8 +24,8 @@ public class VerbPutEntityInstanceApiNonHttpTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -37,8 +38,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } @@ -211,7 +212,7 @@ public void putCanNotAmendGUID() { // Map requestBody; // ApiResponse apiresponse; // -// project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); +// project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); // final Field anIdField = Field.is("id", FieldType.AUTO_INCREMENT); // project.definition().addField(anIdField); // @@ -246,7 +247,7 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { Map requestBody; ApiResponse apiresponse; - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); final Field anIdField = Field.is("id", FieldType.AUTO_INCREMENT); project.definition().addField(anIdField); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java index f41f66db..6737b246 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java @@ -6,7 +6,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -17,8 +18,8 @@ public class DeleteRequestTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // TODO: need the http_api tests to achieve 100% of ThingifierRestApiHandler @@ -28,8 +29,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java index 6fa9b09e..579bbff9 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java @@ -6,7 +6,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -20,8 +21,8 @@ public class JsonRequestResponseTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // todo: Too complicated any test that uses the TodoManagerModel in thingifier needs to be simplified @@ -32,8 +33,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); todoManager.apiConfig().setApiToEnforceAcceptHeaderForResponses(false); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java index c18b0a92..46d2ff2c 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java @@ -7,7 +7,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -20,18 +21,18 @@ public class RelationshipHttpTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; - EntityInstanceCollection categories; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; + RepositoryBackedTestCollection categories; @BeforeEach public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); - categories = todoManager.getThingInstancesNamed("category", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + categories = ThingifierRepositoryTestSupport.collection(todoManager, "category"); } @@ -338,7 +339,7 @@ public void canNotCreateEstimateWithoutMandatoryRelationship(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(400, response.getStatusCode()); - Assertions.assertEquals(0, todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME).countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.collection(todoManager, "estimate").countInstances()); } @Test @@ -357,7 +358,7 @@ public void canCreateAnEstimateForTodoMandatoryRelationship(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME).countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.collection(todoManager, "estimate").countInstances()); Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); } @@ -369,7 +370,7 @@ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO for estimating"); - final EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); final EntityInstance anEstimate = estimates.addInstance(new EntityInstance(estimates.definition())).setValue("duration", "7"); anEstimate.getRelationships().connect("estimate", atodo); @@ -396,7 +397,7 @@ public void canGetEstimatesViaRelationship(){ final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO for estimating"); - final EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); final EntityInstance anEstimate = estimates.addInstance(new EntityInstance(estimates.definition())).setValue("duration", "7").setValue("description", "an estimate"); anEstimate.getRelationships().connect("estimate", atodo); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java index a111e76b..aae7cab4 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java @@ -5,7 +5,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -18,8 +19,8 @@ public class XmlRequestResponseTest { private Thingifier todoManager; - EntityInstanceCollection todo; - EntityInstanceCollection project; + RepositoryBackedTestCollection todo; + RepositoryBackedTestCollection project; // todo: Too complicated any test that uses the TodoManagerModel in thingifier needs to be simplified @@ -30,8 +31,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java index 5f1ba873..5b51e5e6 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java @@ -5,7 +5,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import java.util.Collection; @@ -25,7 +26,7 @@ public void createDefinitions(){ public void todoModelDefinitionCheck(){ - EntityInstanceCollection todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); Assertions.assertTrue(todo.definition().hasFieldNameDefined("title")); Assertions.assertTrue(todo.definition().hasFieldNameDefined("description")); @@ -42,8 +43,8 @@ public void todoModelDefinitionCheck(){ public void relationshipDefinitionCheck(){ - EntityInstanceCollection todo = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstanceCollection project = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + RepositoryBackedTestCollection project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "scan paperwork"); EntityInstance filework = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "file paperwork"); @@ -75,7 +76,7 @@ public void relationshipDefinitionCheck(){ @Test public void createAndAmendSomeTodos(){ - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). setValue("title", "Tidy up my room"). @@ -98,7 +99,7 @@ public void createAndAmendSomeTodos(){ @Test public void createAndDeleteTodos(){ - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); int originalTodosCount = todos.countInstances(); @@ -131,7 +132,7 @@ public void createAndDeleteTodos(){ @Test public void createAmendAndDeleteATodoWithAGivenGUID(){ - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); int originalTodosCount = todos.countInstances(); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java index f01ed583..09fb97f6 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java @@ -6,7 +6,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -33,14 +34,14 @@ public void createDefinitions(){ public void byDefaultRelationshipsAreOptional(){ - EntityInstanceCollection projects = todoManager.getThingInstancesNamed("project", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection projects = ThingifierRepositoryTestSupport.collection(todoManager, "project"); EntityInstance aProject = projects.addInstance(new EntityInstance(projects.definition())).setValue("title", "myproject"); Assertions.assertTrue(aProject.validate().isValid()); - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). setValue("title", "Tidy up my room"). @@ -54,13 +55,13 @@ public void byDefaultRelationshipsAreOptional(){ @Test public void anEstimateWithoutATodoIsInvalid(){ - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); EntityInstance tidyRoomEstimate = estimates.addInstance(new EntityInstance(estimates.definition())). setValue("duration", "1"); @@ -73,13 +74,13 @@ public void anEstimateWithoutATodoIsInvalid(){ @Test public void anEstimateMustHaveATodoToBeValid(){ - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); EntityInstance tidyRoomEstimate = estimates.addInstance(new EntityInstance(estimates.definition())). setValue("duration", "1"); @@ -100,14 +101,14 @@ public void anEstimateMustHaveATodoToBeValid(){ @Test public void deleteAlsoCoversMandatoryOptionalityRelationships(){ - EntityInstanceCollection todos = todoManager.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - EntityInstanceCollection estimates = todoManager.getThingInstancesNamed("estimate", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); EntityInstance tidyRoomEstimate = estimates.addInstance(new EntityInstance(estimates.definition())). setValue("duration", "1"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java index 75d7e0a1..f8e3c466 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java @@ -7,14 +7,13 @@ import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; import uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.datapopulator.DataPopulator; -import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import java.util.HashMap; import java.util.Map; @@ -46,7 +45,7 @@ public void aGetRequestWillCreateNewSessionWithDatabase(){ final HttpApiResponse response = api.get(new HttpApiRequest("/things").setHeaders(headers)); // add a thing - final EntityInstanceCollection thing = thingifier.getERmodel().getInstanceData("other_things").getInstanceCollectionForEntityNamed("thing"); + final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); final HttpApiResponse response2 = api.get(new HttpApiRequest("/things/" + existingInstance.getPrimaryKeyValue()).setHeaders(headers)); @@ -59,22 +58,20 @@ public void aRequestWillCreateNewSessionWithDatabaseUsingDefaultData(){ Thingifier thingifier = getTestThingifier(); - DataPopulator dataPopulator = new DataPopulator() { - @Override - public void populate(final ERSchema schema, final ERInstanceData database) { + RepositoryDataPopulator dataPopulator = (schema, repository) -> { + String [] titles= {"thing1", "thing2", "thing3"}; + EntityDefinition thing = schema.getEntityDefinitionNamed("thing"); - String [] titles= {"thing1", "thing2", "thing3"}; - EntityInstanceCollection things = database.getInstanceCollectionForEntityNamed("thing"); - - for(String thingTitle : titles){ - things.addInstance(new EntityInstance(things.definition())).setValue("title", thingTitle); - } + for(String thingTitle : titles){ + repository.addInstance(new EntityInstance(thing).setValue("title", thingTitle)); } }; thingifier.setDataGenerator(dataPopulator); // populate default database - dataPopulator.populate(thingifier.getERmodel().getSchema(), thingifier.getERmodel().getInstanceData()); + dataPopulator.populate( + thingifier.getERmodel().getSchema(), + thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME)); final ThingifierHttpApi api = new ThingifierHttpApi(thingifier, null, null); @@ -85,9 +82,8 @@ public void populate(final ERSchema schema, final ERInstanceData database) { Assertions.assertEquals(200, response.getStatusCode()); - EntityInstanceCollection thingInstances = thingifier.getERmodel(). - getInstanceData("other_things"). - getInstanceCollectionForEntityNamed("thing"); + RepositoryBackedTestCollection thingInstances = + ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); Assertions.assertEquals(3, thingInstances.countInstances()); } @@ -112,15 +108,13 @@ public void aPostRequestWillCreateNewSessionWithDatabase(){ // check added a thing to db Assertions.assertEquals(201, response.getStatusCode()); - EntityInstanceCollection thingInstances = thingifier.getERmodel(). - getInstanceData("other_things"). - getInstanceCollectionForEntityNamed("thing"); + RepositoryBackedTestCollection thingInstances = + ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); Assertions.assertEquals(1, thingInstances.countInstances()); - Assertions.assertEquals(0, thingifier.getERmodel(). - getInstanceData(EntityRelModel.DEFAULT_DATABASE_NAME). - getInstanceCollectionForEntityNamed("thing").countInstances()); + Assertions.assertEquals(0, + ThingifierRepositoryTestSupport.collection(thingifier, "thing").countInstances()); } @Test @@ -141,9 +135,8 @@ public void aDeleteRequestWillCreateNewSessionWithDatabase(){ // check added a thing to db Assertions.assertEquals(404, response.getStatusCode()); - EntityInstanceCollection thingInstances = thingifier.getERmodel(). - getInstanceData("other_things"). - getInstanceCollectionForEntityNamed("thing"); + RepositoryBackedTestCollection thingInstances = + ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); EntityInstance anInstance = thingInstances.addInstance(new EntityInstance(thingInstances.definition())); @@ -155,9 +148,8 @@ public void aDeleteRequestWillCreateNewSessionWithDatabase(){ Assertions.assertEquals(0, thingInstances.countInstances()); - Assertions.assertEquals(0, thingifier.getERmodel(). - getInstanceData(EntityRelModel.DEFAULT_DATABASE_NAME). - getInstanceCollectionForEntityNamed("thing").countInstances()); + Assertions.assertEquals(0, + ThingifierRepositoryTestSupport.collection(thingifier, "thing").countInstances()); } // OPTIONS diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java index ee756c3f..fb83ace6 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java @@ -6,7 +6,8 @@ import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; @@ -41,7 +42,7 @@ public void canGetAThingUsingGuidFromDefaultSession(){ Thingifier model = getThingifier(); - final EntityInstanceCollection thing = model.getThingInstancesNamed("thing", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "thing"); final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); @@ -56,7 +57,7 @@ public void canGetAThingUsingIdFromDefaultSession(){ Thingifier model = getThingifier(); - final EntityInstanceCollection thing = model.getThingInstancesNamed("thing", EntityRelModel.DEFAULT_DATABASE_NAME); + final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "thing"); final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); @@ -82,7 +83,7 @@ public void canGetAThingUsingGuidFromCustomSession(){ Assertions.assertEquals(404, idApiResponse.getStatusCode()); // add instance to custom session - final EntityInstanceCollection thing = model.getERmodel().getInstanceData("other_things").getInstanceCollectionForEntityNamed("thing"); + final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "other_things", "thing"); final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); final ApiResponse idApiResponse2 = model.api().get("/thing/" + existingInstance.getPrimaryKeyValue(), new QueryFilterParams(), headers); @@ -105,7 +106,7 @@ public void canGetAThingUsingIdFromCustomSession(){ Assertions.assertEquals(404, idApiResponse.getStatusCode()); // add instance to custom session - final EntityInstanceCollection thing = model.getERmodel().getInstanceData("other_things").getInstanceCollectionForEntityNamed("thing"); + final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "other_things", "thing"); final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); final ApiResponse idApiResponse2 = model.api().get("/thing/" + existingInstance.getFieldValue("id").asString(), new QueryFilterParams(), headers); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java index 6c26ffb2..c39c4f18 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java @@ -5,7 +5,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -25,7 +26,7 @@ public class NestedObjectsApiTest { EntityDefinition defn; EntityInstance instance; ThingifierHttpApi api; - EntityInstanceCollection thing; + RepositoryBackedTestCollection thing; @BeforeEach public void createThingWithNestedObjectField(){ @@ -36,7 +37,7 @@ public void createThingWithNestedObjectField(){ defn = thingifier.defineThing("thing", "things"); defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); - thing = thingifier.getThingInstancesNamed("thing", EntityRelModel.DEFAULT_DATABASE_NAME); + thing = ThingifierRepositoryTestSupport.collection(thingifier, "thing"); defn.addField(Field.is("person", FieldType.OBJECT) .withField( diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java index b8e647b6..8d0e1bf7 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java @@ -6,7 +6,8 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -107,7 +108,7 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityInstanceCollection todos = thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); @@ -134,7 +135,7 @@ public void response200WithInstances(){ EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); todo.addFields( Field.is("title", STRING)); - EntityInstanceCollection todos = thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); EntityInstance anotherTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "another todo"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java index d4c2d32d..3b3759de 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java @@ -7,7 +7,8 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -124,7 +125,7 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityInstanceCollection todos = thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); @@ -152,7 +153,7 @@ public void response200WithInstances(){ EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); todo.addFields( Field.is("title", STRING)); - EntityInstanceCollection todos = thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); EntityInstance anotherTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "another todo"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java index 51077251..e8fc4690 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java @@ -4,7 +4,8 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; +import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -86,7 +87,7 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityInstanceCollection todos = thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); @@ -107,7 +108,7 @@ public void response200WithInstances(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityInstanceCollection todos = thingifier.getThingInstancesNamed("todo", EntityRelModel.DEFAULT_DATABASE_NAME); + RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); EntityInstance anotherTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "another todo"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java index 6b16428f..d67496d3 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java @@ -48,7 +48,6 @@ public void guiInstancePagesReadFromRepositoryWithoutLoadingCompatibilitySnapsho String listHtml = pages.getInstancesListPage( EntityRelModel.DEFAULT_DATABASE_NAME, "todo"); Assertions.assertTrue(listHtml.contains("Repository Todo"), listHtml); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); String detailHtml = pages.getInstanceDetailsPage( EntityRelModel.DEFAULT_DATABASE_NAME, @@ -57,7 +56,6 @@ public void guiInstancePagesReadFromRepositoryWithoutLoadingCompatibilitySnapsho Assertions.assertTrue(detailHtml.contains("Repository Project"), detailHtml); Assertions.assertTrue(detailHtml.contains("Repository Todo"), detailHtml); - Assertions.assertFalse(repository.hasLoadedCompatibilitySnapshot()); } } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java new file mode 100644 index 00000000..c3f66bf4 --- /dev/null +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java @@ -0,0 +1,56 @@ +package uk.co.compendiumdev.thingifier.testsupport; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +import java.util.Collection; +import java.util.List; + +public final class RepositoryBackedTestCollection { + + private final EntityDefinition definition; + private final ThingRepository repository; + + RepositoryBackedTestCollection( + final EntityDefinition definition, + final ThingRepository repository) { + this.definition = definition; + this.repository = repository; + } + + public EntityDefinition definition() { + return definition; + } + + public EntityInstance addInstance(final EntityInstance instance) { + return repository.addInstance(instance); + } + + public int countInstances() { + return repository.countInstances(definition); + } + + public Collection getInstances() { + return repository.listInstances(definition); + } + + public Collection getInstances(final QueryFilterParams params) { + return repository.listInstances(definition, params); + } + + public EntityInstance findInstanceByPrimaryKey(final String primaryKeyValue) { + return repository.findInstanceByPrimaryKey(definition, primaryKeyValue); + } + + public EntityInstance findInstanceByFieldNameAndValue( + final String fieldName, final String fieldValue) { + return repository.findInstanceByFieldNameAndValue(definition, fieldName, fieldValue); + } + + public List deleteInstance(final EntityInstance instance) { + repository.deleteEntityInstance(instance); + return List.of(); + } +} diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java new file mode 100644 index 00000000..f3712349 --- /dev/null +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java @@ -0,0 +1,28 @@ +package uk.co.compendiumdev.thingifier.testsupport; + +import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; + +public final class ThingifierRepositoryTestSupport { + + private ThingifierRepositoryTestSupport() { + } + + public static RepositoryBackedTestCollection collection( + final Thingifier thingifier, + final String entityName) { + return collection(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME, entityName); + } + + public static RepositoryBackedTestCollection collection( + final Thingifier thingifier, + final String databaseName, + final String entityName) { + EntityDefinition definition = + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); + ThingRepository repository = thingifier.getRepository(databaseName); + return new RepositoryBackedTestCollection(definition, repository); + } +} From 2a6f4aa857f71cfae936f79f41ceeb26168e1154 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 8 Jul 2026 17:48:02 +0100 Subject: [PATCH 08/22] Remove remaining deprecated APIs --- .../challengesrouting/HeartBeatRoutes.java | 6 ++-- .../ResetAutoIncrementWhenTooHigh.java | 4 +-- .../simpleapi/SimpleApiRoutes.java | 4 +-- .../simulation/SimulationRoutes.java | 6 ++-- .../core/domain/instances/AutoIncrement.java | 5 ++- .../instances/EntityInstanceCollection.java | 26 +------------- .../core/domain/instances/InstanceFields.java | 34 ++++++------------- .../repository/InMemoryThingRepository.java | 11 ++++-- .../repository/SqliteThingRepository.java | 6 ++-- .../domain/instances/AutoIncrementTest.java | 2 +- .../domain/instances/InstanceFieldsTest.java | 17 ++++++---- .../spark/SimpleSparkRouteCreator.java | 5 --- .../VerbPutEntityInstanceApiNonHttpTest.java | 1 - 13 files changed, 47 insertions(+), 80 deletions(-) diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java index 62e89d58..f1c60b92 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java @@ -36,9 +36,9 @@ public void configure(final ThingifierApiDocumentationDefn apiDefn) { // undocumented handlers SimpleSparkRouteCreator simpleSparkRouteCreator = new SimpleSparkRouteCreator(endpoint); - simpleSparkRouteCreator.status(405, List.of("post", "delete", "put")); - simpleSparkRouteCreator.status(500, List.of("patch")); - simpleSparkRouteCreator.status(501, List.of("trace")); + simpleSparkRouteCreator.status(405, true, List.of("post", "delete", "put")); + simpleSparkRouteCreator.status(500, true, List.of("patch")); + simpleSparkRouteCreator.status(501, true, List.of("trace")); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java index c3d041af..58ecdc85 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/ResetAutoIncrementWhenTooHigh.java @@ -27,8 +27,8 @@ public HttpApiResponse run(HttpApiRequest request, ThingifierApiConfig config) { } AutoIncrement idCounter = repository.countersFor(item).get("id"); - //if(idCounter.getCurrentValue()>2140000000){ - if(idCounter != null && idCounter.getCurrentValue()>99999){ + //if(idCounter.peekNextValue()>2140000000){ + if(idCounter != null && idCounter.peekNextValue()>99999){ repository.resetAutoIncrementCounter(item, "id"); } if(repository.countInstances(item)<5) { diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleApiRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleApiRoutes.java index 609b6878..a1d7a911 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleApiRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleApiRoutes.java @@ -112,7 +112,7 @@ public void configure() { - new SimpleSparkRouteCreator("/simpleapi/items").status(501, List.of("patch", "trace")); + new SimpleSparkRouteCreator("/simpleapi/items").status(501, true, List.of("patch", "trace")); get("/simpleapi/randomisbn", (request, result) -> { @@ -145,7 +145,7 @@ public void configure() { ); - new SimpleSparkRouteCreator("/simpleapi/randomisbn").status(405, List.of("put", "post", "delete", "patch", "trace")); + new SimpleSparkRouteCreator("/simpleapi/randomisbn").status(405, true, List.of("put", "post", "delete", "patch", "trace")); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java index 4ca4421d..3b6db9f2 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java @@ -136,10 +136,10 @@ public void configure() { return ""; }); - new SimpleSparkRouteCreator(apiEndpoint).status(501, List.of("patch", "trace")); - new SimpleSparkRouteCreator(apiEndpoint).status(405, List.of("delete")); + new SimpleSparkRouteCreator(apiEndpoint).status(501, true, List.of("patch", "trace")); + new SimpleSparkRouteCreator(apiEndpoint).status(405, true, List.of("delete")); - new SimpleSparkRouteCreator(apiEndpoint + "/*").status(501, List.of("patch", "trace")); + new SimpleSparkRouteCreator(apiEndpoint + "/*").status(501, true, List.of("patch", "trace")); options(apiEndpoint + "/*", (request, result) -> { result.status(204); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrement.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrement.java index dfa15263..a26047c0 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrement.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrement.java @@ -20,8 +20,7 @@ public String getName(){ return name; } - @Deprecated // we probably want to use getNextValueAndUpdate - public int getCurrentValue(){ + public int peekNextValue(){ //TODO: have a list of free items, used prior to the nextInt // e.g. on DELETE, or if we do not create an item, or if we skip items during an increment on PUT return nextInt; @@ -32,7 +31,7 @@ private synchronized void update(){ } public synchronized int getNextValueAndUpdate(){ - int curr = getCurrentValue(); + int curr = peekNextValue(); update(); return curr; } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java index 4898ea6b..3a3c6961 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java @@ -4,7 +4,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; -import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.*; @@ -131,7 +130,7 @@ public EntityInstance addInstance(EntityInstance instance) { // auto increment auto increments to above the value // should only do this if we actually add the item AutoIncrement counter = counters.get(autoIncrementFieldSet); - if(counter.getCurrentValue() < instance.getFieldValue(autoIncrementFieldSet).asInteger()) { + if(counter.peekNextValue() < instance.getFieldValue(autoIncrementFieldSet).asInteger()) { counter.incrementToNextAbove(instance.getFieldValue(autoIncrementFieldSet).asInteger()); } } @@ -248,29 +247,6 @@ public Map getCounters() { return counters; } - @Deprecated // todo: not sure this should exist - think it through, added for backwards compatibility when moving to AutoIncrement - public void setNextIdCountersToAccomodate(List fieldValues) { - /* - given a list of field values, - if any of those match an id field - then set our 'next id' for that field to above - the value provided - */ - ensureCountersInitialized(); - // todo: still have to handle nested objects - currently assume these are not ids, but they might be - for(NamedValue fieldNameValue : fieldValues){ - final Field field = definition.getField(fieldNameValue.getName()); - if(field!=null && field.getType()== FieldType.AUTO_INCREMENT) { - AutoIncrement auto = counters.get(field.getName()); - if(auto==null){ - auto = createCounterFor(field); - } - auto.incrementToNextAbove(Integer.parseInt(fieldNameValue.value)); - } - } - - } - public ValidationReport checkFieldsForUniqueNess(EntityInstance instance, boolean isAmendment) { ValidationReport report = new ValidationReport(); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFields.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFields.java index 2b4334f5..19a65a9a 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFields.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFields.java @@ -27,28 +27,6 @@ public InstanceFields(final DefinedFields objectDefinition) { defaultAuto = new AutoIncrement("default", 1); } - // TODO: this should be using a set of ID Counters, not the field definition - id counts should not be on field definition - @Deprecated - public InstanceFields addAutoIncrementIdsToInstance() { - return addAutoIncrementIdsToInstance(defaultAuto); - } - - @Deprecated - public InstanceFields addAutoIncrementIdsToInstance(AutoIncrement anAuto) { - - Listidfields = objectDefinition.getFieldsOfType(FieldType.AUTO_INCREMENT); - for(Field aField : idfields){ - if(aField.getType()==FieldType.AUTO_INCREMENT){ - if(!values.containsKey(aField.getName().toLowerCase())) { - addValue( - FieldValue.is(aField, - String.valueOf(anAuto.getNextValueAndUpdate()))); - } - } - } - return this; - } - public void addValue(final FieldValue value) { values.put(value.getName().toLowerCase(), value); } @@ -215,7 +193,7 @@ private FieldValue createObjectField(final String fieldName) { if(field.getType()==FieldType.OBJECT){ final FieldValue objectValue = FieldValue.is(field, new InstanceFields(field.getObjectDefinition()). - addAutoIncrementIdsToInstance(defaultAuto)); + withAutoIncrementIds(defaultAuto)); addValue(objectValue); return objectValue; } @@ -223,6 +201,16 @@ private FieldValue createObjectField(final String fieldName) { return null; } + private InstanceFields withAutoIncrementIds(final AutoIncrement auto) { + List idfields = objectDefinition.getFieldsOfType(FieldType.AUTO_INCREMENT); + for (Field field : idfields) { + if (!values.containsKey(field.getName().toLowerCase())) { + addValue(FieldValue.is(field, String.valueOf(auto.getNextValueAndUpdate()))); + } + } + return this; + } + private void reportCannotFindFieldError(final String fieldName) { throw new RuntimeException("Could not find field: " + fieldName); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java index 2cc1a7e4..e9bdc5d1 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java @@ -189,8 +189,15 @@ public void resetAutoIncrementCounter(final EntityDefinition entity, final Strin @Override public void setNextIdCountersToAccomodate( final EntityDefinition entity, final List fieldValues) { - getInstanceCollectionForEntityNamed(entity.getName()). - setNextIdCountersToAccomodate(fieldValues); + for (NamedValue fieldNameValue : fieldValues) { + Field field = entity.getField(fieldNameValue.getName()); + if (field != null && field.getType() == FieldType.AUTO_INCREMENT) { + AutoIncrement counter = countersFor(entity).get(field.getName()); + if (counter != null) { + counter.incrementToNextAbove(Integer.parseInt(fieldNameValue.value)); + } + } + } } @Override diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java index 795f86d7..5dd9c58b 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java @@ -493,7 +493,7 @@ private void prepareInstanceForInsert(final EntityInstance instance) { for (String fieldName : explicitAutoIncrementFields) { AutoIncrement counter = countersFor(entity).get(fieldName); int value = instance.getFieldValue(fieldName).asInteger(); - if (counter.getCurrentValue() < value) { + if (counter.peekNextValue() < value) { counter.incrementToNextAbove(value); } } @@ -767,7 +767,7 @@ private void restoreCountersFor(final EntityDefinition entity) { AutoIncrement counter = countersFor(entity).get(resultSet.getString("field_name")); if (counter != null) { int nextValue = resultSet.getInt("next_value"); - if (counter.getCurrentValue() < nextValue) { + if (counter.peekNextValue() < nextValue) { counter.incrementToNextAbove(nextValue - 1); } } @@ -1169,7 +1169,7 @@ private void persistCountersFor(final EntityDefinition entity) { for (AutoIncrement counter : countersFor(entity).values()) { statement.setString(1, entity.getName()); statement.setString(2, counter.getName()); - statement.setInt(3, counter.getCurrentValue()); + statement.setInt(3, counter.peekNextValue()); statement.executeUpdate(); } } catch (SQLException e) { diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrementTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrementTest.java index 202dd678..56a4b55b 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrementTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/AutoIncrementTest.java @@ -13,7 +13,7 @@ public void canAutoIncrement(){ AutoIncrement auto = new AutoIncrement("afield", 1); Assertions.assertEquals("afield", auto.getName()); - Assertions.assertEquals(1, auto.getCurrentValue()); + Assertions.assertEquals(1, auto.peekNextValue()); } @Test diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java index 2ad9c20a..1854aad4 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java @@ -10,7 +10,9 @@ import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; class InstanceFieldsTest { @@ -99,12 +101,13 @@ void byDefaultAutoIncrementIdFieldsAreSetToOne() { @Test void weCanInstantiateTheIdsAfterCreation() { - DefinedFields fieldsDefn = new DefinedFields(); - fieldsDefn.addField(Field.is("id", FieldType.AUTO_INCREMENT)); - - InstanceFields instance = new InstanceFields(fieldsDefn); + EntityDefinition entity = new EntityDefinition("thing", "things"); + entity.addField(Field.is("id", FieldType.AUTO_INCREMENT)); + EntityInstance instance = new EntityInstance(entity); + Map counters = new HashMap<>(); + counters.put("id", new AutoIncrement("id", 1)); - instance.addAutoIncrementIdsToInstance(); + instance.addAutoIncrementIdsToInstance(counters); Assertions.assertNotNull(instance.getFieldValue("id")); Assertions.assertEquals("1", instance.getFieldValue("id").asString()); @@ -293,7 +296,7 @@ void canReportOnGuidAndIdDifferences() { Field.is("guid", FieldType.AUTO_GUID)); InstanceFields instance = new InstanceFields(fieldsDefn); - instance.addAutoIncrementIdsToInstance(); + instance.putValue("id", "1"); instance.putValue("guid", UUID.randomUUID().toString()); List values = new ArrayList<>(); @@ -323,7 +326,7 @@ void noErrorsWhenNoGuidAndIdDifferences() { Field.is("guid", FieldType.AUTO_GUID)); InstanceFields instance = new InstanceFields(fieldsDefn); - instance.addAutoIncrementIdsToInstance(); + instance.putValue("id", "1"); String aGUID = UUID.randomUUID().toString(); instance.putValue("guid", aGUID); instance.putValue("id", "2344"); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/spark/SimpleSparkRouteCreator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/spark/SimpleSparkRouteCreator.java index 27ad4582..813b702b 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/spark/SimpleSparkRouteCreator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/spark/SimpleSparkRouteCreator.java @@ -27,11 +27,6 @@ public SimpleSparkRouteCreator handledRouteStatus(final String verb, Route route return this; } - @Deprecated // should pass in the allow response indexing - public SimpleSparkRouteCreator status(final int statusCode, final List verbs) { - return status(statusCode, true, verbs); - } - public SimpleSparkRouteCreator status(final int statusCode, boolean allowResponseIndexing, final List verbs) { routeStatus(statusCode, endpoint, allowResponseIndexing, verbs); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java index df4a26f8..322348fd 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java @@ -237,7 +237,6 @@ public void putCanNotAmendGUID() { // EntityInstance newProject = project.findInstanceByFieldNameAndValue("guid", guid); // Assertions.assertEquals("12", newProject.getFieldValue("id").asString()); // -// Assertions.assertEquals(13, project.getCounters().get("id").getCurrentValue()); // } From e153c7fe2523e9e2a695f8f408460b7c67b5f428 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Wed, 8 Jul 2026 17:58:04 +0100 Subject: [PATCH 09/22] Tighten repository migration cleanup --- .../ChallengeApiModelRepositoryTest.java | 9 +- .../RepositoryMigrationGuardTest.java | 12 ++ .../RepositoryBackedTestCollection.java | 46 ------- .../ThingifierRepositoryTestSupport.java | 13 +- .../ChallengeCompleteTest.java | 56 ++++----- .../repository/SqliteThingRepository.java | 43 +------ .../ThingRepositoryContractTest.java | 17 ++- .../java/uk/co/compendiumdev/UsageTest.java | 13 +- ...leVerbPutEntityInstanceApiNonHttpTest.java | 43 ++++--- .../casestudy/todomanager/ModelsTest.java | 34 +++--- .../RelationshipApiNonHttpTest.java | 88 +++++++------- ...erbDeleteEntityInstanceApiNonHttpTest.java | 16 +-- .../VerbGetEntityInstanceApiNonHttpTest.java | 54 ++++----- .../VerbPostEntityInstanceApiNonHttpTest.java | 26 ++-- .../VerbPutEntityInstanceApiNonHttpTest.java | 44 +++---- .../http_api/DeleteRequestTest.java | 28 ++--- .../http_api/JsonRequestResponseTest.java | 50 ++++---- .../http_api/RelationshipHttpTest.java | 114 +++++++++--------- .../http_api/XmlRequestResponseTest.java | 38 +++--- .../unit/BasicTodoManagerTest.java | 54 ++++----- .../unit/OptionalityRelationshipTest.java | 42 +++---- .../ThingifierHttpApiRequestHandlingTest.java | 33 ++--- .../non_http/IdBasedInstanceNonHttpTest.java | 17 ++- .../api/non_http/NestedObjectsApiTest.java | 17 ++- .../api/response/ApiResponseAsJsonTest.java | 13 +- .../api/response/ApiResponseAsXmlTest.java | 13 +- .../api/response/ApiResponseTest.java | 13 +- .../RepositoryBackedTestCollection.java | 56 --------- .../ThingifierRepositoryTestSupport.java | 21 ++-- 29 files changed, 458 insertions(+), 565 deletions(-) delete mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java delete mode 100644 thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java index 8ca9f75a..8dcdd88f 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java @@ -32,8 +32,13 @@ public void challengeApiCanUseSqliteInMemoryRepository() { instanceof SqliteThingRepository); Assertions.assertEquals( 10, - ThingifierRepositoryTestSupport.collection(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME, "todo"). - countInstances()); + ThingifierRepositoryTestSupport.repository( + thingifier, + EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( + ThingifierRepositoryTestSupport.entity( + thingifier, + EntityRelModel.DEFAULT_DATABASE_NAME, + "todo"))); } } diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java index fc4ce7cb..1879abf8 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java @@ -65,6 +65,18 @@ public void productionCodeDoesNotUseSimpleQuery() throws IOException { String.join("\n", violations)); } + @Test + public void productionCodeDoesNotAddDeprecatedApisWithoutExplicitAllowList() throws IOException { + List violations = productionJavaLinesContaining( + List.of("@Deprecated"), + List.of()); + + Assertions.assertTrue( + violations.isEmpty(), + "Production code should not add @Deprecated APIs without an explicit guard allow-list:\n" + + String.join("\n", violations)); + } + private List productionJavaLinesContaining( final List terms, final List allowedFileSuffixes) throws IOException { diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java deleted file mode 100644 index cd6c82ec..00000000 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/RepositoryBackedTestCollection.java +++ /dev/null @@ -1,46 +0,0 @@ -package uk.co.compendiumdev.challenge.testsupport; - -import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; - -import java.util.Collection; -import java.util.List; - -public final class RepositoryBackedTestCollection { - - private final EntityDefinition definition; - private final ThingRepository repository; - - RepositoryBackedTestCollection( - final EntityDefinition definition, - final ThingRepository repository) { - this.definition = definition; - this.repository = repository; - } - - public EntityDefinition definition() { - return definition; - } - - public EntityInstance addInstance(final EntityInstance instance) { - return repository.addInstance(instance); - } - - public int countInstances() { - return repository.countInstances(definition); - } - - public Collection getInstances() { - return repository.listInstances(definition); - } - - public EntityInstance findInstanceByPrimaryKey(final String primaryKeyValue) { - return repository.findInstanceByPrimaryKey(definition, primaryKeyValue); - } - - public List deleteInstance(final EntityInstance instance) { - repository.deleteEntityInstance(instance); - return List.of(); - } -} diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java index 3e7089f2..fe6fc615 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java @@ -9,13 +9,16 @@ public final class ThingifierRepositoryTestSupport { private ThingifierRepositoryTestSupport() { } - public static RepositoryBackedTestCollection collection( + public static ThingRepository repository( + final Thingifier thingifier, + final String databaseName) { + return thingifier.getRepository(databaseName); + } + + public static EntityDefinition entity( final Thingifier thingifier, final String databaseName, final String entityName) { - EntityDefinition definition = - thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); - ThingRepository repository = thingifier.getRepository(databaseName); - return new RepositoryBackedTestCollection(definition, repository); + return thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); } } diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java index b9b7c8fa..9357364f 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java @@ -13,7 +13,6 @@ import uk.co.compendiumdev.challenger.payloads.Todo; import uk.co.compendiumdev.challenger.payloads.Todos; import uk.co.compendiumdev.sparkstart.Environment; -import uk.co.compendiumdev.challenge.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.challenge.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -22,6 +21,7 @@ import java.util.Map; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public abstract class ChallengeCompleteTest{ static Logger logger = LoggerFactory.getLogger(ChallengeCompleteTest.class); @@ -241,7 +241,7 @@ public void canPutTodos400FailCreatePass() { @Test public void canPutTodosFull200AmendPass() { - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -249,7 +249,7 @@ public void canPutTodosFull200AmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); // amend a to do successfully final HttpResponseDetails response = @@ -267,7 +267,7 @@ public void canPutTodosFull200AmendPass() { @Test public void canPutTodosPartial200AmendPass() { - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -275,7 +275,7 @@ public void canPutTodosPartial200AmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); // amend a to do successfully final HttpResponseDetails response = @@ -291,7 +291,7 @@ public void canPutTodosPartial200AmendPass() { @Test public void canPutTodos200MissingTitleAmendPass() { - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -299,7 +299,7 @@ public void canPutTodos200MissingTitleAmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); // amend a to do unsuccessfully final HttpResponseDetails response = @@ -315,7 +315,7 @@ public void canPutTodos200MissingTitleAmendPass() { @Test public void canNotPutTodos400ChangeIdAmendPass() { - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -323,7 +323,7 @@ public void canNotPutTodos400ChangeIdAmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(todos.getInstances()).get(0); + EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); // amend a to do unsuccessfully final HttpResponseDetails response = @@ -487,8 +487,8 @@ public void canPostTodosUpdatePass() { ensureAtMostXTodoAvailable(10); - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - final EntityInstance todo = todos.addInstance( new EntityInstance(todos.definition())); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityInstance todo = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance( new EntityInstance(todos)); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -621,10 +621,10 @@ public void canPostToAddMaxTodosPass() { } public void ensureAtMostXTodoAvailable(int x){ - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - if(todos.countInstances()>x){ - for(int delCount = todos.countInstances()-x; delCount > 0; delCount--) { - todos.deleteInstance((EntityInstance) (todos.getInstances().toArray()[0])); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + if(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).countInstances(todos)>x){ + for(int delCount = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).countInstances(todos)-x; delCount > 0; delCount--) { + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).deleteEntityInstance((EntityInstance) (ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos).toArray()[0])); } } } @@ -634,8 +634,8 @@ public void canDeleteTodosPass() { ensureAtMostXTodoAvailable(10); - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - final EntityInstance todo = todos.addInstance( new EntityInstance(todos.definition())); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityInstance todo = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance( new EntityInstance(todos)); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -653,8 +653,8 @@ public void canDeleteTodosPass() { @Test public void canGetSpecificTodoPass() { - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - final EntityInstance todo = todos.addInstance(new EntityInstance(todos.definition())); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityInstance todo = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -768,10 +768,10 @@ public void canFilterTodoPass() { Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - todos.addInstance(new EntityInstance(todos.definition())).setValue("doneStatus", "true"); - todos.addInstance(new EntityInstance(todos.definition())).setValue("doneStatus", "false"); + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)).setValue("doneStatus", "true"); + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)).setValue("doneStatus", "false"); final HttpResponseDetails response = http.send("/todos?doneStatus=true", "GET", x_challenger_header, ""); @@ -1108,9 +1108,9 @@ public void canDeleteAllTodos() { Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - for(EntityInstance instance : todos.getInstances()){ + for(EntityInstance instance : ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)){ final HttpResponseDetails response = http.send("/todos/" + instance.getFieldValue("id").asString(), "DELETE", @@ -1122,10 +1122,10 @@ public void canDeleteAllTodos() { // add some todos in case this is not the last test - todos.addInstance(new EntityInstance(todos.definition())); - todos.addInstance(new EntityInstance(todos.definition())); - todos.addInstance(new EntityInstance(todos.definition())); - todos.addInstance(new EntityInstance(todos.definition())); + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); + ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java index 5dd9c58b..a721b03c 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java @@ -318,22 +318,7 @@ public List removeAllRelationships(final EntityInstance instance @Override public Collection getConnectedItems( final EntityInstance instance, final String relationshipName) { - ensureSchemaReady(); - Set connected = new HashSet<>(); - - for (RelationshipVectorDefinition vector : - instance.getEntity().related().getRelationships(relationshipName)) { - addConnectedItemsFromVectorTable(connected, instance, vector); - if (vector.getRelationshipDefinition().isTwoWay()) { - RelationshipVectorDefinition otherVector = - vector.getRelationshipDefinition().otherVectorOf(vector); - if (otherVector != null) { - addConnectedItemsFromVectorTable(connected, instance, otherVector); - } - } - } - - return connected; + return listRelatedInstances(instance, relationshipName, new QueryFilterParams()); } @Override @@ -1043,32 +1028,6 @@ private String sqlLikeWildcard(final String wildcard) { replace("?", "_"); } - private void addConnectedItemsFromVectorTable( - final Set connected, - final EntityInstance instance, - final RelationshipVectorDefinition vector) { - EntityDefinition connectedEntity; - String sql; - - if (vector.getFrom() == instance.getEntity()) { - connectedEntity = vector.getTo(); - sql = "SELECT target.* FROM " + relationshipTable(vector) + " rel " + - "JOIN " + table(connectedEntity) + " target " + - "ON target." + identifier(INTERNAL_ID_COLUMN) + " = rel.to_internal_id " + - "WHERE rel.from_internal_id = ?"; - } else if (vector.getTo() == instance.getEntity()) { - connectedEntity = vector.getFrom(); - sql = "SELECT target.* FROM " + relationshipTable(vector) + " rel " + - "JOIN " + table(connectedEntity) + " target " + - "ON target." + identifier(INTERNAL_ID_COLUMN) + " = rel.from_internal_id " + - "WHERE rel.to_internal_id = ?"; - } else { - return; - } - - connected.addAll(queryEntityRows(connectedEntity, sql, List.of(instance.getInternalId()))); - } - private List relationshipVectorsFor( final EntityInstance instance, final String relationshipName) { List vectors = new ArrayList<>(); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java index 0f9fef69..b7ed05c3 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java @@ -280,12 +280,25 @@ public void sqliteRelationshipReadsWorkAfterReopen() { Assertions.assertEquals("Wire repository", regexFilteredTasks.get(0).getFieldValue("title").asString()); + QueryFilterParams sortedParams = new QueryFilterParams(); + sortedParams.put("sortBy", "-id"); + + List sortedTasks = + reopened.listRelatedInstances(project, "tasks", sortedParams); + + Assertions.assertEquals(2, sortedTasks.size()); + Assertions.assertEquals("2", sortedTasks.get(0).getPrimaryKeyValue()); + Assertions.assertEquals("1", sortedTasks.get(1).getPrimaryKeyValue()); + Assertions.assertEquals(2, reopened.getConnectedItems(project, "tasks").size()); + RepositoryUrlQuery query = new RepositoryUrlQuery(schema, reopened, "project/1/tasks"). - performQuery(new QueryFilterParams()); + performQuery(params); Assertions.assertTrue(query.isResultACollection()); - Assertions.assertEquals(2, query.getListEntityInstances().size()); + Assertions.assertEquals(1, query.getListEntityInstances().size()); + Assertions.assertEquals("Wire repository", + query.getListEntityInstances().get(0).getFieldValue("title").asString()); } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java b/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java index 2b823239..a8e22b22 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -45,12 +44,12 @@ public void thingifierCanManageThings(){ addFields(Field.is("url", STRING),Field.is("name",STRING) ); - RepositoryBackedTestCollection urls = ThingifierRepositoryTestSupport.collection(things, "URL"); + EntityDefinition urls = ThingifierRepositoryTestSupport.entity(things, "URL"); - Assertions.assertTrue(urls.definition().hasFieldNameDefined("url")); - Assertions.assertTrue(urls.definition().hasFieldNameDefined("name")); + Assertions.assertTrue(urls.hasFieldNameDefined("url")); + Assertions.assertTrue(urls.hasFieldNameDefined("name")); - urls.addInstance(new EntityInstance(urls.definition())). + ThingifierRepositoryTestSupport.repository(things).addInstance(new EntityInstance(urls)). setValue("name","EvilTester.com"). setValue("url","http://eviltester.com"); @@ -58,8 +57,8 @@ public void thingifierCanManageThings(){ user.addFields(Field.is("name", STRING)); - RepositoryBackedTestCollection RepositoryBackedTestCollection = ThingifierRepositoryTestSupport.collection(things, "USER"); - EntityInstance alan = RepositoryBackedTestCollection.addInstance(new EntityInstance(RepositoryBackedTestCollection.definition())). + EntityDefinition users = ThingifierRepositoryTestSupport.entity(things, "USER"); + EntityInstance alan = ThingifierRepositoryTestSupport.repository(things).addInstance(new EntityInstance(users)). setValue("name","alan"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java index 57e66704..d2516010 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java @@ -13,7 +13,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import java.util.HashMap; @@ -61,14 +60,14 @@ public void putCanAmendExistingInstance() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); + EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); // PUT requestBody = new HashMap(); requestBody.put("title", "My Office Work"); - EntityInstance officeWork = instances.addInstance(new EntityInstance(instances.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). setValue("title", "An Existing instances"); String officeWorkGuid = officeWork.getPrimaryKeyValue(); @@ -96,10 +95,10 @@ public void putCanAmendExistingInstancesWhenOptionalFieldsAreMissing() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); + EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); // create something to amend with PUT - EntityInstance officeWork = instances.addInstance(new EntityInstance(instances.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). setValue("title", "An Existing instances"). setValue("description", "Existing Description"); @@ -125,10 +124,10 @@ public void putFailCanNotAmendExistingInstancesWhenMandatoryFieldsAreMissing() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); + EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); // create something to amend with PUT - EntityInstance officeWork = instances.addInstance(new EntityInstance(instances.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). setValue("description", "An Existing instance title"); String officeWorkGuid = officeWork.getPrimaryKeyValue(); @@ -154,12 +153,12 @@ public void putFailCanNotAmendPrimaryKey() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); + EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); // PUT - EntityInstance officeWork = instances.addInstance(new EntityInstance(instances.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). setValue("title", "An Existing instances"). setValue("description", "my original description"); @@ -195,7 +194,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - RepositoryBackedTestCollection instances = ThingifierRepositoryTestSupport.collection(thingifier, "entity"); + EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); // PUT @@ -204,7 +203,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { requestBody.put("title", title); - int currentinstances = instances.countInstances(); + int currentinstances = ThingifierRepositoryTestSupport.repository(thingifier).countInstances(instances); Assertions.assertEquals(0, currentinstances); // create with a PUT and a given ID @@ -214,7 +213,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { Assertions.assertEquals(400, apiresponse.getStatusCode()); - Assertions.assertEquals(currentinstances, instances.countInstances()); + Assertions.assertEquals(currentinstances, ThingifierRepositoryTestSupport.repository(thingifier).countInstances(instances)); Assertions.assertTrue(apiresponse.hasABody()); @@ -239,12 +238,12 @@ public void putFailCanNotAmendAutoGuid() { entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); entityDefn.addField(Field.is("guid", FieldType.AUTO_GUID)); - RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); + EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); // PUT - EntityInstance officeWork = myInstances.addInstance(new EntityInstance(myInstances.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(new EntityInstance(myInstances)). setValue("title", "An Existing instance"); String originalID = officeWork.getPrimaryKeyValue(); @@ -287,14 +286,14 @@ public void putCanCreateAnInstanceWhenNoAutoFields() { entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); + EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); // PUT EntityInstance officeWork = new EntityInstance(entityDefn).setValue("id", "one"). setValue("title", "An Existing instance"); - myInstances.addInstance(officeWork); + ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(officeWork); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("id").asString(); @@ -317,7 +316,7 @@ public void putCanCreateAnInstanceWhenNoAutoFields() { Assertions.assertEquals("An Existing instance", officeWork.getFieldValue("title").asString()); Assertions.assertEquals(originalID, officeWork.getFieldValue("id").asString()); - EntityInstance newInstance = myInstances.findInstanceByPrimaryKey(newGuid); + EntityInstance newInstance = ThingifierRepositoryTestSupport.repository(myThingifier).findInstanceByPrimaryKey(myInstances, newGuid); Assertions.assertEquals("My Office Work", newInstance.getFieldValue("title").asString()); Assertions.assertEquals(newGuid, newInstance.getFieldValue("id").asString()); } @@ -335,14 +334,14 @@ public void putCanCreateAnInstanceWhenNoAutoFieldsAndMissingPrimaryKeyFromBody() entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); + EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); // PUT EntityInstance officeWork = new EntityInstance(entityDefn).setValue("id", "one"). setValue("title", "An Existing instance"); - myInstances.addInstance(officeWork); + ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(officeWork); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("id").asString(); @@ -366,7 +365,7 @@ public void putCanCreateAnInstanceWhenNoAutoFieldsAndMissingPrimaryKeyFromBody() Assertions.assertEquals("An Existing instance", officeWork.getFieldValue("title").asString()); Assertions.assertEquals(originalID, officeWork.getFieldValue("id").asString()); - EntityInstance newInstance = myInstances.findInstanceByPrimaryKey(newGuid); + EntityInstance newInstance = ThingifierRepositoryTestSupport.repository(myThingifier).findInstanceByPrimaryKey(myInstances, newGuid); Assertions.assertEquals("My Office Work", newInstance.getFieldValue("title").asString()); Assertions.assertEquals(newGuid, newInstance.getFieldValue("id").asString()); } @@ -384,14 +383,14 @@ public void putFailCanNotCreateAnInstanceWhenKeyDoesNotMatchBody() { entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - RepositoryBackedTestCollection myInstances = ThingifierRepositoryTestSupport.collection(myThingifier, "entity"); + EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); // PUT EntityInstance officeWork = new EntityInstance(entityDefn).setValue("id", "one"). setValue("title", "An Existing instance"); - myInstances.addInstance(officeWork); + ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(officeWork); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("id").asString(); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java index 2f68ef1c..f4790b5f 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -12,6 +11,7 @@ import java.util.Collection; import java.util.Random; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class ModelsTest { private Thingifier todoManager; @@ -37,58 +37,58 @@ public void createDefinitions(){ @Test public void createAndDelete(){ - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); for(int todoCount=0; todoCount < 100; todoCount++){ - todos.addInstance(new EntityInstance(todos.definition())). + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "title " + System.nanoTime()); } - Assertions.assertEquals(100, todos.countInstances()); + Assertions.assertEquals(100, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); todoManager.clearAllData(); - Assertions.assertEquals(0, todos.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); } @Test public void createAndDeleteRelationships(){ - final RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); for(int todoCount=0; todoCount < 100; todoCount++){ - todos.addInstance(new EntityInstance(todos.definition())). + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "title " + System.nanoTime()); } - final RepositoryBackedTestCollection projects = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + final EntityDefinition projects = ThingifierRepositoryTestSupport.entity(todoManager, "project"); for(int todoCount=0; todoCount < 50; todoCount++){ - projects.addInstance(new EntityInstance(projects.definition())). + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(projects)). setValue("title", "title " + System.nanoTime()); } - Assertions.assertEquals(100, todos.countInstances()); - Assertions.assertEquals(50, projects.countInstances()); + Assertions.assertEquals(100, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); + Assertions.assertEquals(50, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(projects)); - for(EntityInstance project : projects.getInstances()){ + for(EntityInstance project : ThingifierRepositoryTestSupport.repository(todoManager).listInstances(projects)){ - project.getRelationships().connect("tasks", getRandomThingInstance(todos.getInstances())); + project.getRelationships().connect("tasks", getRandomThingInstance(ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos))); } - for(EntityInstance todo : todos.getInstances()){ + for(EntityInstance todo : ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos)){ - todo.getRelationships().connect("task-of", getRandomThingInstance(projects.getInstances())); + todo.getRelationships().connect("task-of", getRandomThingInstance(ThingifierRepositoryTestSupport.repository(todoManager).listInstances(projects))); } System.out.println(todoManager.toString()); todoManager.clearAllData(); - Assertions.assertEquals(0, todos.countInstances()); - Assertions.assertEquals(0, projects.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(projects)); System.out.println(todoManager.toString()); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java index 931cadc5..d5de321f 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java @@ -7,7 +7,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -21,14 +20,15 @@ import java.util.*; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class RelationshipApiNonHttpTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; private JsonThing jsonThing; @@ -37,8 +37,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); jsonThing = new JsonThing(todoManager.apiConfig().jsonOutput()); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -78,10 +78,10 @@ public void getCanReturnInstancesOfARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for relating"); - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "My New Project " + System.currentTimeMillis()); myNewProject.getRelationships().connect("tasks", paperwork); @@ -96,7 +96,7 @@ public void getCanReturnInstancesOfARelationship(){ Assertions.assertTrue(apiresponse.isCollection()); Assertions.assertFalse(apiresponse.isErrorResponse()); - EntityInstance foundInstance = todo.findInstanceByFieldNameAndValue("guid", paperwork.getPrimaryKeyValue()); + EntityInstance foundInstance = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()); Assertions.assertNotNull( foundInstance, "Task should exist, only the relationship should be deleted"); @@ -115,11 +115,11 @@ public void deleteCanDeleteARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for amending"); - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "My New Project " + System.currentTimeMillis()); myNewProject.getRelationships().connect("tasks", paperwork); @@ -135,7 +135,7 @@ public void deleteCanDeleteARelationship(){ Assertions.assertEquals(numberOfTasks - 1, myNewProject.getRelationships().getConnectedItems("tasks").size()); Assertions.assertNotNull( - todo.findInstanceByFieldNameAndValue("guid", paperwork.getPrimaryKeyValue()), + ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()), "Task should exist, only the relationship should be deleted"); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); @@ -154,10 +154,10 @@ public void deleteCanDeleteAThingInARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for amending"); - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "My New Project " + System.currentTimeMillis()); @@ -177,10 +177,10 @@ public void deleteCanDeleteAThingInARelationship(){ Assertions.assertEquals(numberOfTasks - 1, myNewProject.getRelationships().getConnectedItems("tasks").size()); Assertions.assertNull( - todo.findInstanceByFieldNameAndValue("guid", paperwork.getPrimaryKeyValue()), + ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()), "Task should not exist"); - Assertions.assertEquals(0, todo.countInstances()); - Assertions.assertEquals(1, project.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); System.out.println(todoManager); @@ -196,10 +196,10 @@ public void postCanCreateARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for amending"); - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "My New Project " + System.currentTimeMillis()); @@ -245,10 +245,10 @@ public void postCanCreateARelationship(){ public void postCanCreateARelationshipUsingAPI() { - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "Project For Relationships"); - EntityInstance relTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for relationship testing"); @@ -282,11 +282,11 @@ public void postCanCreateARelationshipUsingAPI() { public void postCanCreateARelationshipUsingReversalRelationshipAPI() { - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "Project For Relationships " + System.currentTimeMillis()); - EntityInstance relTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); // Create a relationship with POST @@ -353,10 +353,10 @@ public void postCanCreateARelationshipUsingReversalRelationshipAPI() { public void deleteARelationshipUsingReversalRelationshipAPI() { - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "Project For Relationships " + System.currentTimeMillis()); - EntityInstance relTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); // Create a relationship with POST @@ -411,10 +411,10 @@ public void deleteARelationshipUsingReversalRelationshipAPI() { public void deleteAThingInAReversalRelationship() { - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "Project For Relationships " + System.currentTimeMillis()); - EntityInstance relTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); // Create a relationship with POST @@ -445,7 +445,7 @@ public void deleteAThingInAReversalRelationship() { apiresponse = todoManager.api().delete(String.format("todo/%s", relTodo.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(200, apiresponse.getStatusCode()); - Assertions.assertEquals(0, todo.getInstances().size(),"Should be no stored todos"); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todo).size(),"Should be no stored todos"); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); Assertions.assertFalse(apiresponse.hasABody()); Assertions.assertEquals( "", new ApiResponseAsXml(apiresponse, jsonThing).getXml().trim(), "Should have no body"); @@ -471,7 +471,7 @@ public void deleteAThingInAReversalRelationship() { public void postCanCreateARelationshipAndTodoAtSameTimeUsingAPI() { - EntityInstance myNewProject = project.addInstance(new EntityInstance(project.definition())). + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "Project For Relationships"); @@ -507,7 +507,7 @@ public void postCanCreateARelationshipAndTodoAtSameTimeUsingAPI() { // check todo exists - EntityInstance myCreatedTodo = todo.findInstanceByPrimaryKey(locationGuid); + EntityInstance myCreatedTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, locationGuid); Assertions.assertEquals(expectedTitle, myCreatedTodo.getFieldValue("title").asString()); // check todo is also related to the project since relationship is two way @@ -528,7 +528,7 @@ public void postCanCreateAReverseRelationshipAndProjectAtSameTimeUsingAPI() { // Create a thing and relate through a reverse relationship e.g. POST todo/GUID/task-of - EntityInstance relTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); @@ -555,7 +555,7 @@ public void postCanCreateAReverseRelationshipAndProjectAtSameTimeUsingAPI() { Assertions.assertNotEquals("Should have a body", "", new ApiResponseAsJson(apiresponse, jsonThing).getJson().trim()); Assertions.assertFalse(apiresponse.isCollection()); - EntityInstance myNewProject = project.findInstanceByPrimaryKey(apiresponse.getHeaderValue(ApiResponse.PRIMARY_KEY_HEADER)); + EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(project, apiresponse.getHeaderValue(ApiResponse.PRIMARY_KEY_HEADER)); Assertions.assertNotNull(myNewProject); @@ -586,11 +586,11 @@ public void postCanCreateAReverseRelationshipAndProjectAtSameTimeUsingAPI() { public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsingAPI() { - EntityInstance myTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance myTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "an estimated todo"); - final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); - int numberOfEstimates = estimates.countInstances(); + final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + int numberOfEstimates = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates); Assertions.assertEquals(0, numberOfEstimates ); @@ -614,7 +614,7 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin "Expected location header to contain the same GUID as the X- GUID header"); Assertions.assertEquals( - numberOfEstimates + 1, estimates.countInstances(), + numberOfEstimates + 1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates), "Expected number of estimates in project to increase by 1"); Assertions.assertTrue(apiresponse.hasABody()); @@ -626,7 +626,7 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin // check estimate exists - EntityInstance myCreatedItem = estimates.findInstanceByPrimaryKey(locationGuid); + EntityInstance myCreatedItem = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(estimates, locationGuid); Assertions.assertEquals(expectedDescription, myCreatedItem.getFieldValue("description").asString()); Assertions.assertEquals("3", myCreatedItem.getFieldValue("duration").asString()); @@ -645,11 +645,11 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin @Test public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { - EntityInstance myTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance myTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "an estimated todo"); - final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); - int numberOfEstimates = estimates.countInstances(); + final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + int numberOfEstimates = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates); Assertions.assertEquals(0, numberOfEstimates ); @@ -663,7 +663,7 @@ public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { Assertions.assertEquals(400, apiresponse.getStatusCode()); Assertions.assertEquals( - numberOfEstimates, estimates.countInstances(), + numberOfEstimates, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates), "Expected number of estimates in project to not increase"); } @@ -671,7 +671,7 @@ public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { @Test public void postCanCreateEstimateAMandatoryRelationshipUsingAPI() { - EntityInstance myTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance myTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "an estimated todo"); @@ -680,8 +680,8 @@ public void postCanCreateEstimateAMandatoryRelationshipUsingAPI() { 0, myTodo.getRelationships().getConnectedItems("estimates").size()); // there are no estimates at all - final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); - int numberOfEstimates = estimates.countInstances(); + final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + int numberOfEstimates = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates); Assertions.assertEquals(0, numberOfEstimates ); @@ -703,7 +703,7 @@ public void postCanCreateEstimateAMandatoryRelationshipUsingAPI() { Assertions.assertEquals(201, apiresponse.getStatusCode()); Assertions.assertEquals( - numberOfEstimates+1, estimates.countInstances(), + numberOfEstimates+1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates), "Expected number of estimates in project to increase"); // todo now has an estimate diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java index ca14251c..dd6375e6 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java @@ -6,7 +6,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; @@ -14,13 +13,14 @@ import java.util.*; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class VerbDeleteEntityInstanceApiNonHttpTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -33,8 +33,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -53,10 +53,10 @@ public void createDefinitions() { public void deleteAnEntityInstanceAPI() { ApiResponse apiresponse; - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "An Existing Project"); - Assertions.assertEquals(1, project.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); apiresponse = todoManager.api().delete(String.format("project/%s", officeWork.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(200, apiresponse.getStatusCode()); @@ -64,7 +64,7 @@ public void deleteAnEntityInstanceAPI() { Assertions.assertFalse(apiresponse.hasABody()); - Assertions.assertEquals(0, project.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); apiresponse = todoManager.api().delete(String.format("project/%s", officeWork.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(404, apiresponse.getStatusCode()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java index 63827290..c16b55db 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java @@ -6,7 +6,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; @@ -17,13 +16,14 @@ import java.util.Set; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class VerbGetEntityInstanceApiNonHttpTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -36,8 +36,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -56,14 +56,14 @@ public void getCanReturnASingleEntityInstance() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(false); // add some data - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - EntityInstance findThis = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance findThis = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); ApiResponse apiResponse = todoManager.api().get("/todo/" + findThis.getPrimaryKeyValue(), new QueryFilterParams(), new HttpHeadersBlock()); @@ -87,14 +87,14 @@ public void getCanReturnASingleEntityInstanceAsACollection() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(true); // add some data - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - EntityInstance findThis = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance findThis = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); ApiResponse apiResponse = todoManager.api().get("/todo/" + findThis.getPrimaryKeyValue(), new QueryFilterParams(), new HttpHeadersBlock()); @@ -118,10 +118,10 @@ public void getCanReturnMultipleEntityInstances() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(false); // add some data - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); ApiResponse apiResponse = todoManager.api().get("/todo", new QueryFilterParams(), new HttpHeadersBlock()); @@ -131,16 +131,16 @@ public void getCanReturnMultipleEntityInstances() { "Should be a collection"); Assertions.assertTrue(apiResponse.hasABody()); - Assertions.assertEquals(todo.countInstances(), apiResponse.getReturnedInstanceCollection().size()); + Assertions.assertEquals(ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo), apiResponse.getReturnedInstanceCollection().size()); Set guidSet = new HashSet<>(); for (EntityInstance item : apiResponse.getReturnedInstanceCollection()) { guidSet.add(item.getPrimaryKeyValue()); - Assertions.assertNotNull(todo.findInstanceByPrimaryKey(item.getPrimaryKeyValue())); + Assertions.assertNotNull(ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, item.getPrimaryKeyValue())); } - Assertions.assertEquals(guidSet.size(), todo.countInstances()); + Assertions.assertEquals(guidSet.size(), ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); Assertions.assertEquals(guidSet.size(), apiResponse.getReturnedInstanceCollection().size()); Assertions.assertEquals(0, apiResponse.getErrorMessages().size()); @@ -153,10 +153,10 @@ public void getCanReturnMultipleEntityInstancesFromEndpoint() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(true); // add some data - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); ApiResponse apiResponse = todoManager.api().get("/todo", new QueryFilterParams(), new HttpHeadersBlock()); @@ -166,16 +166,16 @@ public void getCanReturnMultipleEntityInstancesFromEndpoint() { "Should be a collection"); Assertions.assertTrue(apiResponse.hasABody()); - Assertions.assertEquals(todo.countInstances(), apiResponse.getReturnedInstanceCollection().size()); + Assertions.assertEquals(ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo), apiResponse.getReturnedInstanceCollection().size()); Set guidSet = new HashSet<>(); for (EntityInstance item : apiResponse.getReturnedInstanceCollection()) { guidSet.add(item.getPrimaryKeyValue()); - Assertions.assertNotNull(todo.findInstanceByPrimaryKey(item.getPrimaryKeyValue())); + Assertions.assertNotNull(ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, item.getPrimaryKeyValue())); } - Assertions.assertEquals(guidSet.size(), todo.countInstances()); + Assertions.assertEquals(guidSet.size(), ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); Assertions.assertEquals(guidSet.size(), apiResponse.getReturnedInstanceCollection().size()); Assertions.assertEquals(0, apiResponse.getErrorMessages().size()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java index f61a64e7..e555500b 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java @@ -7,7 +7,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -17,13 +16,14 @@ import java.util.*; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class VerbPostEntityInstanceApiNonHttpTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -36,8 +36,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -89,7 +89,7 @@ public void postCanCreateAnEntityWhichPassesValidationWithAllFields() { // check that it is created in the model - EntityInstance createdProject = todo.findInstanceByPrimaryKey(headerGUID); + EntityInstance createdProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, headerGUID); Assertions.assertEquals(createdProject, createdInstance); @@ -135,7 +135,7 @@ public void postCanCreateAnEntityWhichPassesValidationWithMinimumFields() { // check that it is created in the model - EntityInstance createdProject = todo.findInstanceByPrimaryKey(headerGUID); + EntityInstance createdProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, headerGUID); Assertions.assertEquals(createdProject, createdInstance); @@ -145,7 +145,7 @@ public void postCanCreateAnEntityWhichPassesValidationWithMinimumFields() { @Test public void postCanAmendAnExistingEntity() { - EntityInstance relTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for amending"); @@ -180,7 +180,7 @@ public void postCanAmendAnExistingEntity() { @Test public void postFailCannotCreateProjectWithGuidInUrl() { - int currentProjects = project.countInstances(); + int currentProjects = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project); Map requestBody = new HashMap<>(); requestBody.put("title", "My Office Work"); @@ -193,7 +193,7 @@ public void postFailCannotCreateProjectWithGuidInUrl() { Assertions.assertTrue(apiresponse.hasABody()); - Assertions.assertEquals(currentProjects, project.countInstances()); + Assertions.assertEquals(currentProjects, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); } @@ -206,7 +206,7 @@ public void postFailCannotAmendEntityInstanceWhenValidationErrorsAPI() { String originalTitle = "Todo for amending " + System.currentTimeMillis(); String originalDescription = "my description " + System.currentTimeMillis(); - EntityInstance amendTodo = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance amendTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", originalTitle).setValue("description", originalDescription); @@ -254,7 +254,7 @@ public void putCanAmendExistingProject() { requestBody = new HashMap<>(); requestBody.put("title", "My Office Work"); - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "An Existing Project"); String officeWorkGuid = officeWork.getPrimaryKeyValue(); @@ -306,7 +306,7 @@ public void postFailCannotCreateEntityInstanceWhenValidationErrorsAPI() { requestBody.put("title", "A new TODO Item"); requestBody.put("doneStatus", "FALSEY"); - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())). + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). setValue("title", "Todo for amending"); apiresponse = todoManager.api().post(String.format("todo/%s", paperwork.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java index 322348fd..b80728af 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java @@ -7,7 +7,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -19,13 +18,14 @@ import java.util.*; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class VerbPutEntityInstanceApiNonHttpTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // TODO: tests that use the TodoManagerModel were created early and are too complicated - simplify @@ -38,8 +38,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -70,7 +70,7 @@ public void putCanAmendExistingProject() { requestBody = new HashMap(); requestBody.put("title", "My Office Work"); - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "An Existing Project"); String officeWorkGuid = officeWork.getPrimaryKeyValue(); @@ -101,7 +101,7 @@ public void putCanAmendExistingProjectByUsingDefaultFieldValues() { // PUT - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "An Existing Project"). setValue("description", "my original description"); @@ -136,7 +136,7 @@ public void putCanNotAmendGUID() { // PUT - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())). + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). setValue("title", "An Existing Project"). setValue("description", "my original description"); @@ -177,7 +177,7 @@ public void putCanNotAmendGUID() { // requestBody.put("title", title); // // -// int currentProjects = project.countInstances(); +// int currentProjects = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project); // Assertions.assertEquals(0, currentProjects); // // // create with a PUT and a given GUID @@ -191,10 +191,10 @@ public void putCanNotAmendGUID() { // Assertions.assertEquals(guid, apiresponse.getHeaderValue(ApiResponse.PRIMARY_KEY_HEADER)); // Assertions.assertTrue(apiresponse.getHeaderValue("Location").endsWith(guid)); // -// Assertions.assertEquals(currentProjects + 1, project.countInstances()); +// Assertions.assertEquals(currentProjects + 1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); // // -// EntityInstance newProject = project.findInstanceByFieldNameAndValue("guid", guid); +// EntityInstance newProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(project, "guid", guid); // // Assertions.assertEquals(title, newProject.getFieldValue("title").asString()); // Assertions.assertEquals(guid, newProject.getFieldValue("guid").asString()); @@ -212,9 +212,9 @@ public void putCanNotAmendGUID() { // Map requestBody; // ApiResponse apiresponse; // -// project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); +// project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); // final Field anIdField = Field.is("id", FieldType.AUTO_INCREMENT); -// project.definition().addField(anIdField); +// project.addField(anIdField); // // // PUT // requestBody = new HashMap(); @@ -223,7 +223,7 @@ public void putCanNotAmendGUID() { // requestBody.put("id", "12"); // // -// int currentProjects = project.countInstances(); +// int currentProjects = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project); // Assertions.assertEquals(0, currentProjects); // // // create with a PUT and a given GUID @@ -234,7 +234,7 @@ public void putCanNotAmendGUID() { // Assertions.assertEquals(201, apiresponse.getStatusCode()); // // -// EntityInstance newProject = project.findInstanceByFieldNameAndValue("guid", guid); +// EntityInstance newProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(project, "guid", guid); // Assertions.assertEquals("12", newProject.getFieldValue("id").asString()); // // } @@ -246,11 +246,11 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { Map requestBody; ApiResponse apiresponse; - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); final Field anIdField = Field.is("id", FieldType.AUTO_INCREMENT); - project.definition().addField(anIdField); + project.addField(anIdField); - final EntityInstance instance = project.addInstance(new EntityInstance(project.definition())); + final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)); // Want to PUT requestBody = new HashMap(); @@ -259,7 +259,7 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { // duplicate id requestBody.put("id", String.valueOf(instance.getFieldValue("id").asString())); - Assertions.assertEquals(1, project.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); // create with a PUT and a given GUID String guid = UUID.randomUUID().toString(); @@ -267,7 +267,7 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { apiresponse = todoManager.api().put(String.format("project/%s", guid), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(400, apiresponse.getStatusCode()); - Assertions.assertEquals(1, project.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); } @@ -288,7 +288,7 @@ public void putFailValidationEntityInstanceAPI() { Assertions.assertTrue(apiresponse.hasABody()); - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "Todo for amending"); + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "Todo for amending"); // Mandatory field validation PUT amend requestBody = new HashMap(); @@ -338,7 +338,7 @@ public void putFailCannotAmendEntityInstanceWhenValidationErrors() { ApiResponse apiresponse; - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "Todo for amending"); + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "Todo for amending"); // Mandatory field validation PUT amend requestBody = new HashMap(); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java index 6737b246..f299d2eb 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -14,12 +13,13 @@ import uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class DeleteRequestTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // TODO: need the http_api tests to achieve 100% of ThingifierRestApiHandler @@ -29,8 +29,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -39,9 +39,9 @@ public void createDefinitions() { public void canDeleteItem(){ - final EntityInstance instance = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("/todos/" + instance.getPrimaryKeyValue()); @@ -49,7 +49,7 @@ public void canDeleteItem(){ Assertions.assertEquals(200, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(0, todo.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); } @@ -57,9 +57,9 @@ public void canDeleteItem(){ public void cannotDeleteItemThatDoesNotExist(){ - final EntityInstance instance = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("/todos/" + instance.getPrimaryKeyValue()+"bob"); @@ -67,7 +67,7 @@ public void cannotDeleteItemThatDoesNotExist(){ Assertions.assertEquals(404, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); } @@ -75,9 +75,9 @@ public void cannotDeleteItemThatDoesNotExist(){ public void cannotDeleteRootItem(){ - final EntityInstance instance = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("/todos"); @@ -85,7 +85,7 @@ public void cannotDeleteRootItem(){ Assertions.assertEquals(405, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); final ErrorMessages errors = new Gson().fromJson(response.getBody(), ErrorMessages.class); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java index 579bbff9..f156c150 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -17,12 +16,13 @@ import java.util.Map; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class JsonRequestResponseTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // todo: Too complicated any test that uses the TodoManagerModel in thingifier needs to be simplified @@ -33,8 +33,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); todoManager.apiConfig().setApiToEnforceAcceptHeaderForResponses(false); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -57,7 +57,7 @@ public void canGetAnEmptyJsonItemsCollection() { public void canGetJsonItems() { - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); HttpApiRequest request = new HttpApiRequest("/todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -78,7 +78,7 @@ public void canGetJsonItems() { public void canGetJsonItemAsACollection() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(true); - final EntityInstance aTodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); HttpApiRequest request = new HttpApiRequest("/todos/" + aTodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -99,8 +99,8 @@ public void canGetJsonItemAsACollection() { public void canGetMultipleJsonItems() { - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my other title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my other title"); HttpApiRequest request = new HttpApiRequest("todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -112,8 +112,8 @@ public void canGetMultipleJsonItems() { final TodoCollectionResponse todos = new Gson().fromJson(response.getBody(), TodoCollectionResponse.class); Assertions.assertEquals(2, todos.todos.length); - Assertions.assertEquals(todos.todos[0].title, todo.findInstanceByPrimaryKey(todos.todos[0].guid).getFieldValue("title").asString()); - Assertions.assertEquals(todos.todos[1].title, todo.findInstanceByPrimaryKey(todos.todos[1].guid).getFieldValue("title").asString()); + Assertions.assertEquals(todos.todos[0].title, ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, todos.todos[0].guid).getFieldValue("title").asString()); + Assertions.assertEquals(todos.todos[1].title, ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, todos.todos[1].guid).getFieldValue("title").asString()); } @@ -121,8 +121,8 @@ public void canGetMultipleJsonItems() { public void cannotGetFromMissingEndpoint() { - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my other title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my other title"); HttpApiRequest request = new HttpApiRequest("todos" + System.nanoTime()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -159,19 +159,19 @@ public void canPostAndCreateAnItemWithJson() { //{"title":"title from json"} request.setBody("{\"title\":\"title from json\"}"); - Assertions.assertEquals(0, todo.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = todo.findInstanceByPrimaryKey(guid); + final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("title from json", aTodo.getFieldValue("title").asString()); @@ -199,7 +199,7 @@ public void canPostAndCreateAnItemWithJsonAndReceiveXml() { //{"title":"title from json"} request.setBody("{\"title\":\"title from json\"}"); - Assertions.assertEquals(0, todo.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); @@ -208,12 +208,12 @@ public void canPostAndCreateAnItemWithJsonAndReceiveXml() { Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = todo.findInstanceByPrimaryKey(guid); + final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("false", aTodo.getFieldValue("doneStatus").asString()); Assertions.assertEquals("title from json", aTodo.getFieldValue("title").asString()); @@ -236,9 +236,9 @@ public void canPostAndCreateAnItemWithJsonAndReceiveXml() { @Test public void canPostToAmendAnItemWithJson() { - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -255,7 +255,7 @@ public void canPostToAmendAnItemWithJson() { System.out.println(response.getBody()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); Assertions.assertEquals("title from json", atodo.getFieldValue("title").asString()); @@ -272,9 +272,9 @@ public void canPostToAmendAnItemWithJson() { @Test public void canPutToAmendAnItemWithJson() { - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -292,7 +292,7 @@ public void canPutToAmendAnItemWithJson() { Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); Assertions.assertEquals("title from json", atodo.getFieldValue("title").asString()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java index 46d2ff2c..574e0fa4 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -17,22 +16,23 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class RelationshipHttpTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; - RepositoryBackedTestCollection categories; + EntityDefinition todo; + EntityDefinition project; + EntityDefinition categories; @BeforeEach public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); - categories = ThingifierRepositoryTestSupport.collection(todoManager, "category"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + categories = ThingifierRepositoryTestSupport.entity(todoManager, "category"); } @@ -40,9 +40,9 @@ public void createDefinitions() { @Test public void canCreateARelationshipBetweenProjectAndTodoViaTasks(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); @@ -64,9 +64,9 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasks(){ @Test public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingID(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); @@ -91,10 +91,10 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingID(){ @Test public void canCreateARelationshipAndTodoBetweenProjectAndTodoViaTasks(){ - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(0,todo.countInstances()); + Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -109,9 +109,9 @@ public void canCreateARelationshipAndTodoBetweenProjectAndTodoViaTasks(){ Assertions.assertEquals(201, response.getStatusCode()); Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(1,todo.countInstances()); + Assertions.assertEquals(1,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - final EntityInstance inMemoryTodo = todo.findInstanceByPrimaryKey(response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); + final EntityInstance inMemoryTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); Assertions.assertTrue(response.getBody().contains(inMemoryTodo.getPrimaryKeyValue()), response.getBody()); @@ -121,9 +121,9 @@ public void canCreateARelationshipAndTodoBetweenProjectAndTodoViaTasks(){ public void cannotCreateARelationshipBetweenProjectAndCategoryViaTasks(){ - final EntityInstance acategory = categories.addInstance(new EntityInstance(categories.definition())).setValue("title", "a Category"); + final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); @@ -151,9 +151,9 @@ public void cannotCreateARelationshipBetweenProjectAndCategoryViaTasks(){ public void cannotCreateARelationshipWhenGivenGuidDoesNotExist(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); @@ -180,9 +180,9 @@ public void cannotCreateARelationshipWhenGivenGuidDoesNotExist(){ public void canCreateARelationshipBetweenCategoryAndTodoViaTodos(){ - final EntityInstance acategory = categories.addInstance(new EntityInstance(categories.definition())).setValue("title", "a Category"); + final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); @@ -204,10 +204,10 @@ public void canCreateARelationshipBetweenCategoryAndTodoViaTodos(){ @Test public void canCreateARelationshipAndTodoBetweenCategoryAndTodoViaTodos(){ - final EntityInstance acategory = categories.addInstance(new EntityInstance(categories.definition())).setValue("title", "a Category"); + final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(0,todo.countInstances()); + Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("categories/" + acategory.getPrimaryKeyValue() + "/todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -223,9 +223,9 @@ public void canCreateARelationshipAndTodoBetweenCategoryAndTodoViaTodos(){ Assertions.assertEquals(201, response.getStatusCode()); Assertions.assertEquals(1, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1,todo.countInstances()); + Assertions.assertEquals(1,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - final EntityInstance inMemoryTodo = todo.findInstanceByPrimaryKey(response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); + final EntityInstance inMemoryTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); Assertions.assertTrue(response.getBody().contains(inMemoryTodo.getPrimaryKeyValue()), response.getBody()); @@ -234,9 +234,9 @@ public void canCreateARelationshipAndTodoBetweenCategoryAndTodoViaTodos(){ @Test public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingXml(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); @@ -256,15 +256,15 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingXml(){ @Test public void canDeleteARelationshipBetweenProjectAndTodoViaTasks(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); - final EntityInstance aproject = project.addInstance(new EntityInstance(project.definition())).setValue("title", "a Project"); + final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); aproject.getRelationships().connect("tasks", atodo); Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(1, todo.countInstances()); - Assertions.assertEquals(1, project.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks/" + atodo.getPrimaryKeyValue()); @@ -273,8 +273,8 @@ public void canDeleteARelationshipBetweenProjectAndTodoViaTasks(){ Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(1, todo.countInstances()); - Assertions.assertEquals(1, project.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); } @@ -283,15 +283,15 @@ public void canDeleteARelationshipBetweenProjectAndTodoViaTasks(){ public void canDeleteARelationshipBetweenCategoryAndTodoViaTodos(){ - final EntityInstance acategory = categories.addInstance(new EntityInstance(categories.definition())).setValue("title", "a Category"); + final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); acategory.getRelationships().connect("todos", atodo); Assertions.assertEquals(1, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1, todo.countInstances()); - Assertions.assertEquals(1, categories.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(categories)); final HttpApiRequest request = new HttpApiRequest("categories/" + acategory.getPrimaryKeyValue() + "/todos/" + atodo.getPrimaryKeyValue()); @@ -299,16 +299,16 @@ public void canDeleteARelationshipBetweenCategoryAndTodoViaTodos(){ Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1, todo.countInstances()); - Assertions.assertEquals(1, categories.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(categories)); // if relationship doesn't exist, I should get a 404 if I reissue therequest response = new ThingifierHttpApi(todoManager).delete(request); Assertions.assertEquals(404, response.getStatusCode()); Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1, todo.countInstances()); - Assertions.assertEquals(1, categories.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(categories)); } /** @@ -339,13 +339,14 @@ public void canNotCreateEstimateWithoutMandatoryRelationship(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(400, response.getStatusCode()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.collection(todoManager, "estimate").countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances( + ThingifierRepositoryTestSupport.entity(todoManager, "estimate"))); } @Test public void canCreateAnEstimateForTodoMandatoryRelationship(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO for estimating"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO for estimating"); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue() + "/estimates" ); @@ -358,7 +359,8 @@ public void canCreateAnEstimateForTodoMandatoryRelationship(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.collection(todoManager, "estimate").countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances( + ThingifierRepositoryTestSupport.entity(todoManager, "estimate"))); Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); } @@ -368,16 +370,16 @@ public void canCreateAnEstimateForTodoMandatoryRelationship(){ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO for estimating"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO for estimating"); - final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); - final EntityInstance anEstimate = estimates.addInstance(new EntityInstance(estimates.definition())).setValue("duration", "7"); + final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + final EntityInstance anEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)).setValue("duration", "7"); anEstimate.getRelationships().connect("estimate", atodo); Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); - Assertions.assertEquals(1, estimates.countInstances()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); final HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); @@ -386,8 +388,8 @@ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(0, todo.countInstances()); - Assertions.assertEquals(0, estimates.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates)); } @@ -395,16 +397,16 @@ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ public void canGetEstimatesViaRelationship(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "a TODO for estimating"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO for estimating"); - final RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); - final EntityInstance anEstimate = estimates.addInstance(new EntityInstance(estimates.definition())).setValue("duration", "7").setValue("description", "an estimate"); + final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + final EntityInstance anEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)).setValue("duration", "7").setValue("description", "an estimate"); anEstimate.getRelationships().connect("estimate", atodo); Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); - Assertions.assertEquals(1, estimates.countInstances()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates)); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue() + "/estimates"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java index aae7cab4..32505ab5 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -15,12 +14,13 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class XmlRequestResponseTest { private Thingifier todoManager; - RepositoryBackedTestCollection todo; - RepositoryBackedTestCollection project; + EntityDefinition todo; + EntityDefinition project; // todo: Too complicated any test that uses the TodoManagerModel in thingifier needs to be simplified @@ -31,8 +31,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); } @@ -57,7 +57,7 @@ public void canGetAnEmptyXmlItemsCollection(){ public void canGetXmlItemsWhenAskedForXml(){ - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); HttpApiRequest request = new HttpApiRequest("todos"); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -73,7 +73,7 @@ public void canGetXmlItemsWhenAskedForXml(){ public void canGetXmlErrorMessagesWhenAskedForXml(){ - todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); HttpApiRequest request = new HttpApiRequest("todosyoohoo"); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -108,19 +108,19 @@ public void canPostAndCreateAnItemWithXml(){ request.setBody("test title"); - Assertions.assertEquals(0, todo.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = todo.findInstanceByPrimaryKey(guid); + final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("test title", aTodo.getFieldValue("title").asString()); @@ -132,9 +132,9 @@ public void canPostAndCreateAnItemWithXml(){ @Test public void canPostAndAmendAnItemWithXml(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -150,7 +150,7 @@ public void canPostAndAmendAnItemWithXml(){ System.out.println(response.getBody()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); Assertions.assertEquals("test title", atodo.getFieldValue("title").asString()); @@ -173,7 +173,7 @@ public void canPutAndCreateAnItemWithXmlAndReceiveJson(){ request.setBody("test title"); - Assertions.assertEquals(0, todo.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); @@ -182,12 +182,12 @@ public void canPutAndCreateAnItemWithXmlAndReceiveJson(){ Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = todo.findInstanceByPrimaryKey(guid); + final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("test title", aTodo.getFieldValue("title").asString()); @@ -209,9 +209,9 @@ public void canPutAndCreateAnItemWithXmlAndReceiveJson(){ @Test public void canPutToAmendAnItemWithJson(){ - final EntityInstance atodo = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "my title"); + final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/"+atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -227,7 +227,7 @@ public void canPutToAmendAnItemWithJson(){ Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(1, todo.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); Assertions.assertEquals("test title", atodo.getFieldValue("title").asString()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java index 5b51e5e6..50b71e69 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java @@ -5,12 +5,12 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import java.util.Collection; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class BasicTodoManagerTest { private Thingifier todoManager; @@ -26,13 +26,13 @@ public void createDefinitions(){ public void todoModelDefinitionCheck(){ - RepositoryBackedTestCollection todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - Assertions.assertTrue(todo.definition().hasFieldNameDefined("title")); - Assertions.assertTrue(todo.definition().hasFieldNameDefined("description")); - Assertions.assertTrue(todo.definition().hasFieldNameDefined("doneStatus")); + Assertions.assertTrue(todo.hasFieldNameDefined("title")); + Assertions.assertTrue(todo.hasFieldNameDefined("description")); + Assertions.assertTrue(todo.hasFieldNameDefined("doneStatus")); - Assertions.assertEquals("false", todo.definition(). + Assertions.assertEquals("false", todo. getField("doneStatus"). getDefaultValue().asString()); @@ -43,13 +43,13 @@ public void todoModelDefinitionCheck(){ public void relationshipDefinitionCheck(){ - RepositoryBackedTestCollection todo = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); - RepositoryBackedTestCollection project = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + EntityDefinition todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); - EntityInstance paperwork = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "scan paperwork"); - EntityInstance filework = todo.addInstance(new EntityInstance(todo.definition())).setValue("title", "file paperwork"); + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "scan paperwork"); + EntityInstance filework = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "file paperwork"); - EntityInstance officeWork = project.addInstance(new EntityInstance(project.definition())).setValue("title", "Office Work"); + EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "Office Work"); officeWork.getRelationships().connect("tasks", paperwork); officeWork.getRelationships().connect("tasks", filework); @@ -76,13 +76,13 @@ public void relationshipDefinitionCheck(){ @Test public void createAndAmendSomeTodos(){ - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - EntityInstance paperwork = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title","Do Paperwork"). setValue("description", "Scan everything in, upload to document management system and file paperwork"); @@ -99,23 +99,23 @@ public void createAndAmendSomeTodos(){ @Test public void createAndDeleteTodos(){ - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - int originalTodosCount = todos.countInstances(); + int originalTodosCount = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos); - EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title","Delete this todo"). setValue("description", "I need to be deleted"); - EntityInstance foundit = todos.findInstanceByPrimaryKey(tidy.getPrimaryKeyValue()); + EntityInstance foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todos, tidy.getPrimaryKeyValue()); Assertions.assertEquals("Delete this todo", foundit.getFieldValue("title").asString()); todoManager.deleteThing(foundit, EntityRelModel.DEFAULT_DATABASE_NAME); - Assertions.assertEquals(originalTodosCount, todos.countInstances()); + Assertions.assertEquals(originalTodosCount, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); - foundit = todos.findInstanceByPrimaryKey(tidy.getPrimaryKeyValue()); + foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todos, tidy.getPrimaryKeyValue()); Assertions.assertNull(foundit); @@ -132,30 +132,30 @@ public void createAndDeleteTodos(){ @Test public void createAmendAndDeleteATodoWithAGivenGUID(){ - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - int originalTodosCount = todos.countInstances(); + int originalTodosCount = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos); String guid="6fd86e2d-7c52-4dea-85bb-34760ef66d9d"; - EntityInstance tidy = new EntityInstance(todos.definition()); + EntityInstance tidy = new EntityInstance(todos); tidy.overrideValue("guid", guid); tidy.setValue("title", "Delete this todo"). setValue("description", "I need to be deleted"); - todos.addInstance(tidy); + ThingifierRepositoryTestSupport.repository(todoManager).addInstance(tidy); - EntityInstance foundit = todos.findInstanceByFieldNameAndValue("guid", guid); + EntityInstance foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todos, "guid", guid); Assertions.assertEquals("Delete this todo", foundit.getFieldValue("title").asString()); todoManager.deleteThing(foundit, EntityRelModel.DEFAULT_DATABASE_NAME); - Assertions.assertEquals(originalTodosCount, todos.countInstances()); + Assertions.assertEquals(originalTodosCount, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); - foundit = todos.findInstanceByFieldNameAndValue("guid", guid); + foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todos, "guid", guid); Assertions.assertNull(foundit); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java index 09fb97f6..0793fd59 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -14,6 +13,7 @@ import java.util.ArrayList; import java.util.Collection; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class OptionalityRelationshipTest { // relationships can be Optional:Optional @@ -34,16 +34,16 @@ public void createDefinitions(){ public void byDefaultRelationshipsAreOptional(){ - RepositoryBackedTestCollection projects = ThingifierRepositoryTestSupport.collection(todoManager, "project"); + EntityDefinition projects = ThingifierRepositoryTestSupport.entity(todoManager, "project"); - EntityInstance aProject = projects.addInstance(new EntityInstance(projects.definition())).setValue("title", "myproject"); + EntityInstance aProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(projects)).setValue("title", "myproject"); Assertions.assertTrue(aProject.validate().isValid()); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); @@ -55,15 +55,15 @@ public void byDefaultRelationshipsAreOptional(){ @Test public void anEstimateWithoutATodoIsInvalid(){ - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); + EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - EntityInstance tidyRoomEstimate = estimates.addInstance(new EntityInstance(estimates.definition())). + EntityInstance tidyRoomEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)). setValue("duration", "1"); // it should be invalid because the estimate does not have a relationship with a to do @@ -74,15 +74,15 @@ public void anEstimateWithoutATodoIsInvalid(){ @Test public void anEstimateMustHaveATodoToBeValid(){ - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); + EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - EntityInstance tidyRoomEstimate = estimates.addInstance(new EntityInstance(estimates.definition())). + EntityInstance tidyRoomEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)). setValue("duration", "1"); tidyRoomEstimate.getRelationships().connect("estimate", tidy); @@ -101,16 +101,16 @@ public void anEstimateMustHaveATodoToBeValid(){ @Test public void deleteAlsoCoversMandatoryOptionalityRelationships(){ - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(todoManager, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - EntityInstance tidy = todos.addInstance(new EntityInstance(todos.definition())). + EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). setValue("title", "Tidy up my room"). setValue("description", "I need to tidy up my room because it is a mess"); - RepositoryBackedTestCollection estimates = ThingifierRepositoryTestSupport.collection(todoManager, "estimate"); + EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - EntityInstance tidyRoomEstimate = estimates.addInstance(new EntityInstance(estimates.definition())). + EntityInstance tidyRoomEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)). setValue("duration", "1"); tidyRoomEstimate.getRelationships().connect("estimate", tidy); @@ -120,8 +120,8 @@ public void deleteAlsoCoversMandatoryOptionalityRelationships(){ final Collection relatedEstimates = tidy.getRelationships().getConnectedItems("estimates"); Assertions.assertEquals(1, relatedEstimates.size()); - Assertions.assertEquals(1, estimates.getInstances().size()); - Assertions.assertEquals(1, todos.getInstances().size()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(estimates).size()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos).size()); // now delete the to do, and the estimate should also be deleted @@ -131,8 +131,8 @@ public void deleteAlsoCoversMandatoryOptionalityRelationships(){ // things only know about themselves and their instances, but the thingifier knows about // all things and so can delete related items as well - Assertions.assertEquals(0, todos.getInstances().size()); - Assertions.assertEquals(0, estimates.getInstances().size()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos).size()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(estimates).size()); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java index f8e3c466..4f43bd9b 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java @@ -12,7 +12,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import java.util.HashMap; @@ -45,8 +44,8 @@ public void aGetRequestWillCreateNewSessionWithDatabase(){ final HttpApiResponse response = api.get(new HttpApiRequest("/things").setHeaders(headers)); // add a thing - final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); - final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); + final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); + final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(thingifier, "other_things").addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); final HttpApiResponse response2 = api.get(new HttpApiRequest("/things/" + existingInstance.getPrimaryKeyValue()).setHeaders(headers)); @@ -82,10 +81,10 @@ public void aRequestWillCreateNewSessionWithDatabaseUsingDefaultData(){ Assertions.assertEquals(200, response.getStatusCode()); - RepositoryBackedTestCollection thingInstances = - ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); + EntityDefinition thingInstances = + ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); - Assertions.assertEquals(3, thingInstances.countInstances()); + Assertions.assertEquals(3, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); } @Test @@ -108,13 +107,14 @@ public void aPostRequestWillCreateNewSessionWithDatabase(){ // check added a thing to db Assertions.assertEquals(201, response.getStatusCode()); - RepositoryBackedTestCollection thingInstances = - ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); + EntityDefinition thingInstances = + ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); - Assertions.assertEquals(1, thingInstances.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); Assertions.assertEquals(0, - ThingifierRepositoryTestSupport.collection(thingifier, "thing").countInstances()); + ThingifierRepositoryTestSupport.repository(thingifier).countInstances( + ThingifierRepositoryTestSupport.entity(thingifier, "thing"))); } @Test @@ -135,21 +135,22 @@ public void aDeleteRequestWillCreateNewSessionWithDatabase(){ // check added a thing to db Assertions.assertEquals(404, response.getStatusCode()); - RepositoryBackedTestCollection thingInstances = - ThingifierRepositoryTestSupport.collection(thingifier, "other_things", "thing"); + EntityDefinition thingInstances = + ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); - EntityInstance anInstance = thingInstances.addInstance(new EntityInstance(thingInstances.definition())); + EntityInstance anInstance = ThingifierRepositoryTestSupport.repository(thingifier, "other_things").addInstance(new EntityInstance(thingInstances)); - Assertions.assertEquals(1, thingInstances.countInstances()); + Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); final HttpApiResponse actualDeleteResponse = api.delete(new HttpApiRequest("/things/" + anInstance.getPrimaryKeyValue()) .setHeaders(headers) ); - Assertions.assertEquals(0, thingInstances.countInstances()); + Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); Assertions.assertEquals(0, - ThingifierRepositoryTestSupport.collection(thingifier, "thing").countInstances()); + ThingifierRepositoryTestSupport.repository(thingifier).countInstances( + ThingifierRepositoryTestSupport.entity(thingifier, "thing"))); } // OPTIONS diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java index fb83ace6..056321fb 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java @@ -6,7 +6,6 @@ import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; @@ -42,8 +41,8 @@ public void canGetAThingUsingGuidFromDefaultSession(){ Thingifier model = getThingifier(); - final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "thing"); - final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", + final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "thing"); + final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model).addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); // no session header so use default session @@ -57,8 +56,8 @@ public void canGetAThingUsingIdFromDefaultSession(){ Thingifier model = getThingifier(); - final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "thing"); - final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", + final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "thing"); + final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model).addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); // no session header so use default session @@ -83,8 +82,8 @@ public void canGetAThingUsingGuidFromCustomSession(){ Assertions.assertEquals(404, idApiResponse.getStatusCode()); // add instance to custom session - final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "other_things", "thing"); - final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); + final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "other_things", "thing"); + final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model, "other_things").addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); final ApiResponse idApiResponse2 = model.api().get("/thing/" + existingInstance.getPrimaryKeyValue(), new QueryFilterParams(), headers); Assertions.assertEquals(200, idApiResponse2.getStatusCode()); @@ -106,8 +105,8 @@ public void canGetAThingUsingIdFromCustomSession(){ Assertions.assertEquals(404, idApiResponse.getStatusCode()); // add instance to custom session - final RepositoryBackedTestCollection thing = ThingifierRepositoryTestSupport.collection(model, "other_things", "thing"); - final EntityInstance existingInstance = thing.addInstance(new EntityInstance(thing.definition())).setValue("title", "My Title" + System.nanoTime()); + final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "other_things", "thing"); + final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model, "other_things").addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); final ApiResponse idApiResponse2 = model.api().get("/thing/" + existingInstance.getFieldValue("id").asString(), new QueryFilterParams(), headers); Assertions.assertEquals(200, idApiResponse2.getStatusCode()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java index c39c4f18..db63c3e0 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; @@ -26,7 +25,7 @@ public class NestedObjectsApiTest { EntityDefinition defn; EntityInstance instance; ThingifierHttpApi api; - RepositoryBackedTestCollection thing; + EntityDefinition thing; @BeforeEach public void createThingWithNestedObjectField(){ @@ -37,7 +36,7 @@ public void createThingWithNestedObjectField(){ defn = thingifier.defineThing("thing", "things"); defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); - thing = ThingifierRepositoryTestSupport.collection(thingifier, "thing"); + thing = ThingifierRepositoryTestSupport.entity(thingifier, "thing"); defn.addField(Field.is("person", FieldType.OBJECT) .withField( @@ -56,7 +55,7 @@ void canAmendConnie(){ instance.setValue("person.firstname", "Connie"); instance.setValue("person.surname", "Dobbs"); - thing.addInstance(instance); + ThingifierRepositoryTestSupport.repository(thingifier).addInstance(instance); api = new ThingifierHttpApi(thingifier, null, null); @@ -81,7 +80,7 @@ void canCreateBob(){ api = new ThingifierHttpApi(thingifier, null, null); - Assertions.assertEquals(0,thing.countInstances()); + Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); final HttpApiRequest createBobRequest = new HttpApiRequest("/things"); createBobRequest.setHeaders(Map.of("content-type", "application/json")); @@ -92,9 +91,9 @@ void canCreateBob(){ final HttpApiResponse response = api.post(createBobRequest); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1,thing.countInstances()); + Assertions.assertEquals(1,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); - for(EntityInstance bob : thing.getInstances()){ + for(EntityInstance bob : ThingifierRepositoryTestSupport.repository(thingifier).listInstances(thing)){ FieldValue fv = bob.getFieldValue("person"); InstanceFields obj = fv.asObject(); FieldValue fn = obj.getFieldValue("firstname"); @@ -115,7 +114,7 @@ public void failValidationAtObjectFieldLevel() { api = new ThingifierHttpApi(thingifier, null, null); - Assertions.assertEquals(0,thing.countInstances()); + Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); final HttpApiRequest failToCreateBobRequest = new HttpApiRequest("/things"); failToCreateBobRequest.setVerb(HttpApiRequest.VERB.POST); @@ -126,7 +125,7 @@ public void failValidationAtObjectFieldLevel() { final HttpApiResponse response = api.post(failToCreateBobRequest); Assertions.assertEquals(400, response.getStatusCode()); - Assertions.assertEquals(0,thing.countInstances()); + Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); Assertions.assertTrue( response.apiResponse().getErrorMessages().contains("surname : field is mandatory")); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java index 8d0e1bf7..57b759be 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java @@ -6,7 +6,6 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; @@ -108,9 +107,9 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); - EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); + EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); ApiResponse response = ApiResponse.success().returnSingleInstance(aTodo); @@ -135,12 +134,12 @@ public void response200WithInstances(){ EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); todo.addFields( Field.is("title", STRING)); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); - EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); - EntityInstance anotherTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "another todo"); + EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); + EntityInstance anotherTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "another todo"); - ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(todos.getInstances())); + ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(ThingifierRepositoryTestSupport.repository(thingifier).listInstances(todos))); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(true, response.hasABody()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java index 3b3759de..53025211 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java @@ -7,7 +7,6 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; @@ -125,9 +124,9 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); - EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); + EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); ApiResponse response = ApiResponse.success().returnSingleInstance(aTodo); @@ -153,12 +152,12 @@ public void response200WithInstances(){ EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); todo.addFields( Field.is("title", STRING)); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); - EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); - EntityInstance anotherTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "another todo"); + EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); + EntityInstance anotherTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "another todo"); - ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(todos.getInstances())); + ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(ThingifierRepositoryTestSupport.repository(thingifier).listInstances(todos))); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(true, response.hasABody()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java index e8fc4690..f5cb0a2a 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.testsupport.RepositoryBackedTestCollection; import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -87,9 +86,9 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); - EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); + EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); ApiResponse response = ApiResponse.success().returnSingleInstance(aTodo); @@ -108,12 +107,12 @@ public void response200WithInstances(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - RepositoryBackedTestCollection todos = ThingifierRepositoryTestSupport.collection(thingifier, "todo"); + EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); - EntityInstance aTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "a todo"); - EntityInstance anotherTodo = todos.addInstance(new EntityInstance(todos.definition())).setValue("title", "another todo"); + EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); + EntityInstance anotherTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "another todo"); - ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(todos.getInstances())); + ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(ThingifierRepositoryTestSupport.repository(thingifier).listInstances(todos))); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(true, response.hasABody()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java deleted file mode 100644 index c3f66bf4..00000000 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/RepositoryBackedTestCollection.java +++ /dev/null @@ -1,56 +0,0 @@ -package uk.co.compendiumdev.thingifier.testsupport; - -import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; -import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; - -import java.util.Collection; -import java.util.List; - -public final class RepositoryBackedTestCollection { - - private final EntityDefinition definition; - private final ThingRepository repository; - - RepositoryBackedTestCollection( - final EntityDefinition definition, - final ThingRepository repository) { - this.definition = definition; - this.repository = repository; - } - - public EntityDefinition definition() { - return definition; - } - - public EntityInstance addInstance(final EntityInstance instance) { - return repository.addInstance(instance); - } - - public int countInstances() { - return repository.countInstances(definition); - } - - public Collection getInstances() { - return repository.listInstances(definition); - } - - public Collection getInstances(final QueryFilterParams params) { - return repository.listInstances(definition, params); - } - - public EntityInstance findInstanceByPrimaryKey(final String primaryKeyValue) { - return repository.findInstanceByPrimaryKey(definition, primaryKeyValue); - } - - public EntityInstance findInstanceByFieldNameAndValue( - final String fieldName, final String fieldValue) { - return repository.findInstanceByFieldNameAndValue(definition, fieldName, fieldValue); - } - - public List deleteInstance(final EntityInstance instance) { - repository.deleteEntityInstance(instance); - return List.of(); - } -} diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java index f3712349..a2f24f99 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java @@ -10,19 +10,26 @@ public final class ThingifierRepositoryTestSupport { private ThingifierRepositoryTestSupport() { } - public static RepositoryBackedTestCollection collection( + public static ThingRepository repository(final Thingifier thingifier) { + return repository(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME); + } + + public static ThingRepository repository( + final Thingifier thingifier, + final String databaseName) { + return thingifier.getRepository(databaseName); + } + + public static EntityDefinition entity( final Thingifier thingifier, final String entityName) { - return collection(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME, entityName); + return entity(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME, entityName); } - public static RepositoryBackedTestCollection collection( + public static EntityDefinition entity( final Thingifier thingifier, final String databaseName, final String entityName) { - EntityDefinition definition = - thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); - ThingRepository repository = thingifier.getRepository(databaseName); - return new RepositoryBackedTestCollection(definition, repository); + return thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); } } From d408b01fd5643d16a752ce6a7dbbae8c7f439bc5 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Thu, 9 Jul 2026 09:26:52 +0100 Subject: [PATCH 10/22] Configure simulation routes repository backend --- .../challenge/ChallengeMain.java | 5 +- .../challenge/ChallengeRouteHandler.java | 15 +++- .../challenge/ChallengerConfig.java | 74 ++++++++++++++++ .../simulation/SimulationRoutes.java | 58 ++++++++----- .../ChallengerConfigRepositoryTest.java | 84 +++++++++++++++++++ .../SimulationRoutesRepositoryTest.java | 34 ++++++++ .../practicemodes/SmulationModeTest.java | 33 ++++++++ 7 files changed, 278 insertions(+), 25 deletions(-) create mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java create mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java index e3996999..1afa0065 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java @@ -35,6 +35,9 @@ public static void main(String[] args) { // create a default configuration class which we pass into the ChallengeRouteHandler and configure via args ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs(args); + logger.info("Using Simulation repository {}", + config.getSimulationRepositoryConfig().describe()); for (String arg : args) { if (arg.toLowerCase().startsWith("-multiplayer") || @@ -119,7 +122,7 @@ public static ChallengeRouteHandler getChallenger(){ public static void stop(){ if (challenger != null) { - challenger.getThingifier().close(); + challenger.close(); } challenger = null; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java index c7b165a4..13fbe789 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java @@ -25,13 +25,16 @@ public class ChallengeRouteHandler { ChallengeDefinitions challengeDefinitions; Challengers challengers; private boolean single_player_mode; + private final ChallengerConfig config; PersistenceLayer persistenceLayer; private boolean guiStayAlive=false; // when set gui makes a call every 5 mins to keep session alive, private DefaultGUIHTML guiTemplates; + private SimulationRoutes simulationRoutes; // not needed when storing data public ChallengeRouteHandler(Thingifier thingifier, ThingifierApiDocumentationDefn apiDefn, ChallengerConfig config){ + this.config = config; this.apiChallengesDocumentationDefn = apiDefn; apiDefn.setThingifier(thingifier); apiDefn.setSeoTitle("API Challenges API Documentation | API Challenges"); @@ -99,7 +102,10 @@ public ChallengeRouteHandler configureRoutes() { new MirrorRoutes().configure(mirrorModeDocumentationDefn, guiTemplates); // Simulation routes should not show - new SimulationRoutes(guiTemplates).configure(); + simulationRoutes = new SimulationRoutes( + guiTemplates, + config.getSimulationRepositoryConfig()); + simulationRoutes.configure(); new SimpleApiRoutes(guiTemplates).configure(); @@ -133,6 +139,13 @@ public Thingifier getThingifier() { return thingifier; } + public void close() { + if (simulationRoutes != null) { + simulationRoutes.close(); + } + thingifier.close(); + } + private void enableAdminApi() { thingifier.apiConfig().adminConfig().enableAdminSearch(); thingifier.apiConfig().adminConfig().enableAdminDataClear(); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerConfig.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerConfig.java index 57a6b506..5ca3f4c6 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerConfig.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerConfig.java @@ -1,13 +1,55 @@ package uk.co.compendiumdev.challenge; import uk.co.compendiumdev.challenge.persistence.PersistenceLayer; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProviderConfig; + +import java.nio.file.Path; +import java.nio.file.Paths; public class ChallengerConfig { + public static final String ARG_SIM_REPOSITORY = "-sim-repository"; + public static final String ARG_SIM_SQLITE_MEMORY = "-sim-sqlite-memory"; + public static final String ARG_SIM_SQLITE_DIRECTORY = "-sim-sqlite-directory"; + public static final String DEFAULT_SIM_SQLITE_DIRECTORY = "thingifier-sim-sqlite"; + public boolean single_player_mode = true; public boolean isAdminApiEnabled = false; public boolean guiStayAlive=false; public PersistenceLayer persistenceLayer = new PersistenceLayer(PersistenceLayer.StorageType.LOCAL); + private ThingRepositoryProviderConfig simulationRepositoryConfig = + defaultSimulationRepositoryConfig(); + + public static ThingRepositoryProviderConfig defaultSimulationRepositoryConfig() { + return new ThingRepositoryProviderConfig( + ThingRepositoryProviderConfig.DEFAULT_REPOSITORY_MODE, + Paths.get(DEFAULT_SIM_SQLITE_DIRECTORY)); + } + + public void setSimulationRepositoryFromArgs(final String[] args) { + String repositoryMode = ThingRepositoryProviderConfig.DEFAULT_REPOSITORY_MODE; + if (hasArg(args, ARG_SIM_SQLITE_MEMORY)) { + repositoryMode = "sqlite-memory"; + } else { + String configuredMode = argValue(args, ARG_SIM_REPOSITORY); + if (configuredMode != null && !configuredMode.trim().isEmpty()) { + repositoryMode = configuredMode; + } + } + + String sqliteDirectory = argValue(args, ARG_SIM_SQLITE_DIRECTORY); + if (sqliteDirectory == null || sqliteDirectory.trim().isEmpty()) { + sqliteDirectory = DEFAULT_SIM_SQLITE_DIRECTORY; + } + + simulationRepositoryConfig = new ThingRepositoryProviderConfig( + repositoryMode, + Path.of(sqliteDirectory)); + } + + public ThingRepositoryProviderConfig getSimulationRepositoryConfig() { + return simulationRepositoryConfig; + } public void setToMultiPlayerMode() { single_player_mode=false; @@ -28,4 +70,36 @@ public void setToNoPersistenceMode() { public void enableAdminApi() { isAdminApiEnabled=true; } + + private static String argValue(final String[] args, final String argName) { + if (args == null) { + return null; + } + + for (String arg : args) { + if (arg == null) { + continue; + } + if (arg.equalsIgnoreCase(argName)) { + return ""; + } + if (arg.toLowerCase().startsWith(argName.toLowerCase() + "=")) { + return arg.substring(arg.indexOf("=") + 1).trim(); + } + } + return null; + } + + private static boolean hasArg(final String[] args, final String argName) { + if (args == null) { + return false; + } + + for (String arg : args) { + if (arg != null && arg.equalsIgnoreCase(argName)) { + return true; + } + } + return false; + } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java index 3b6db9f2..3cd26954 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java @@ -1,6 +1,8 @@ package uk.co.compendiumdev.challenge.practicemodes.simulation; +import uk.co.compendiumdev.challenge.ChallengerConfig; import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi; @@ -15,15 +17,13 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.validation.MaximumLengthValidationRule; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListFilter; -import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListSorter; +import uk.co.compendiumdev.thingifier.core.query.FilterBy; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; -import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProviderConfig; import uk.co.compendiumdev.thingifier.htmlgui.htmlgen.DefaultGUIHTML; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; -import java.util.ArrayList; import java.util.List; import static spark.Spark.*; @@ -39,14 +39,28 @@ public class SimulationRoutes { private ThingifierApiDocumentationDefn apiDocDefn; private ThingifierAutoDocGenRouting simulatorDocsRouting; private DefaultGUIHTML guiTemplates; + private final ThingRepositoryProviderConfig simulationRepositoryConfig; public SimulationRoutes(DefaultGUIHTML guiTemplates){ + this(guiTemplates, ChallengerConfig.defaultSimulationRepositoryConfig()); + } + + public SimulationRoutes( + final DefaultGUIHTML guiTemplates, + final ThingRepositoryProviderConfig simulationRepositoryConfig) { this.guiTemplates=guiTemplates; + this.simulationRepositoryConfig = simulationRepositoryConfig; } public void setUpData(){ + setUpRepositoryBackedData(); + setUpDocumentation(); + } + + void setUpRepositoryBackedData() { // fake the data storage - simulation = new Thingifier(); + simulation = new Thingifier( + new EntityRelModel(simulationRepositoryConfig.createProvider())); simulation.setDocumentation("Simulation Mode", "A simulated API, each request generates a new set of data but responses are processed by an API handler."); entityDefn = simulation.defineThing("entity", "entities"); @@ -76,6 +90,11 @@ public void setUpData(){ jsonThing = new JsonThing(simulation.apiConfig().jsonOutput()); + ThingifierApiConfig customApiconfig = new ThingifierApiConfig("/sim"); + simulation.apiConfig().setFrom(customApiconfig); + } + + private void setUpDocumentation() { apiDocDefn = new ThingifierApiDocumentationDefn(); apiDocDefn.addServer("https://apichallenges.eviltester.com", "cloud hosted version"); apiDocDefn.addServer("http://localhost:4567", "local execution"); @@ -88,9 +107,6 @@ public void setUpData(){ apiDocDefn.setOgType("website"); apiDocDefn.setTwitterCard("summary_large_image"); - ThingifierApiConfig customApiconfig = new ThingifierApiConfig("/sim"); - simulation.apiConfig().setFrom(customApiconfig); - simulation.apidocsconfig().setHeaderSectionOverride("""

A simulated API, where each request is run against a new generated set of data but responses are processed by an API handler. @@ -149,22 +165,12 @@ public void configure() { }); HttpApiRequestHandler getEntitiesHandler = (HttpApiRequest anHttpApiRequest) -> { - // remove id 11 because that is a POST so not available in the list - List idsToRemove = new ArrayList<>(); - idsToRemove.add(11); - - // process it because the request validated - List instances = new ArrayList<>(); - for (EntityInstance possible : entityRepository.listInstances(entityDefn)) { - if (!idsToRemove.contains( - possible.getFieldValue("id").asInteger())) { - instances.add(possible); - } - } - QueryFilterParams queryParams = anHttpApiRequest.getFilterableQueryParams(); - instances = new EntityInstanceListFilter(queryParams).filter(instances); - instances = new EntityInstanceListSorter(queryParams).sort(instances); + // id 11 is the special POST-created entity and is not visible in collection GET. + queryParams.add(new FilterBy("id", "!=11")); + + List instances = + entityRepository.listInstances(entityDefn, queryParams); return ApiResponse.success(). returnInstanceCollection(instances). resultContainsType(entityDefn); @@ -306,4 +312,10 @@ public void configure() { }).handle(); }); } + + public void close() { + if (simulation != null) { + simulation.close(); + } + } } diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java new file mode 100644 index 00000000..b64156f3 --- /dev/null +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java @@ -0,0 +1,84 @@ +package uk.co.compendiumdev.challenge; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; + +import java.nio.file.Path; + +public class ChallengerConfigRepositoryTest { + + @Test + void defaultSimulationRepositoryIsMemory() { + ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs(new String[]{}); + + Assertions.assertEquals( + "memory", + config.getSimulationRepositoryConfig().getRepositoryMode()); + + try (ThingRepositoryProvider provider = + config.getSimulationRepositoryConfig().createProvider()) { + Assertions.assertTrue(provider instanceof InMemoryThingRepositoryProvider); + } + } + + @Test + void simRepositoryFlagCanUseSqliteMemory() { + ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs( + new String[]{"-sim-repository=sqlite-memory"}); + + Assertions.assertEquals( + "sqlite-memory", + config.getSimulationRepositoryConfig().getRepositoryMode()); + + try (ThingRepositoryProvider provider = + config.getSimulationRepositoryConfig().createProvider()) { + Assertions.assertTrue(provider instanceof SqliteThingRepositoryProvider); + } + } + + @Test + void simSqliteMemoryAliasCanUseSqliteMemory() { + ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs(new String[]{"-sim-sqlite-memory"}); + + Assertions.assertEquals( + "sqlite-memory", + config.getSimulationRepositoryConfig().getRepositoryMode()); + + try (ThingRepositoryProvider provider = + config.getSimulationRepositoryConfig().createProvider()) { + Assertions.assertTrue(provider instanceof SqliteThingRepositoryProvider); + } + } + + @Test + void mainRepositorySqliteMemoryFlagDoesNotConfigureSimulationRepository() { + ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs(new String[]{"-sqlite-memory"}); + + Assertions.assertEquals( + "memory", + config.getSimulationRepositoryConfig().getRepositoryMode()); + } + + @Test + void simSqliteFileCanUseSeparateDirectory() { + ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs(new String[]{ + "-sim-repository=sqlite-file", + "-sim-sqlite-directory=target/simulation-repository-test" + }); + + Assertions.assertEquals( + "sqlite-file", + config.getSimulationRepositoryConfig().getRepositoryMode()); + Assertions.assertEquals( + Path.of("target/simulation-repository-test"), + config.getSimulationRepositoryConfig().getSqliteDirectory()); + } +} diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java new file mode 100644 index 00000000..4c78d082 --- /dev/null +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java @@ -0,0 +1,34 @@ +package uk.co.compendiumdev.challenge.practicemodes.simulation; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.challenge.ChallengerConfig; +import uk.co.compendiumdev.thingifier.core.EntityRelModel; +import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.htmlgui.htmlgen.DefaultGUIHTML; + +public class SimulationRoutesRepositoryTest { + + @Test + void simulationRoutesUseConfiguredSqliteMemoryRepository() { + ChallengerConfig config = new ChallengerConfig(); + config.setSimulationRepositoryFromArgs( + new String[]{"-sim-repository=sqlite-memory"}); + + SimulationRoutes routes = new SimulationRoutes( + new DefaultGUIHTML(), + config.getSimulationRepositoryConfig()); + + try { + routes.setUpRepositoryBackedData(); + + ThingRepository repository = + routes.simulation.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + Assertions.assertTrue(repository instanceof SqliteThingRepository); + Assertions.assertEquals(11, repository.countInstances(routes.entityDefn)); + } finally { + routes.close(); + } + } +} diff --git a/challenger/src/test/java/uk/co/compendiumdev/practicemodes/SmulationModeTest.java b/challenger/src/test/java/uk/co/compendiumdev/practicemodes/SmulationModeTest.java index 46b0444b..bd81a982 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/practicemodes/SmulationModeTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/practicemodes/SmulationModeTest.java @@ -84,6 +84,39 @@ void canSimulateGetOfEntitiesJSON(){ Assertions.assertFalse(response.body.contains("id\":11,")); } + @Test + void canFilterSimulatedEntitiesThroughRepository(){ + + http.clearHeaders(); + http.setHeader("Accept", "application/json"); + + HttpResponseDetails response = http.get("/sim/entities?id=11"); + + Assertions.assertEquals(200, response.statusCode); + Assertions.assertEquals("{\"entities\":[]}", response.body); + + response = http.get("/sim/entities?name*=entity%20number%201"); + + Assertions.assertEquals(200, response.statusCode); + Assertions.assertTrue(response.body.contains("\"id\":1,")); + Assertions.assertFalse(response.body.contains("\"id\":10,")); + } + + @Test + void canSortSimulatedEntitiesThroughRepository(){ + + http.clearHeaders(); + http.setHeader("Accept", "application/json"); + + HttpResponseDetails response = http.get("/sim/entities?sortBy=-id"); + + Assertions.assertEquals(200, response.statusCode); + Assertions.assertTrue( + response.body.indexOf("\"id\":10,") < + response.body.indexOf("\"id\":9,")); + Assertions.assertFalse(response.body.contains("\"id\":11,")); + } + @Test void canSimulateGetOfEntitiesXML(){ From d7776bd08d77ee125551cfc18d8117a31e8685bd Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Thu, 9 Jul 2026 09:45:30 +0100 Subject: [PATCH 11/22] Organize repository implementations by backend --- .../ChallengerConfigRepositoryTest.java | 4 +- .../ChallengeApiModelRepositoryTest.java | 4 +- .../ChallengerApiResponseHookTest.java | 4 +- .../SimulationRoutesRepositoryTest.java | 2 +- .../RepositoryMigrationGuardTest.java | 152 ------------------ checkstyle-project-fqn.xml | 37 +++++ .../thingifier/core/EntityRelModel.java | 2 +- .../ThingRepositoryProviderConfig.java | 3 + .../InMemoryThingRepository.java | 3 +- .../InMemoryThingRepositoryProvider.java | 4 +- .../{ => sqlite}/SqliteRegexFilterPolicy.java | 2 +- .../SqliteRegexToLikeConverter.java | 2 +- .../{ => sqlite}/SqliteThingRepository.java | 3 +- .../SqliteThingRepositoryProvider.java | 4 +- .../thingifier/core/EntityRelModelTest.java | 2 +- .../ThingRepositoryContractTest.java | 8 +- .../ThingRepositoryProviderConfigTest.java | 2 + .../SqliteRegexToLikeConverterTest.java | 2 +- .../todolist/sqlite/application/Main.java | 2 +- .../RelationshipApiSqliteRepositoryTest.java | 2 +- .../DefaultGuiHtmlPagesRepositoryTest.java | 2 +- 21 files changed, 73 insertions(+), 173 deletions(-) delete mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/{ => inmemory}/InMemoryThingRepository.java (98%) rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/{ => inmemory}/InMemoryThingRepositoryProvider.java (90%) rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/{ => sqlite}/SqliteRegexFilterPolicy.java (95%) rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/{ => sqlite}/SqliteRegexToLikeConverter.java (98%) rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/{ => sqlite}/SqliteThingRepository.java (99%) rename ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/{ => sqlite}/SqliteThingRepositoryProvider.java (94%) rename ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/{ => sqlite}/SqliteRegexToLikeConverterTest.java (98%) diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java index b64156f3..4d639464 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/ChallengerConfigRepositoryTest.java @@ -2,8 +2,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepositoryProvider; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; import java.nio.file.Path; diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java index 8dcdd88f..a40c060b 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java @@ -16,8 +16,8 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepository; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class ChallengeApiModelRepositoryTest { diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java index 19c54ac3..b93503f2 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java @@ -18,8 +18,8 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepositoryProvider; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java index 4c78d082..9454ce54 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutesRepositoryTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.challenge.ChallengerConfig; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.htmlgui.htmlgen.DefaultGUIHTML; diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java deleted file mode 100644 index 1879abf8..00000000 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/repository/RepositoryMigrationGuardTest.java +++ /dev/null @@ -1,152 +0,0 @@ -package uk.co.compendiumdev.challenge.repository; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; - -public class RepositoryMigrationGuardTest { - - @Test - public void productionCodeDoesNotCallLegacySnapshotEscapesOutsideCompatibilityAdapters() throws IOException { - List violations = productionJavaLinesContaining( - List.of( - "getInstanceData(", - "getThingInstancesNamed(", - "getInstancesForSingularOrPluralNamedEntity(", - "getThings(", - "cloneWithDifferentData(", - "createInstanceCollectionFor(", - "getAllInstanceCollections(", - "getInstanceCollectionForEntityNamed("), - List.of( - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/ERInstanceData.java", - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java")); - - Assertions.assertTrue( - violations.isEmpty(), - "Production code should use repository-native APIs instead of legacy snapshot escapes:\n" + - String.join("\n", violations)); - } - - @Test - public void thingifierAndChallengerProductionCodeDoesNotDependOnInstanceCollections() throws IOException { - List violations = productionJavaLinesContaining( - List.of("EntityInstanceCollection"), - List.of(), - List.of("thingifier", "challenger")); - - Assertions.assertTrue( - violations.isEmpty(), - "Thingifier and Challenger runtime code should use ThingRepository instead of EntityInstanceCollection:\n" + - String.join("\n", violations)); - } - - @Test - public void productionCodeDoesNotUseSimpleQuery() throws IOException { - Assertions.assertFalse( - Files.exists(repoRoot().resolve( - "ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/query/SimpleQuery.java")), - "SimpleQuery should be deleted; use RepositoryUrlQuery or repository APIs"); - - List violations = productionJavaLinesContaining( - List.of("SimpleQuery"), - List.of()); - - Assertions.assertTrue( - violations.isEmpty(), - "Production code should use RepositoryUrlQuery or repository APIs instead of SimpleQuery:\n" + - String.join("\n", violations)); - } - - @Test - public void productionCodeDoesNotAddDeprecatedApisWithoutExplicitAllowList() throws IOException { - List violations = productionJavaLinesContaining( - List.of("@Deprecated"), - List.of()); - - Assertions.assertTrue( - violations.isEmpty(), - "Production code should not add @Deprecated APIs without an explicit guard allow-list:\n" + - String.join("\n", violations)); - } - - private List productionJavaLinesContaining( - final List terms, - final List allowedFileSuffixes) throws IOException { - return productionJavaLinesContaining( - terms, allowedFileSuffixes, List.of("ercoremodel", "thingifier", "challenger")); - } - - private List productionJavaLinesContaining( - final List terms, - final List allowedFileSuffixes, - final List modules) throws IOException { - - Path root = repoRoot(); - List violations = new ArrayList<>(); - - for (String module : modules) { - Path sourceRoot = root.resolve(module).resolve("src/main/java"); - if (!Files.exists(sourceRoot)) { - continue; - } - - try (Stream paths = Files.walk(sourceRoot)) { - for (Path path : paths.filter(path -> path.toString().endsWith(".java")).toList()) { - String relativePath = root.relativize(path).toString().replace('\\', '/'); - if (isAllowed(relativePath, allowedFileSuffixes)) { - continue; - } - - List lines = Files.readAllLines(path); - for (int lineIndex = 0; lineIndex < lines.size(); lineIndex++) { - String line = lines.get(lineIndex); - if (containsAny(line, terms)) { - violations.add(relativePath + ":" + (lineIndex + 1) + ": " + line.trim()); - } - } - } - } - } - - return violations; - } - - private Path repoRoot() { - Path current = Paths.get("").toAbsolutePath(); - while (current != null) { - if (Files.exists(current.resolve("ercoremodel")) && - Files.exists(current.resolve("thingifier")) && - Files.exists(current.resolve("challenger"))) { - return current; - } - current = current.getParent(); - } - throw new IllegalStateException("Could not locate thingifier repository root"); - } - - private boolean isAllowed(final String relativePath, final List allowedFileSuffixes) { - for (String allowed : allowedFileSuffixes) { - if (relativePath.endsWith(allowed)) { - return true; - } - } - return false; - } - - private boolean containsAny(final String line, final List terms) { - for (String term : terms) { - if (line.contains(term)) { - return true; - } - } - return false; - } -} diff --git a/checkstyle-project-fqn.xml b/checkstyle-project-fqn.xml index 1cfd3da7..68cb87b2 100644 --- a/checkstyle-project-fqn.xml +++ b/checkstyle-project-fqn.xml @@ -4,11 +4,48 @@ "https://checkstyle.org/dtds/configuration_1_3.dtd"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java index c74ab668..a0263762 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/EntityRelModel.java @@ -5,7 +5,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; -import uk.co.compendiumdev.thingifier.core.repository.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfig.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfig.java index b9c55e4b..d1edcdac 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfig.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfig.java @@ -1,5 +1,8 @@ package uk.co.compendiumdev.thingifier.core.repository; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; + import java.nio.file.Path; import java.nio.file.Paths; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java similarity index 98% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java index e9bdc5d1..2a250ff0 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java @@ -1,4 +1,4 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.inmemory; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; @@ -12,6 +12,7 @@ import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListFilter; import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListSorter; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.ArrayList; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepositoryProvider.java similarity index 90% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepositoryProvider.java index 75d19c7a..84456570 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/InMemoryThingRepositoryProvider.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepositoryProvider.java @@ -1,7 +1,9 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.inmemory; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; import java.util.HashMap; import java.util.HashSet; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexFilterPolicy.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexFilterPolicy.java similarity index 95% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexFilterPolicy.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexFilterPolicy.java index 07f1bff3..f3cce24b 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexFilterPolicy.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexFilterPolicy.java @@ -1,4 +1,4 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.sqlite; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexToLikeConverter.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexToLikeConverter.java similarity index 98% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexToLikeConverter.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexToLikeConverter.java index 416913ca..29d9e809 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexToLikeConverter.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexToLikeConverter.java @@ -1,4 +1,4 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.sqlite; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java similarity index 99% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java index a721b03c..5e70ff63 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java @@ -1,4 +1,4 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.sqlite; import org.sqlite.Function; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; @@ -16,6 +16,7 @@ import uk.co.compendiumdev.thingifier.core.query.FilterBy; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.SortByFieldName; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import uk.co.compendiumdev.thingifier.core.reporting.RepositoryJsonExporter; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepositoryProvider.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepositoryProvider.java similarity index 94% rename from ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepositoryProvider.java rename to ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepositoryProvider.java index 835c629d..65e6ca1a 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/SqliteThingRepositoryProvider.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepositoryProvider.java @@ -1,7 +1,9 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.sqlite; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; import java.io.IOException; import java.nio.file.Files; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java index f147303b..2c8c087f 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java @@ -10,7 +10,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.Optionality; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.ArrayList; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java index b7ed05c3..4813685f 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java @@ -12,6 +12,10 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.RepositoryUrlQuery; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import java.nio.file.Path; import java.util.List; @@ -411,7 +415,7 @@ public void sqliteInvalidRegexFilterFails() { regexParams.put("title", "~=["); Assertions.assertThrows( - SqliteRegexFilterPolicy.UnsupportedRegexFilterException.class, + RuntimeException.class, () -> repository.listInstances(projectDefinition, regexParams)); } } @@ -431,7 +435,7 @@ public void sqliteTooComplexRegexFilterFails() { regexParams.put("title", "~=(.+)+"); Assertions.assertThrows( - SqliteRegexFilterPolicy.UnsupportedRegexFilterException.class, + RuntimeException.class, () -> repository.listInstances(projectDefinition, regexParams)); } } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfigTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfigTest.java index e4a33ed3..607025a5 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfigTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryProviderConfigTest.java @@ -3,6 +3,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import java.nio.file.Path; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexToLikeConverterTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexToLikeConverterTest.java similarity index 98% rename from ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexToLikeConverterTest.java rename to ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexToLikeConverterTest.java index 8bded696..9ab8cc01 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/SqliteRegexToLikeConverterTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteRegexToLikeConverterTest.java @@ -1,4 +1,4 @@ -package uk.co.compendiumdev.thingifier.core.repository; +package uk.co.compendiumdev.thingifier.core.repository.sqlite; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/standAloneSqlLiteTodoListManagerRestApi/src/main/java/uk/co/compendiumdev/todolist/sqlite/application/Main.java b/standAloneSqlLiteTodoListManagerRestApi/src/main/java/uk/co/compendiumdev/todolist/sqlite/application/Main.java index 094e1654..fed1225b 100644 --- a/standAloneSqlLiteTodoListManagerRestApi/src/main/java/uk/co/compendiumdev/todolist/sqlite/application/Main.java +++ b/standAloneSqlLiteTodoListManagerRestApi/src/main/java/uk/co/compendiumdev/todolist/sqlite/application/Main.java @@ -2,7 +2,7 @@ import uk.co.compendiumdev.thingifier.application.MainImplementation; import uk.co.compendiumdev.thingifier.application.examples.TodoManagerThingifier; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import static spark.Spark.get; diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java index 1056ef46..d4d98cad 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java @@ -15,7 +15,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.HashMap; diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java index d67496d3..7c4f2967 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java @@ -9,7 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.core.repository.SqliteThingRepositoryProvider; +import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.Map; From 4d3d1c63591c545ea722f366891098414d97b059 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Thu, 9 Jul 2026 14:55:10 +0100 Subject: [PATCH 12/22] Move entity instance writes behind repository drafts --- .../apimodel/ChallengeThingifier.java | 26 +-- .../apimodel/TodoAPIDataPopulator.java | 7 +- .../challengesrouting/AuthRoutes.java | 11 +- .../mirror/MirrorHttpApiRequestHandler.java | 5 +- .../simpleapi/SimpleAPITestDataPopulator.java | 14 +- .../simulation/SimulationRoutes.java | 16 +- .../ChallengeApiModelRepositoryTest.java | 28 ++- .../ChallengerApiResponseHookTest.java | 10 +- .../ThingifierRepositoryTestSupport.java | 24 --- .../ChallengeCompleteTest.java | 80 +++++--- checkstyle-project-fqn.xml | 70 ++++++- .../core/domain/instances/EntityInstance.java | 90 +++++--- .../instances/EntityInstanceCollection.java | 3 +- .../domain/instances/EntityInstanceDraft.java | 67 ++++++ .../EntityInstanceRelationships.java | 43 +++- .../EntityInstanceRepositoryAccess.java | 116 +++++++++++ .../EntityInstanceDraftValidator.java | 189 +++++++++++++++++ .../EntityInstanceStateValidator.java | 31 +++ .../core/repository/ThingRepository.java | 7 +- .../inmemory/InMemoryThingRepository.java | 41 +++- .../sqlite/SqliteThingRepository.java | 73 +++++-- .../EntityInstanceWriteValidator.java | 51 +++++ .../thingifier/core/EntityRelModelTest.java | 42 ++-- .../MultipleEntityInstancesDatabasesTest.java | 15 +- .../thingifier/core/ThingTest.java | 29 +-- .../domain/definitions/CardinalityTest.java | 39 ++-- .../instances/EntityInstanceDraftTest.java | 144 +++++++++++++ .../domain/instances/InstanceFieldsTest.java | 17 -- .../instances/ThingInstanceCreationTest.java | 18 -- .../domain/instances/fields/AutoGuidTest.java | 16 +- .../fields/AutoIncrementIdFieldTest.java | 47 ++--- .../instances/fields/BooleanFieldTest.java | 24 ++- .../instances/fields/EnumFieldTest.java | 15 +- .../instances/fields/FloatFieldTest.java | 12 +- .../instances/fields/IntegerFieldTest.java | 14 +- .../instances/fields/ObjectFieldTest.java | 34 ++- .../instances/fields/StringFieldTest.java | 30 +-- .../instances/fields/UniqueFieldTest.java | 17 +- .../core/query/QueryFiltersBooleanTest.java | 21 +- .../core/query/QueryFiltersFloatTest.java | 27 +-- .../core/query/QueryFiltersIdTest.java | 13 +- .../core/query/QueryFiltersIntegerTest.java | 33 +-- .../core/query/QueryFiltersStringTest.java | 27 +-- .../query/RepositoryUrlQueryTestSupport.java | 28 --- .../query/SortingViaQueryFiltersTest.java | 47 +++-- .../query/TodoManagerQueryEngineTest.java | 63 ++++-- .../ThingRepositoryContractTest.java | 193 ++++++++++++------ .../data/TodoAPIDataPopulator.java | 6 +- .../data/TodoManagerAPIDataPopulator.java | 21 +- pom.xml | 1 + .../swaggerizer/TodoAPITestDataPopulator.java | 5 +- .../api/ermodelconversion/JsonPopulator.java | 21 +- .../api/ermodelconversion/JsonThing.java | 63 +++++- .../restapihandlers/BodyArgsProcessor.java | 19 +- .../EntityInstanceBulkUpdater.java | 49 ++++- .../restapihandlers/RelationshipCreation.java | 5 +- .../restapihandlers/RestApiPostHandler.java | 8 +- .../api/restapihandlers/ThingAmendment.java | 53 ++--- .../api/restapihandlers/ThingCreation.java | 112 +++++----- .../htmlgen/DocumentationThingInstance.java | 19 +- .../java/uk/co/compendiumdev/UsageTest.java | 21 +- ...leVerbPutEntityInstanceApiNonHttpTest.java | 66 +++--- .../casestudy/todomanager/ModelsTest.java | 52 +++-- .../RelationshipApiNonHttpTest.java | 176 +++++++--------- .../RelationshipApiSqliteRepositoryTest.java | 40 ++-- ...erbDeleteEntityInstanceApiNonHttpTest.java | 13 +- .../VerbGetEntityInstanceApiNonHttpTest.java | 52 +++-- .../VerbPostEntityInstanceApiNonHttpTest.java | 29 ++- .../VerbPutEntityInstanceApiNonHttpTest.java | 44 ++-- .../http_api/DeleteRequestTest.java | 24 +-- .../http_api/JsonRequestResponseTest.java | 46 ++--- .../http_api/RelationshipHttpTest.java | 164 +++++++-------- .../http_api/XmlRequestResponseTest.java | 34 +-- .../unit/BasicTodoManagerTest.java | 95 +++++---- .../unit/OptionalityRelationshipTest.java | 89 ++++---- .../ThingifierHttpApiRequestHandlingTest.java | 33 +-- .../BulkUpdateEntityInstanceTest.java | 45 ++-- .../non_http/IdBasedInstanceNonHttpTest.java | 20 +- .../api/non_http/NestedObjectsApiTest.java | 24 +-- .../api/response/ApiResponseAsJsonTest.java | 14 +- .../api/response/ApiResponseAsXmlTest.java | 14 +- .../api/response/ApiResponseTest.java | 14 +- ...toryBackedRelationshipUrlResolverTest.java | 16 +- .../DefaultGuiHtmlPagesRepositoryTest.java | 7 +- .../thingifier/reporting/JsonThingTest.java | 7 +- .../thingifier/reporting/XmlThingTest.java | 7 +- .../ThingifierRepositoryTestSupport.java | 35 ---- 87 files changed, 2224 insertions(+), 1306 deletions(-) delete mode 100644 challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraft.java create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRepositoryAccess.java create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceDraftValidator.java create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceStateValidator.java create mode 100644 ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/validation/EntityInstanceWriteValidator.java create mode 100644 ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraftTest.java delete mode 100644 ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java delete mode 100644 thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java index d7e901c9..a1a231dc 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java @@ -9,6 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class ChallengeThingifier { @@ -51,11 +52,11 @@ public void populateThingifierFrom(ChallengeDefinitions challengeDefinitions) { // set all the status codes to the specific challenger status ThingRepository repository = challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); for (ChallengeDefinitionData challenge : challengeDefinitions.getChallenges()) { - EntityInstance instance = new EntityInstance(challengeDefn). - overrideValue("id", challenge.id). - setValue("name", challenge.name). - setValue("description", challenge.description); - repository.addInstance(instance); + repository.createInstance( + EntityInstanceDraft.forEntity(challengeDefn). + withProtectedField("id", challenge.id). + withField("name", challenge.name). + withField("description", challenge.description)); } } @@ -72,13 +73,14 @@ public void populateThingifierFromStatus(ChallengerAuthData challenger){ for (ChallengeDefinitionData challenge : challengeDefinitions.getChallenges()) { final EntityInstance instance = repository.findInstanceByFieldNameAndValue(challengeDefn, "id", challenge.id); - instance.setValue("status", - challengerToUse.statusOfChallenge( - challengeDefinitions. - getChallenge(challenge.name) - ).toString() - ); - repository.updateInstance(instance); + repository.patchInstance( + instance, + EntityInstanceDraft.forEntity(challengeDefn). + withField( + "status", + challengerToUse.statusOfChallenge( + challengeDefinitions.getChallenge(challenge.name)). + toString())); } } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java index 2a21b7a4..d7365179 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java @@ -3,7 +3,7 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoAPIDataPopulator implements RepositoryDataPopulator { @@ -25,9 +25,8 @@ public void populate(final ERSchema schema, final ThingRepository repository) { EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); for(String todoItem : todos){ - EntityInstance instance = new EntityInstance(todo). - setValue("title", todoItem); - repository.addInstance(instance); + repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", todoItem)); } } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java index 198b3dce..0923c220 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java @@ -24,6 +24,8 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.validation.MaximumLengthValidationRule; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.domain.instances.validation.EntityInstanceStateValidator; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; @@ -38,6 +40,7 @@ public class AuthRoutes { private EntityDefinition secretNote; private ThingifierHttpApi httpApi; private JsonThing jsonThing; + private final EntityInstanceStateValidator stateValidator = new EntityInstanceStateValidator(); public void configure(final Challengers challengers, final ThingifierApiDocumentationDefn apiDefn) { @@ -173,7 +176,8 @@ public void configure(final Challengers challengers, final HttpApiRequest myRequest = SparkToHttpApiRequest.convert(request); - EntityInstance note = new EntityInstance(secretNote).setValue("note", challenger.getNote()); + EntityInstance note = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(secretNote). + withField("note", challenger.getNote())); final ApiResponse response = ApiResponse.success().returnSingleInstance(note); final HttpApiResponse httpApiResponse = new HttpApiResponse(myRequest.getHeaders(), response, @@ -278,8 +282,9 @@ public void configure(final Challengers challengers, EntityInstance returnedInstance = response.getReturnedInstance(); final List protectedFieldNames = returnedInstance.getEntity().getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); - ValidationReport validity = returnedInstance.validateFieldValues(protectedFieldNames, false); - validity.combine(returnedInstance.validateRelationships()); + ValidationReport validity = stateValidator.validateFields( + returnedInstance, protectedFieldNames, false); + validity.combine(stateValidator.validateRelationships(returnedInstance)); this.secretNoteStore.deleteThing(response.getReturnedInstance(), EntityRelModel.DEFAULT_DATABASE_NAME); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/mirror/MirrorHttpApiRequestHandler.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/mirror/MirrorHttpApiRequestHandler.java index e40ead53..a3d29356 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/mirror/MirrorHttpApiRequestHandler.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/mirror/MirrorHttpApiRequestHandler.java @@ -6,6 +6,7 @@ import uk.co.compendiumdev.thingifier.application.routehandlers.HttpApiRequestHandler; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class MirrorHttpApiRequestHandler implements HttpApiRequestHandler { private final EntityDefinition entityDefn; @@ -31,8 +32,8 @@ public ApiResponse handle(final HttpApiRequest myRequest) { if(response==null) { // let main code handle formatting etc. - EntityInstance fake = new EntityInstance(entityDefn). - setValue("details", requestDetails); + EntityInstance fake = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityDefn). + withField("details", requestDetails)); response = ApiResponse.success().returnSingleInstance(fake); } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java index 3ac3c815..d72cec36 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simpleapi/SimpleAPITestDataPopulator.java @@ -3,7 +3,7 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.Random; @@ -26,12 +26,12 @@ public void populate(final ERSchema schema, final ThingRepository repository) { Random random = new Random(); for(String type : types){ - EntityInstance instance = new EntityInstance(item). - setValue("type", type). - setValue("numberinstock", String.valueOf(random.nextInt(20))). - setValue("isbn13", randomIsbn(random)). - setValue("price", String.valueOf(random.nextInt(99)) + "." + String.valueOf(random.nextInt(99)) ); - repository.addInstance(instance); + repository.createInstance( + EntityInstanceDraft.forEntity(item). + withField("type", type). + withField("numberinstock", String.valueOf(random.nextInt(20))). + withField("isbn13", randomIsbn(random)). + withField("price", String.valueOf(random.nextInt(99)) + "." + random.nextInt(99))); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java index 3cd26954..6cd8f5f9 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/practicemodes/simulation/SimulationRoutes.java @@ -17,6 +17,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.validation.MaximumLengthValidationRule; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.query.FilterBy; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; @@ -131,9 +132,8 @@ private void setUpDocumentation() { } private EntityInstance createManagedEntityNamed(final String name) { - EntityInstance instance = new EntityInstance(entityDefn).setValue("name", name); - entityRepository.addInstance(instance); - return instance; + return entityRepository.createInstance( + EntityInstanceDraft.forEntity(entityDefn).withField("name", name)); } public void configure() { @@ -204,8 +204,9 @@ public void configure() { if (id.equals("10")) { // 10 is the entity we amend to name:eris - EntityInstance fake = new EntityInstance(entityDefn). - overrideValue("id", "10").setValue("name", "eris"); + EntityInstance fake = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityDefn). + withProtectedField("id", "10"). + withField("name", "eris")); instance = fake; response = ApiResponse.success().returnSingleInstance(instance); } @@ -258,8 +259,9 @@ public void configure() { } else { if (id.equals("10")) { // 10 is the entity we amend to name:eris - EntityInstance fake = new EntityInstance(entityDefn). - overrideValue("id", "10").setValue("name", "eris"); + EntityInstance fake = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityDefn). + withProtectedField("id", "10"). + withField("name", "eris")); response = ApiResponse.success().returnSingleInstance(fake); } else { final EntityInstance instance = entityRepository.findInstanceByPrimaryKey(entityDefn, id); diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java index a40c060b..bfaf6113 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModelRepositoryTest.java @@ -1,7 +1,6 @@ package uk.co.compendiumdev.challenge.apimodel; -import uk.co.compendiumdev.challenge.testsupport.ThingifierRepositoryTestSupport; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.Thingifier; @@ -15,6 +14,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepository; import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; @@ -32,13 +32,8 @@ public void challengeApiCanUseSqliteInMemoryRepository() { instanceof SqliteThingRepository); Assertions.assertEquals( 10, - ThingifierRepositoryTestSupport.repository( - thingifier, - EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( - ThingifierRepositoryTestSupport.entity( - thingifier, - EntityRelModel.DEFAULT_DATABASE_NAME, - "todo"))); + thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"))); } } @@ -50,8 +45,9 @@ public void challengeApiGetUsesSqliteRepositoryReadsForSimpleTodoQueries() { EntityDefinition todo = thingifier.getDefinitionNamed("todo"); EntityInstance firstTodo = repository.findInstanceByQueryIdentifier(todo, "1"); - firstTodo.setValue("doneStatus", "true"); - repository.updateInstance(firstTodo); + repository.patchInstance( + firstTodo, + EntityInstanceDraft.forEntity(todo).withField("doneStatus", "true")); ApiResponse allTodos = thingifier.api().get( "/todos", new QueryFilterParams(), new HttpHeadersBlock()); @@ -108,10 +104,10 @@ public void relationshipGetRoutesStillFallBackToLegacyQueryTraversal() { whenReversed(Cardinality.ONE_TO_ONE(), "task-of"); ThingRepository repository = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance projectInstance = new EntityInstance(project).setValue("title", "Office"); - EntityInstance taskInstance = new EntityInstance(task).setValue("title", "File paperwork"); - repository.addInstance(projectInstance); - repository.addInstance(taskInstance); + EntityInstance projectInstance = repository.createInstance( + EntityInstanceDraft.forEntity(project).withField("title", "Office")); + EntityInstance taskInstance = repository.createInstance( + EntityInstanceDraft.forEntity(task).withField("title", "File paperwork")); repository.connectRelationship(projectInstance, "tasks", taskInstance); ApiResponse response = thingifier.api().get( @@ -191,8 +187,8 @@ public void sqliteMemoryProvidersHaveSeparateNamedDatabaseLifetimes() { secondNote.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); secondNote.addField(Field.is("title", FieldType.STRING)); - firstModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). - addInstance(new EntityInstance(firstNote).setValue("title", "first")); + firstModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance( + EntityInstanceDraft.forEntity(firstNote).withField("title", "first")); Assertions.assertEquals( 1, diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java index b93503f2..b514a006 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHookTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.challenge.challengehooks; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -186,10 +187,11 @@ EntityInstance addTodo( final String title, final String doneStatus, final String description) { - return repository.addInstance(new EntityInstance(todo). - setValue("title", title). - setValue("doneStatus", doneStatus). - setValue("description", description)); + return repository.createInstance( + EntityInstanceDraft.forEntity(todo). + withField("title", title). + withField("doneStatus", doneStatus). + withField("description", description)); } HttpApiRequest request(final String path, final HttpApiRequest.VERB verb) { diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java b/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java deleted file mode 100644 index fe6fc615..00000000 --- a/challenger/src/test/java/uk/co/compendiumdev/challenge/testsupport/ThingifierRepositoryTestSupport.java +++ /dev/null @@ -1,24 +0,0 @@ -package uk.co.compendiumdev.challenge.testsupport; - -import uk.co.compendiumdev.thingifier.Thingifier; -import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; - -public final class ThingifierRepositoryTestSupport { - - private ThingifierRepositoryTestSupport() { - } - - public static ThingRepository repository( - final Thingifier thingifier, - final String databaseName) { - return thingifier.getRepository(databaseName); - } - - public static EntityDefinition entity( - final Thingifier thingifier, - final String databaseName, - final String entityName) { - return thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); - } -} diff --git a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java index 9357364f..a2d26ecf 100644 --- a/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java +++ b/challenger/src/test/java/uk/co/compendiumdev/challenger/http/completechallenges/ChallengeCompleteTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.challenger.http.completechallenges; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.*; import org.slf4j.Logger; @@ -13,8 +14,8 @@ import uk.co.compendiumdev.challenger.payloads.Todo; import uk.co.compendiumdev.challenger.payloads.Todos; import uk.co.compendiumdev.sparkstart.Environment; -import uk.co.compendiumdev.challenge.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.ArrayList; import java.util.HashMap; @@ -241,7 +242,7 @@ public void canPutTodos400FailCreatePass() { @Test public void canPutTodosFull200AmendPass() { - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -249,7 +250,9 @@ public void canPutTodosFull200AmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); + ThingRepository repository = ChallengeMain.getChallenger(). + getThingifier().getRepository(challenger.getXChallenger()); + EntityInstance aTodo = new ArrayList<>(repository.listInstances(todos)).get(0); // amend a to do successfully final HttpResponseDetails response = @@ -267,7 +270,7 @@ public void canPutTodosFull200AmendPass() { @Test public void canPutTodosPartial200AmendPass() { - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -275,7 +278,9 @@ public void canPutTodosPartial200AmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); + ThingRepository repository = ChallengeMain.getChallenger(). + getThingifier().getRepository(challenger.getXChallenger()); + EntityInstance aTodo = new ArrayList<>(repository.listInstances(todos)).get(0); // amend a to do successfully final HttpResponseDetails response = @@ -291,7 +296,7 @@ public void canPutTodosPartial200AmendPass() { @Test public void canPutTodos200MissingTitleAmendPass() { - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -299,7 +304,9 @@ public void canPutTodos200MissingTitleAmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); + ThingRepository repository = ChallengeMain.getChallenger(). + getThingifier().getRepository(challenger.getXChallenger()); + EntityInstance aTodo = new ArrayList<>(repository.listInstances(todos)).get(0); // amend a to do unsuccessfully final HttpResponseDetails response = @@ -315,7 +322,7 @@ public void canPutTodos200MissingTitleAmendPass() { @Test public void canNotPutTodos400ChangeIdAmendPass() { - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -323,7 +330,9 @@ public void canNotPutTodos400ChangeIdAmendPass() { headers.putAll(x_challenger_header); headers.put("Content-Type", "application/json"); - EntityInstance aTodo = new ArrayList<>(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)).get(0); + ThingRepository repository = ChallengeMain.getChallenger(). + getThingifier().getRepository(challenger.getXChallenger()); + EntityInstance aTodo = new ArrayList<>(repository.listInstances(todos)).get(0); // amend a to do unsuccessfully final HttpResponseDetails response = @@ -482,13 +491,24 @@ private String stringOfLength(final int desiredLength) { return ofLength; } + private EntityInstance createTodo(final EntityDefinition todos, final String title) { + return createTodo(todos, title, "false"); + } + + private EntityInstance createTodo( + final EntityDefinition todos, + final String title, + final String doneStatus) { + return ChallengeMain.getChallenger().getThingifier().getRepository(challenger.getXChallenger()).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", title).withField("doneStatus", doneStatus)); + } + @Test public void canPostTodosUpdatePass() { ensureAtMostXTodoAvailable(10); - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - final EntityInstance todo = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance( new EntityInstance(todos)); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + final EntityInstance todo = createTodo(todos, "post update fixture"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -621,10 +641,12 @@ public void canPostToAddMaxTodosPass() { } public void ensureAtMostXTodoAvailable(int x){ - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - if(ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).countInstances(todos)>x){ - for(int delCount = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).countInstances(todos)-x; delCount > 0; delCount--) { - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).deleteEntityInstance((EntityInstance) (ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos).toArray()[0])); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + ThingRepository repository = ChallengeMain.getChallenger(). + getThingifier().getRepository(challenger.getXChallenger()); + if(repository.countInstances(todos)>x){ + for(int delCount = repository.countInstances(todos)-x; delCount > 0; delCount--) { + repository.deleteEntityInstance((EntityInstance) (repository.listInstances(todos).toArray()[0])); } } } @@ -634,8 +656,8 @@ public void canDeleteTodosPass() { ensureAtMostXTodoAvailable(10); - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - final EntityInstance todo = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance( new EntityInstance(todos)); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + final EntityInstance todo = createTodo(todos, "delete fixture"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -653,8 +675,8 @@ public void canDeleteTodosPass() { @Test public void canGetSpecificTodoPass() { - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); - final EntityInstance todo = ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + final EntityInstance todo = createTodo(todos, "specific todo fixture"); Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); @@ -768,10 +790,10 @@ public void canFilterTodoPass() { Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)).setValue("doneStatus", "true"); - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)).setValue("doneStatus", "false"); + createTodo(todos, "done filter fixture", "true"); + createTodo(todos, "not done filter fixture", "false"); final HttpResponseDetails response = http.send("/todos?doneStatus=true", "GET", x_challenger_header, ""); @@ -1108,9 +1130,11 @@ public void canDeleteAllTodos() { Map x_challenger_header = getXChallengerHeader(challenger.getXChallenger()); - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger(), "todo"); + final EntityDefinition todos = ChallengeMain.getChallenger().getThingifier().getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - for(EntityInstance instance : ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).listInstances(todos)){ + ThingRepository repository = ChallengeMain.getChallenger(). + getThingifier().getRepository(challenger.getXChallenger()); + for(EntityInstance instance : repository.listInstances(todos)){ final HttpResponseDetails response = http.send("/todos/" + instance.getFieldValue("id").asString(), "DELETE", @@ -1122,10 +1146,10 @@ public void canDeleteAllTodos() { // add some todos in case this is not the last test - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); - ThingifierRepositoryTestSupport.repository(ChallengeMain.getChallenger().getThingifier(), challenger.getXChallenger()).addInstance(new EntityInstance(todos)); + createTodo(todos, "restored fixture 1"); + createTodo(todos, "restored fixture 2"); + createTodo(todos, "restored fixture 3"); + createTodo(todos, "restored fixture 4"); } diff --git a/checkstyle-project-fqn.xml b/checkstyle-project-fqn.xml index 68cb87b2..bac1f0a5 100644 --- a/checkstyle-project-fqn.xml +++ b/checkstyle-project-fqn.xml @@ -16,12 +16,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + + + + + + + + + + + + + + + + + @@ -41,6 +87,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstance.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstance.java index 021d1339..9b66ed90 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstance.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstance.java @@ -1,6 +1,6 @@ package uk.co.compendiumdev.thingifier.core.domain.instances; -import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; @@ -18,38 +18,42 @@ public class EntityInstance { // used internally to reference the instance, is not exposed to the world private final UUID internalId; + private boolean repositoryOwned; - public EntityInstance(EntityDefinition eDefn) { + EntityInstance(EntityDefinition eDefn) { this(eDefn, UUID.randomUUID()); } - public EntityInstance(EntityDefinition eDefn, UUID internalId) { + EntityInstance(EntityDefinition eDefn, UUID internalId) { this.entityDefinition = eDefn; this.instanceFields = eDefn.instantiateFields(); this.relationships = new EntityInstanceRelationships(this); this.internalId = internalId; + this.repositoryOwned = false; } - public EntityInstance addAutoGUIDstoInstance(){ - // allow GUIDs to be defined as being 'auto' in which case we will auto generate them - List autoGuids = entityDefinition.getFieldsOfType(FieldType.AUTO_GUID); - for(Field autoGuid : autoGuids){ - instanceFields.addValue(entityDefinition.getField(autoGuid.getName()).valueFor( UUID.randomUUID().toString())); - } - - return this; + public static EntityInstance fromDraft(final EntityInstanceDraft draft) { + return fromDraft(draft, UUID.randomUUID().toString()); } + private static EntityInstance fromDraft( + final EntityInstanceDraft draft, + final String internalId) { + EntityInstance instance = new EntityInstance( + draft.getEntity(), + UUID.fromString(internalId)); + return instance.applyDraftFromRepository(draft); + } - public void addAutoIncrementIdsToInstance(Map autos) { - - for(Field autoIncrementedField : entityDefinition.getFieldsOfType(FieldType.AUTO_INCREMENT)){ - AutoIncrement auto = autos.get(autoIncrementedField.getName()); - if(!instanceFields.hasAssignedValue(autoIncrementedField.getName())){ - instanceFields.putValue(autoIncrementedField.getName(), - String.valueOf(auto.getNextValueAndUpdate())); - } + EntityInstance applyDraftFromRepository(final EntityInstanceDraft draft) { + draft.validate(); + for (NamedValue value : draft.getFieldValues()) { + setValueFromRepository(value.getName(), value.asString()); } + for (NamedValue value : draft.getProtectedFieldValues()) { + overrideValueFromRepository(value.getName(), value.asString()); + } + return this; } public String toString() { @@ -92,13 +96,23 @@ public List getFieldNames() { return this.entityDefinition.getFieldNames(); } - public EntityInstance setValue(String fieldName, String value) { + EntityInstance setValue(String fieldName, String value) { + ensureWritable(); + return setValueFromRepository(fieldName, value); + } + + EntityInstance setValueFromRepository(String fieldName, String value) { instanceFields.setValue(fieldName, value); return this; } - public EntityInstance overrideValue(final String key, final String value) { + EntityInstance overrideValue(final String key, final String value) { + ensureWritable(); + return overrideValueFromRepository(key, value); + } + + EntityInstance overrideValueFromRepository(final String key, final String value) { // bypass all validation - except, field must exist this.instanceFields.putValue(key, value); return this; @@ -116,7 +130,7 @@ public EntityDefinition getEntity() { /** * connect this thing to another thing using the relationship relationshipName */ - public EntityInstanceRelationships getRelationships(){ + EntityInstanceRelationships getRelationships(){ return relationships; } @@ -147,9 +161,22 @@ public ValidationReport validate() { return report; } + public boolean hasRelationshipInstances() { + return relationships.hasAnyRelationshipInstances(); + } + + public Collection getRelatedItems(final String relationshipName) { + return relationships.getConnectedItems(relationshipName); + } + // Cloning and documentation - public void clearAllFields() { + void clearAllFields() { + ensureWritable(); + clearAllFieldsFromRepository(); + } + + void clearAllFieldsFromRepository() { ListignoreFields = new ArrayList<>(); ignoreFields.addAll(getEntity(). @@ -160,8 +187,8 @@ public void clearAllFields() { instanceFields.deleteAllFieldValuesExcept(ignoreFields); } - public EntityInstance createDuplicateWithoutRelationships() { - EntityInstance cloneInstance = new EntityInstance(entityDefinition); + EntityInstance createDuplicateWithoutRelationships(final String internalId) { + EntityInstance cloneInstance = new EntityInstance(entityDefinition, UUID.fromString(internalId)); for(String fieldName : instanceFields.getDefinition().getFieldNames()){ FieldValue value = instanceFields.getAssignedValue(fieldName); @@ -174,10 +201,21 @@ public EntityInstance createDuplicateWithoutRelationships() { } - public InstanceFields getFields() { + InstanceFields getFields() { return instanceFields; } + void lockForRepository() { + repositoryOwned = true; + } + + void ensureWritable() { + if (repositoryOwned) { + throw new IllegalStateException( + "EntityInstance is repository-owned and read-only; use ThingRepository write APIs"); + } + } + public boolean hasInstantiatedFieldNamed(String fieldName) { diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java index 3a3c6961..3090dcef 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceCollection.java @@ -216,7 +216,8 @@ public List deleteInstance(EntityInstance anInstance) { instances.remove(anInstance.getInternalId()); - final List alsoDelete = anInstance.getRelationships().removeAllRelationships(); + final List alsoDelete = + anInstance.getRelationships().removeAllRelationshipsFromRepository(); return alsoDelete; } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraft.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraft.java new file mode 100644 index 00000000..69556f11 --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraft.java @@ -0,0 +1,67 @@ +package uk.co.compendiumdev.thingifier.core.domain.instances; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; +import uk.co.compendiumdev.thingifier.core.domain.instances.validation.EntityInstanceDraftValidator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class EntityInstanceDraft { + + private static final EntityInstanceDraftValidator VALIDATOR = + new EntityInstanceDraftValidator(); + + private final EntityDefinition entityDefinition; + private final List fieldValues; + private final List protectedFieldValues; + + private EntityInstanceDraft(final EntityDefinition entityDefinition) { + this.entityDefinition = entityDefinition; + this.fieldValues = new ArrayList<>(); + this.protectedFieldValues = new ArrayList<>(); + } + + public static EntityInstanceDraft forEntity(final EntityDefinition entityDefinition) { + return new EntityInstanceDraft(entityDefinition); + } + + public static EntityInstanceDraft fromNamedValues( + final EntityDefinition entityDefinition, + final List values) { + EntityInstanceDraft draft = forEntity(entityDefinition); + for (NamedValue value : values) { + draft.withField(value.getName(), value.asString()); + } + return draft; + } + + public EntityInstanceDraft withField(final String name, final String value) { + VALIDATOR.assertCanAddField(entityDefinition, name, value); + fieldValues.add(new NamedValue(name, value)); + return this; + } + + public EntityInstanceDraft withProtectedField(final String name, final String value) { + VALIDATOR.assertCanAddProtectedField(entityDefinition, name, value); + protectedFieldValues.add(new NamedValue(name, value)); + return this; + } + + public EntityDefinition getEntity() { + return entityDefinition; + } + + public List getFieldValues() { + return Collections.unmodifiableList(fieldValues); + } + + public List getProtectedFieldValues() { + return Collections.unmodifiableList(protectedFieldValues); + } + + void validate() { + VALIDATOR.assertValid(this); + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRelationships.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRelationships.java index 517cc5c0..c8f95b55 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRelationships.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRelationships.java @@ -32,6 +32,12 @@ public String toString() { } public void connect(final String relationshipName, final EntityInstance thing) { + forThis.ensureWritable(); + thing.ensureWritable(); + connectFromRepository(relationshipName, thing); + } + + void connectFromRepository(final String relationshipName, final EntityInstance thing) { // TODO: enforce cardinality final EntityDefinition entityDefinition = forThis.getEntity(); @@ -60,15 +66,20 @@ public void connect(final String relationshipName, final EntityInstance thing) { relationship, forThis, thing); - add(related); + addFromRepository(related); if (relationship.getRelationshipDefinition().isTwoWay()) { - thing.getRelationships().add(related); + thing.getRelationships().addFromRepository(related); } } private void add(final RelationshipVectorInstance relationship) { + forThis.ensureWritable(); + addFromRepository(relationship); + } + + private void addFromRepository(final RelationshipVectorInstance relationship) { String instanceIdentification = ""; @@ -142,6 +153,14 @@ public List getConnectedItemsOfType(final String type) { public List removeRelationshipsInvolving(final EntityInstance thing, final String relationshipName) { + forThis.ensureWritable(); + thing.ensureWritable(); + return removeRelationshipsInvolvingFromRepository(thing, relationshipName); + } + + List removeRelationshipsInvolvingFromRepository( + final EntityInstance thing, + final String relationshipName) { List thingsToDelete = new ArrayList<>(); List toDelete = new ArrayList<>(); @@ -152,7 +171,7 @@ public List removeRelationshipsInvolving(final EntityInstance th toDelete.add(relationship); thingsToDelete.addAll(relationship.instancesSubjectToMandatoryRelationship()); // delete any relationship to or from - thing.getRelationships().remove(relationship); + thing.getRelationships().removeFromRepository(relationship); } } } @@ -167,6 +186,11 @@ public List removeRelationshipsInvolving(final EntityInstance th 'things' that are no longer valid since they were involved in a mandatory relationship. */ public List removeAllRelationships() { + forThis.ensureWritable(); + return removeAllRelationshipsFromRepository(); + } + + List removeAllRelationshipsFromRepository() { List deleteThese = new ArrayList<>(); final EntityInstance me = forThis; @@ -181,7 +205,7 @@ public List removeAllRelationships() { them = relationship.getFrom(); } - them.getRelationships().removeAllRelationshipsInvolving(me); + them.getRelationships().removeAllRelationshipsInvolvingFromRepository(me); deleteThese.addAll(relationship.instancesSubjectToMandatoryRelationship()); } @@ -191,10 +215,21 @@ public List removeAllRelationships() { } private void remove(final RelationshipVectorInstance relationship) { + forThis.ensureWritable(); + removeFromRepository(relationship); + } + + private void removeFromRepository(final RelationshipVectorInstance relationship) { relationships.remove(relationship); } public List removeAllRelationshipsInvolving(final EntityInstance thing) { + forThis.ensureWritable(); + thing.ensureWritable(); + return removeAllRelationshipsInvolvingFromRepository(thing); + } + + List removeAllRelationshipsInvolvingFromRepository(final EntityInstance thing) { List instancesToDelete = new ArrayList<>(); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRepositoryAccess.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRepositoryAccess.java new file mode 100644 index 00000000..883a894f --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceRepositoryAccess.java @@ -0,0 +1,116 @@ +package uk.co.compendiumdev.thingifier.core.domain.instances; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; +import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public final class EntityInstanceRepositoryAccess { + + private EntityInstanceRepositoryAccess() { + } + + public static EntityInstance patch( + final EntityInstance existing, + final EntityInstanceDraft draft) { + EntityInstance patched = existing.createDuplicateWithoutRelationships(existing.getInternalId()); + return apply(patched, draft); + } + + public static EntityInstance replace( + final EntityInstance existing, + final EntityInstanceDraft draft) { + EntityInstance replacement = existing.createDuplicateWithoutRelationships(existing.getInternalId()); + clearAllFields(replacement); + return apply(replacement, draft); + } + + public static EntityInstance apply( + final EntityInstance existing, + final EntityInstanceDraft draft) { + return existing.applyDraftFromRepository(draft); + } + + public static EntityInstance empty(final EntityDefinition entity) { + return new EntityInstance(entity); + } + + public static EntityInstance empty( + final EntityDefinition entity, + final java.util.UUID internalId) { + return new EntityInstance(entity, internalId); + } + + public static void setValue( + final EntityInstance instance, + final String fieldName, + final String value) { + instance.setValueFromRepository(fieldName, value); + } + + public static void overrideValue( + final EntityInstance instance, + final String fieldName, + final String value) { + instance.overrideValueFromRepository(fieldName, value); + } + + public static void clearAllFields(final EntityInstance instance) { + instance.clearAllFieldsFromRepository(); + } + + public static InstanceFields fields(final EntityInstance instance) { + return instance.getFields(); + } + + public static void connectRelationship( + final EntityInstance from, + final String relationshipName, + final EntityInstance to) { + from.getRelationships().connectFromRepository(relationshipName, to); + } + + public static List removeRelationshipsInvolving( + final EntityInstance parent, + final EntityInstance child, + final String relationshipName) { + return parent.getRelationships(). + removeRelationshipsInvolvingFromRepository(child, relationshipName); + } + + public static List removeAllRelationships(final EntityInstance instance) { + return instance.getRelationships().removeAllRelationshipsFromRepository(); + } + + public static Collection connectedItems( + final EntityInstance instance, + final String relationshipName) { + return instance.getRelationships().getConnectedItems(relationshipName); + } + + public static ValidationReport validateFieldValues( + final EntityInstance instance, + final List excluding, + final boolean allowedToSetIds) { + return instance.validateFieldValues(excluding, allowedToSetIds); + } + + public static ValidationReport validateRelationships(final EntityInstance instance) { + return instance.validateRelationships(); + } + + public static List findAnyGuidOrIdDifferences( + final EntityInstance instance, + final List fieldValues) { + return instance.getFields().findAnyGuidOrIdDifferences(fieldValues); + } + + public static EntityInstance lock(final EntityInstance instance) { + instance.lockForRepository(); + return instance; + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceDraftValidator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceDraftValidator.java new file mode 100644 index 00000000..f0396c03 --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceDraftValidator.java @@ -0,0 +1,189 @@ +package uk.co.compendiumdev.thingifier.core.domain.instances.validation; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.DefinedFields; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; +import uk.co.compendiumdev.thingifier.core.domain.definitions.validation.TypeValidationFailedMessageGenerator; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +public final class EntityInstanceDraftValidator { + + public void assertCanAddField( + final EntityDefinition entityDefinition, + final String name, + final String value) { + throwIfInvalid(validateFieldValue(entityDefinition, name, value, false)); + } + + public void assertCanAddProtectedField( + final EntityDefinition entityDefinition, + final String name, + final String value) { + throwIfInvalid(validateFieldValue(entityDefinition, name, value, true)); + } + + public void assertValid(final EntityInstanceDraft draft) { + throwIfInvalid(validate(draft)); + } + + public ValidationReport validate(final EntityInstanceDraft draft) { + ValidationReport validation = new ValidationReport(); + for (NamedValue value : draft.getFieldValues()) { + validation.combine( + validateFieldValue( + draft.getEntity(), + value.getName(), + value.asString(), + false)); + } + for (NamedValue value : draft.getProtectedFieldValues()) { + validation.combine( + validateFieldValue( + draft.getEntity(), + value.getName(), + value.asString(), + true)); + } + return validation; + } + + private ValidationReport validateFieldValue( + final EntityDefinition entityDefinition, + final String name, + final String value, + final boolean protectedWrite) { + ValidationReport report = new ValidationReport(); + Field field = fieldForPath(entityDefinition, name); + + if (field == null) { + report.setValid(false); + report.addErrorMessage("Could not find field: " + displayName(name)); + return report; + } + + if (protectedWrite && !isProtected(field)) { + report.setValid(false); + report.addErrorMessage( + "%s : field is not protected and should be set with withField".formatted(name)); + return report; + } + + if (!protectedWrite && isProtected(field)) { + report.setValid(false); + report.addErrorMessage( + "%s : field is protected and can only be set with withProtectedField". + formatted(name)); + return report; + } + + if (value == null) { + return report; + } + + FieldValue fieldValue = FieldValue.is(field, value); + try { + report.combine(field.validate(fieldValue, protectedWrite)); + validateProtectedAutoIncrement(field, fieldValue, report); + validateProtectedAutoGuid(field, fieldValue, report); + } catch (RuntimeException e) { + report.setValid(false); + report.addErrorMessage( + TypeValidationFailedMessageGenerator.thisValueDoesNotMatchType( + fieldValue, field.getType())); + } + + return report; + } + + private void validateProtectedAutoIncrement( + final Field field, + final FieldValue fieldValue, + final ValidationReport report) { + if (field.getType() != FieldType.AUTO_INCREMENT) { + return; + } + + try { + fieldValue.asInteger(); + } catch (RuntimeException e) { + report.setValid(false); + report.addErrorMessage( + TypeValidationFailedMessageGenerator.thisValueDoesNotMatchType( + fieldValue, FieldType.AUTO_INCREMENT)); + } + } + + private void validateProtectedAutoGuid( + final Field field, + final FieldValue fieldValue, + final ValidationReport report) { + if (field.getType() != FieldType.AUTO_GUID) { + return; + } + + try { + UUID.fromString(fieldValue.asString()); + } catch (RuntimeException e) { + report.setValid(false); + report.addErrorMessage( + TypeValidationFailedMessageGenerator.thisValueDoesNotMatchType( + fieldValue, FieldType.AUTO_GUID)); + } + } + + private Field fieldForPath(final EntityDefinition entityDefinition, final String name) { + if (name == null || name.trim().isEmpty()) { + return null; + } + + List path = new ArrayList<>(Arrays.asList(name.split("\\.", -1))); + if (path.stream().anyMatch(String::isEmpty)) { + return null; + } + + String fieldName = path.remove(0); + if (!entityDefinition.hasFieldNameDefined(fieldName)) { + return null; + } + + Field field = entityDefinition.getField(fieldName); + while (!path.isEmpty()) { + if (field.getType() != FieldType.OBJECT || field.getObjectDefinition() == null) { + throw new IllegalArgumentException( + "Cannot reference fields on non object fields: " + field.getName()); + } + + DefinedFields objectDefinition = field.getObjectDefinition(); + fieldName = path.remove(0); + if (!objectDefinition.hasFieldNameDefined(fieldName)) { + throw new IllegalArgumentException("Could not find field: " + fieldName); + } + field = objectDefinition.getField(fieldName); + } + return field; + } + + private boolean isProtected(final Field field) { + return field.getType() == FieldType.AUTO_INCREMENT || + field.getType() == FieldType.AUTO_GUID; + } + + private void throwIfInvalid(final ValidationReport report) { + if (!report.isValid()) { + throw new IllegalArgumentException(report.getCombinedErrorMessages()); + } + } + + private String displayName(final String name) { + return name == null ? "" : name; + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceStateValidator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceStateValidator.java new file mode 100644 index 00000000..f31c220f --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/domain/instances/validation/EntityInstanceStateValidator.java @@ -0,0 +1,31 @@ +package uk.co.compendiumdev.thingifier.core.domain.instances.validation; + +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceRepositoryAccess; +import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; + +import java.util.ArrayList; +import java.util.List; + +public final class EntityInstanceStateValidator { + + public ValidationReport validate(final EntityInstance instance) { + ValidationReport report = validateFields(instance, new ArrayList<>(), false); + report.combine(validateRelationships(instance)); + return report; + } + + public ValidationReport validateFields( + final EntityInstance instance, + final List excluding, + final boolean allowedToSetIds) { + return EntityInstanceRepositoryAccess.validateFieldValues( + instance, + excluding, + allowedToSetIds); + } + + public ValidationReport validateRelationships(final EntityInstance instance) { + return EntityInstanceRepositoryAccess.validateRelationships(instance); + } +} diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java index 1a28f6d9..360a5089 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepository.java @@ -5,6 +5,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.reporting.ERModelReport; import uk.co.compendiumdev.thingifier.core.reporting.RepositoryJsonExporter; @@ -52,9 +53,11 @@ default List listRelatedInstances( List listRelatedInstances( EntityInstance instance, String relationshipName, QueryFilterParams queryParams); - EntityInstance addInstance(EntityInstance instance); + EntityInstance createInstance(EntityInstanceDraft draft); - EntityInstance updateInstance(EntityInstance instance); + EntityInstance patchInstance(EntityInstance instance, EntityInstanceDraft draft); + + EntityInstance replaceInstance(EntityInstance instance, EntityInstanceDraft draft); void deleteEntityInstance(EntityInstance instance); diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java index 2a250ff0..c0c9faa3 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/inmemory/InMemoryThingRepository.java @@ -9,10 +9,13 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.ERInstanceData; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceRepositoryAccess; import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListFilter; import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListSorter; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.validation.EntityInstanceWriteValidator; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.ArrayList; @@ -24,6 +27,7 @@ public class InMemoryThingRepository implements ThingRepository { private final String databaseKey; private final ERInstanceData instanceData; + private final EntityInstanceWriteValidator writeValidator; public InMemoryThingRepository(final String databaseKey) { this(databaseKey, new ERInstanceData()); @@ -32,6 +36,7 @@ public InMemoryThingRepository(final String databaseKey) { InMemoryThingRepository(final String databaseKey, final ERInstanceData instanceData) { this.databaseKey = databaseKey; this.instanceData = instanceData; + this.writeValidator = new EntityInstanceWriteValidator(this); } @Override @@ -136,14 +141,32 @@ public EntityInstance findInstanceByQueryIdentifier( } @Override - public EntityInstance addInstance(final EntityInstance instance) { + public EntityInstance createInstance(final EntityInstanceDraft draft) { + EntityInstance instance = EntityInstance.fromDraft(draft); + writeValidator.assertValidForCreate(instance); getInstanceCollectionForEntityNamed(instance.getEntity().getName()).addInstance(instance); - return instance; + return EntityInstanceRepositoryAccess.lock(instance); } @Override - public EntityInstance updateInstance(final EntityInstance instance) { - return instance; + public EntityInstance patchInstance( + final EntityInstance instance, + final EntityInstanceDraft draft) { + EntityInstance candidate = EntityInstanceRepositoryAccess.patch(instance, draft); + writeValidator.assertValidForAmendment(candidate); + return EntityInstanceRepositoryAccess.lock( + EntityInstanceRepositoryAccess.apply(instance, draft)); + } + + @Override + public EntityInstance replaceInstance( + final EntityInstance instance, + final EntityInstanceDraft draft) { + EntityInstance candidate = EntityInstanceRepositoryAccess.replace(instance, draft); + writeValidator.assertValidForAmendment(candidate); + EntityInstanceRepositoryAccess.clearAllFields(instance); + return EntityInstanceRepositoryAccess.lock( + EntityInstanceRepositoryAccess.apply(instance, draft)); } @Override @@ -204,7 +227,7 @@ public void setNextIdCountersToAccomodate( @Override public void connectRelationship( final EntityInstance from, final String relationshipName, final EntityInstance to) { - from.getRelationships().connect(relationshipName, to); + EntityInstanceRepositoryAccess.connectRelationship(from, relationshipName, to); } @Override @@ -212,18 +235,19 @@ public List removeRelationshipsInvolving( final EntityInstance parent, final EntityInstance child, final String relationshipName) { - return parent.getRelationships().removeRelationshipsInvolving(child, relationshipName); + return EntityInstanceRepositoryAccess.removeRelationshipsInvolving( + parent, child, relationshipName); } @Override public List removeAllRelationships(final EntityInstance instance) { - return instance.getRelationships().removeAllRelationships(); + return EntityInstanceRepositoryAccess.removeAllRelationships(instance); } @Override public Collection getConnectedItems( final EntityInstance instance, final String relationshipName) { - return instance.getRelationships().getConnectedItems(relationshipName); + return EntityInstanceRepositoryAccess.connectedItems(instance, relationshipName); } @Override @@ -250,4 +274,5 @@ private boolean matchesQueryIdentifier(final EntityInstance instance, final Stri String primaryKeyValue = instance.getPrimaryKeyValue(); return primaryKeyValue != null && primaryKeyValue.contentEquals(identifier); } + } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java index 5e70ff63..e56d1a54 100644 --- a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/sqlite/SqliteThingRepository.java @@ -11,12 +11,15 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceRepositoryAccess; import uk.co.compendiumdev.thingifier.core.query.EntityInstanceListSorter; import uk.co.compendiumdev.thingifier.core.query.EntityListSortParamParser; import uk.co.compendiumdev.thingifier.core.query.FilterBy; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.SortByFieldName; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.validation.EntityInstanceWriteValidator; import uk.co.compendiumdev.thingifier.core.reporting.RepositoryJsonExporter; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; @@ -50,6 +53,7 @@ public class SqliteThingRepository implements ThingRepository { private final String jdbcUrl; private final Map materializedInstances; private final Map> countersByEntity; + private final EntityInstanceWriteValidator writeValidator; private ERSchema schema; private Connection connection; private boolean closed; @@ -59,6 +63,7 @@ public SqliteThingRepository(final String databaseKey, final String jdbcUrl) { this.jdbcUrl = jdbcUrl; this.materializedInstances = new HashMap<>(); this.countersByEntity = new HashMap<>(); + this.writeValidator = new EntityInstanceWriteValidator(this); } public static SqliteThingRepository inMemory(final String databaseKey) { @@ -226,11 +231,14 @@ public EntityInstance findInstanceByQueryIdentifier( } @Override - public EntityInstance addInstance(final EntityInstance instance) { + public EntityInstance createInstance(final EntityInstanceDraft draft) { ensureSchemaReady(); + EntityInstance instance = EntityInstance.fromDraft(draft); runInTransaction(() -> { prepareInstanceForInsert(instance); + writeValidator.assertValidForCreate(instance); persistInstance(instance); + EntityInstanceRepositoryAccess.lock(instance); materializedInstances.put(instance.getInternalId(), instance); persistCountersFor(instance.getEntity()); }); @@ -238,14 +246,35 @@ public EntityInstance addInstance(final EntityInstance instance) { } @Override - public EntityInstance updateInstance(final EntityInstance instance) { + public EntityInstance patchInstance( + final EntityInstance instance, + final EntityInstanceDraft draft) { ensureSchemaReady(); + EntityInstance updated = EntityInstanceRepositoryAccess.patch(instance, draft); + writeValidator.assertValidForAmendment(updated); runInTransaction(() -> { - persistInstance(instance); - materializedInstances.put(instance.getInternalId(), instance); - persistCountersFor(instance.getEntity()); + persistInstance(updated); + EntityInstanceRepositoryAccess.lock(updated); + materializedInstances.put(updated.getInternalId(), updated); + persistCountersFor(updated.getEntity()); }); - return instance; + return updated; + } + + @Override + public EntityInstance replaceInstance( + final EntityInstance instance, + final EntityInstanceDraft draft) { + ensureSchemaReady(); + EntityInstance updated = EntityInstanceRepositoryAccess.replace(instance, draft); + writeValidator.assertValidForAmendment(updated); + runInTransaction(() -> { + persistInstance(updated); + EntityInstanceRepositoryAccess.lock(updated); + materializedInstances.put(updated.getInternalId(), updated); + persistCountersFor(updated.getEntity()); + }); + return updated; } @Override @@ -290,7 +319,7 @@ public void clearInstanceDataFor(final String entityName) { public void connectRelationship( final EntityInstance from, final String relationshipName, final EntityInstance to) { ensureSchemaReady(); - from.getRelationships().connect(relationshipName, to); + EntityInstanceRepositoryAccess.connectRelationship(from, relationshipName, to); runInTransaction(() -> persistRelationship(from, relationshipName, to)); } @@ -301,8 +330,8 @@ public List removeRelationshipsInvolving( final String relationshipName) { ensureSchemaReady(); materializeRelationshipsInvolving(parent, child, relationshipName); - List removed = parent.getRelationships(). - removeRelationshipsInvolving(child, relationshipName); + List removed = EntityInstanceRepositoryAccess.removeRelationshipsInvolving( + parent, child, relationshipName); runInTransaction(() -> deleteRelationshipRowsInvolving(parent, child, relationshipName)); return removed; } @@ -311,7 +340,8 @@ public List removeRelationshipsInvolving( public List removeAllRelationships(final EntityInstance instance) { ensureSchemaReady(); materializeRelationshipsFor(instance); - List removed = instance.getRelationships().removeAllRelationships(); + List removed = + EntityInstanceRepositoryAccess.removeAllRelationships(instance); runInTransaction(() -> deleteRelationshipRowsInvolving(instance)); return removed; } @@ -447,10 +477,12 @@ private void prepareInstanceForInsert(final EntityInstance instance) { for (Field field : entity.getFieldsOfType(FieldType.AUTO_GUID, FieldType.AUTO_INCREMENT)) { if (!instance.hasInstantiatedFieldNamed(field.getName())) { if (field.getType() == FieldType.AUTO_GUID) { - instance.setValue(field.getName(), UUID.randomUUID().toString()); + EntityInstanceRepositoryAccess.setValue( + instance, field.getName(), UUID.randomUUID().toString()); } if (field.getType() == FieldType.AUTO_INCREMENT) { - instance.overrideValue( + EntityInstanceRepositoryAccess.overrideValue( + instance, field.getName(), String.valueOf(counterFor(entity, field).getNextValueAndUpdate())); } @@ -528,7 +560,7 @@ private void deleteEntityInstanceAndMandatoryRelated( materializeRelationshipsFor(instance); List alsoDelete = - instance.getRelationships().removeAllRelationships(); + EntityInstanceRepositoryAccess.removeAllRelationships(instance); deletePersistedInstance(instance.getInternalId()); materializedInstances.remove(instance.getInternalId()); @@ -552,8 +584,8 @@ private void materializeRelationshipsInvolving( } EntityInstance from = parent.getEntity() == vector.getFrom() ? parent : child; EntityInstance to = from == parent ? child : parent; - if (!from.getRelationships().getConnectedItems(vector.getName()).contains(to)) { - from.getRelationships().connect(vector.getName(), to); + if (!EntityInstanceRepositoryAccess.connectedItems(from, vector.getName()).contains(to)) { + EntityInstanceRepositoryAccess.connectRelationship(from, vector.getName(), to); } } } @@ -575,8 +607,10 @@ private void materializeRelationshipsFor(final EntityInstance instance) { vector.getTo(), resultSet.getString("to_internal_id")); if (from != null && to != null && - !from.getRelationships().getConnectedItems(vector.getName()).contains(to)) { - from.getRelationships().connect(vector.getName(), to); + !EntityInstanceRepositoryAccess.connectedItems( + from, vector.getName()).contains(to)) { + EntityInstanceRepositoryAccess.connectRelationship( + from, vector.getName(), to); } } } @@ -1001,9 +1035,10 @@ private EntityInstance instanceFromRow( return existing; } - EntityInstance instance = new EntityInstance( + EntityInstance instance = EntityInstanceRepositoryAccess.empty( entity, UUID.fromString(internalId)); hydrateInstanceFromRow(instance, resultSet); + EntityInstanceRepositoryAccess.lock(instance); materializedInstances.put(internalId, instance); return instance; } @@ -1015,7 +1050,7 @@ private void hydrateInstanceFromRow( for (String fieldName : entity.getFieldNames()) { String value = resultSet.getString(fieldName); if (value != null) { - instance.overrideValue(fieldName, value); + EntityInstanceRepositoryAccess.overrideValue(instance, fieldName, value); } } } diff --git a/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/validation/EntityInstanceWriteValidator.java b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/validation/EntityInstanceWriteValidator.java new file mode 100644 index 00000000..b234174a --- /dev/null +++ b/ercoremodel/src/main/java/uk/co/compendiumdev/thingifier/core/repository/validation/EntityInstanceWriteValidator.java @@ -0,0 +1,51 @@ +package uk.co.compendiumdev.thingifier.core.repository.validation; + +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceRepositoryAccess; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; + +import java.util.List; + +public final class EntityInstanceWriteValidator { + + private final ThingRepository repository; + + public EntityInstanceWriteValidator(final ThingRepository repository) { + this.repository = repository; + } + + public void assertValidForCreate(final EntityInstance instance) { + throwIfInvalid(validateForCreate(instance)); + } + + public void assertValidForAmendment(final EntityInstance instance) { + throwIfInvalid(validateForAmendment(instance)); + } + + public ValidationReport validateForCreate(final EntityInstance instance) { + return validateForWrite(instance, false); + } + + public ValidationReport validateForAmendment(final EntityInstance instance) { + return validateForWrite(instance, true); + } + + private ValidationReport validateForWrite( + final EntityInstance instance, + final boolean isAmendment) { + List protectedFields = instance.getEntity(). + getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); + ValidationReport validation = + EntityInstanceRepositoryAccess.validateFieldValues(instance, protectedFields, false); + validation.combine(repository.checkFieldsForUniqueNess(instance, isAmendment)); + return validation; + } + + private void throwIfInvalid(final ValidationReport validation) { + if (!validation.isValid()) { + throw new IllegalArgumentException(validation.getCombinedErrorMessages()); + } + } +} diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java index 2c8c087f..0c3afc66 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/EntityRelModelTest.java @@ -10,6 +10,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.Optionality; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; @@ -35,7 +36,8 @@ public void canCreateAnEntityRelModel() { public void nothingHappensWhenTryToDeleteThingThatDoesNotExist() { EntityRelModel erm = new EntityRelModel(); - EntityInstance missing = new EntityInstance(new EntityDefinition("no", "nos")); + EntityInstance missing = EntityInstance.fromDraft(EntityInstanceDraft.forEntity( + new EntityDefinition("no", "nos"))); Assertions.assertDoesNotThrow(() -> erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).deleteEntityInstance(missing)); @@ -64,11 +66,10 @@ public void repositoryCanAddExplicitAutoIncrementValueWhenFieldAddedAfterCollect defn.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance explicitId = new EntityInstance(defn).overrideValue("id", "12"); - repository.addInstance(explicitId); + EntityInstance explicitId = repository.createInstance( + EntityInstanceDraft.forEntity(defn).withProtectedField("id", "12")); - EntityInstance nextId = new EntityInstance(defn); - repository.addInstance(nextId); + EntityInstance nextId = repository.createInstance(EntityInstanceDraft.forEntity(defn)); Assertions.assertEquals("12", explicitId.getPrimaryKeyValue()); Assertions.assertEquals("13", nextId.getPrimaryKeyValue()); @@ -81,7 +82,7 @@ public void canFindAThingInAModel() { defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance instance = repository.addInstance(new EntityInstance(defn)); + EntityInstance instance = repository.createInstance(EntityInstanceDraft.forEntity(defn)); String thingGuid = instance.getPrimaryKeyValue(); @@ -95,7 +96,7 @@ public void canDeleteAThingInAModel() { defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance instance = repository.addInstance(new EntityInstance(defn)); + EntityInstance instance = repository.createInstance(EntityInstanceDraft.forEntity(defn)); String thingGuid = instance.getPrimaryKeyValue(); repository.deleteEntityInstance(instance); @@ -111,11 +112,11 @@ public void canClearAllDataInAModel() { EntityDefinition thing2 = erm.createEntityDefinition("thing2", "thing2"); ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - repository.addInstance(new EntityInstance(thing)); - repository.addInstance(new EntityInstance(thing)); - repository.addInstance(new EntityInstance(thing2)); - repository.addInstance(new EntityInstance(thing2)); - repository.addInstance(new EntityInstance(thing2)); + repository.createInstance(EntityInstanceDraft.forEntity(thing)); + repository.createInstance(EntityInstanceDraft.forEntity(thing)); + repository.createInstance(EntityInstanceDraft.forEntity(thing2)); + repository.createInstance(EntityInstanceDraft.forEntity(thing2)); + repository.createInstance(EntityInstanceDraft.forEntity(thing2)); Assertions.assertEquals(2, repository.countInstances(thing)); Assertions.assertEquals(3, repository.countInstances(thing2)); @@ -174,10 +175,10 @@ public void canDeleteAThingWithRelationships() { setOptionality(Optionality.MANDATORY_RELATIONSHIP); ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance mainThing = repository.addInstance(new EntityInstance(thing)); - EntityInstance first = repository.addInstance(new EntityInstance(dependant)); - EntityInstance second = repository.addInstance(new EntityInstance(dependant)); - EntityInstance third = repository.addInstance(new EntityInstance(dependant)); + EntityInstance mainThing = repository.createInstance(EntityInstanceDraft.forEntity(thing)); + EntityInstance first = repository.createInstance(EntityInstanceDraft.forEntity(dependant)); + EntityInstance second = repository.createInstance(EntityInstanceDraft.forEntity(dependant)); + EntityInstance third = repository.createInstance(EntityInstanceDraft.forEntity(dependant)); repository.connectRelationship(mainThing, "things", first); repository.connectRelationship(mainThing, "things", second); @@ -207,7 +208,8 @@ public void canSetAndUseARepositoryDataGenerator() { EntityDefinition thing = erm.createEntityDefinition("thing", "things"); RepositoryDataPopulator dataPopulator = (ERSchema schema, ThingRepository repository) -> - repository.addInstance(new EntityInstance(schema.getEntityDefinitionNamed("thing"))); + repository.createInstance( + EntityInstanceDraft.forEntity(schema.getEntityDefinitionNamed("thing"))); erm.setDataGenerator(dataPopulator); @@ -224,9 +226,9 @@ public void populateDatabaseUsesRepositoryDataPopulator() { thing.addField(Field.is("name", FieldType.STRING)); RepositoryDataPopulator dataPopulator = (ERSchema schema, ThingRepository repository) -> - repository.addInstance(new EntityInstance( - schema.getEntityDefinitionNamed("thing")). - setValue("name", "repository")); + repository.createInstance( + EntityInstanceDraft.forEntity(schema.getEntityDefinitionNamed("thing")). + withField("name", "repository")); erm.setDataGenerator(dataPopulator); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java index f1fcdd33..55ac7423 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/MultipleEntityInstancesDatabasesTest.java @@ -6,6 +6,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class MultipleEntityInstancesDatabasesTest { @@ -16,8 +17,7 @@ public void byDefaultASingleDatabaseModelIsCreatedAndUsed() { EntityDefinition thingDefn = defineThing(erm); ThingRepository repository = erm.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance thing = repository.addInstance( - new EntityInstance(thingDefn).setValue("Title", "Thing 1")); + EntityInstance thing = create(repository, thingDefn); EntityInstance foundThing = repository.findEntityInstanceByGUID(thing.getPrimaryKeyValue()); @@ -33,8 +33,7 @@ public void weCanAddANewNamedDatabaseToTheErm() { erm.createInstanceDatabase("other_things"); ThingRepository otherRepository = erm.getRepository("other_things"); - EntityInstance thing = otherRepository.addInstance( - new EntityInstance(thingDefn).setValue("Title", "Thing 1")); + EntityInstance thing = create(otherRepository, thingDefn); EntityInstance foundThing = otherRepository.findEntityInstanceByGUID(thing.getPrimaryKeyValue()); @@ -66,8 +65,7 @@ public void canDeleteNamedDatabaseFromTheErm() { erm.createInstanceDatabase("other_things"); ThingRepository otherRepository = erm.getRepository("other_things"); - EntityInstance thing = otherRepository.addInstance( - new EntityInstance(thingDefn).setValue("Title", "Thing 1")); + EntityInstance thing = create(otherRepository, thingDefn); Assertions.assertNotNull( otherRepository.findEntityInstanceByGUID(thing.getPrimaryKeyValue())); @@ -96,4 +94,9 @@ private EntityDefinition defineThing(final EntityRelModel erm) { thingDefn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); return thingDefn; } + + private EntityInstance create(final ThingRepository repository, final EntityDefinition thingDefn) { + return repository.createInstance( + EntityInstanceDraft.forEntity(thingDefn).withField("Title", "Thing 1")); + } } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/ThingTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/ThingTest.java index 9504f4aa..0acc15b2 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/ThingTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/ThingTest.java @@ -7,6 +7,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceCollection; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.Collection; @@ -24,13 +25,15 @@ public void thingUsageExample(){ person.definition(). addFields(Field.is("name", STRING), Field.is("age", INTEGER)); - EntityInstance bob = person.addInstance(new EntityInstance(person.definition())). - setValue("name","Bob"); + EntityInstance bob = person.addInstance( + EntityInstance.fromDraft(EntityInstanceDraft.forEntity(person.definition()). + withField("name", "Bob"). + withField("age", "56"))); - bob.setValue("age", "56"); - - EntityInstance eris = person.addInstance(new EntityInstance(person.definition())). - setValue("name","Eris").setValue("age", "1000"); + EntityInstance eris = person.addInstance( + EntityInstance.fromDraft(EntityInstanceDraft.forEntity(person.definition()). + withField("name", "Eris"). + withField("age", "1000"))); Assertions.assertEquals(2, person.countInstances()); Assertions.assertEquals("Bob", bob.getFieldValue("name").asString()); @@ -52,14 +55,16 @@ public void moreThingUsageExamples(){ Assertions.assertTrue(url.definition().hasFieldNameDefined("visited")); - url.addInstance(new EntityInstance(url.definition())). - setValue("name","EvilTester.com"). - setValue("url", "http://eviltester.com"); + url.addInstance( + EntityInstance.fromDraft(EntityInstanceDraft.forEntity(url.definition()). + withField("name", "EvilTester.com"). + withField("url", "http://eviltester.com"))); - url.addInstance(new EntityInstance(url.definition())). - setValue("name","JavaForTesters.com"). - setValue("url", "http://javaForTesters.com"); + url.addInstance( + EntityInstance.fromDraft(EntityInstanceDraft.forEntity(url.definition()). + withField("name", "JavaForTesters.com"). + withField("url", "http://javaForTesters.com"))); Collection instances = url.getInstances(); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/definitions/CardinalityTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/definitions/CardinalityTest.java index a2a41087..a3b717c0 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/definitions/CardinalityTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/definitions/CardinalityTest.java @@ -2,9 +2,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipDefinition; -import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; +import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; class CardinalityTest { @@ -17,38 +18,28 @@ void canGetLeftAndRightValues(){ @Test void cannotAddRelationshipExceedCardinality(){ - final DefinedRelationships rels = new DefinedRelationships(); - Assertions.assertFalse( - rels.hasRelationship("bob")); + final EntityRelModel model = new EntityRelModel(); - final EntityDefinition thing1 = new EntityDefinition("thing1", "thing1"); - final EntityDefinition thing2 = new EntityDefinition("thing2", "thing2"); + final EntityDefinition thing1 = model.createEntityDefinition("thing1", "thing1"); + final EntityDefinition thing2 = model.createEntityDefinition("thing2", "thing2"); - final RelationshipVectorDefinition relationshipVector = new RelationshipVectorDefinition( - thing1, - "bob", - thing2, - new Cardinality(0, 2)); + model.createRelationshipDefinition(thing1, thing2, "bob", new Cardinality(0, 2)); + ThingRepository repository = model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - final RelationshipDefinition parentRelationship = - RelationshipDefinition.create(relationshipVector); + final EntityInstance instance1 = repository.createInstance(EntityInstanceDraft.forEntity(thing1)); + final EntityInstance instance2 = repository.createInstance(EntityInstanceDraft.forEntity(thing2)); + final EntityInstance instance3 = repository.createInstance(EntityInstanceDraft.forEntity(thing2)); + final EntityInstance instance4 = repository.createInstance(EntityInstanceDraft.forEntity(thing2)); - rels.addRelationship(relationshipVector); - - final EntityInstance instance1 = new EntityInstance(thing1); - final EntityInstance instance2 = new EntityInstance(thing2); - final EntityInstance instance3 = new EntityInstance(thing2); - final EntityInstance instance4 = new EntityInstance(thing2); - - instance1.getRelationships().connect("bob", instance2); - instance1.getRelationships().connect("bob", instance3); + repository.connectRelationship(instance1, "bob", instance2); + repository.connectRelationship(instance1, "bob", instance3); Assertions.assertEquals(true,instance1.validate().isValid()); // this should fail boolean failed = false; try { - instance1.getRelationships().connect("bob", instance4); + repository.connectRelationship(instance1, "bob", instance4); }catch(RuntimeException e){ System.out.println(e.getMessage()); failed = true; diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraftTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraftTest.java new file mode 100644 index 00000000..dc5f8ce3 --- /dev/null +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/EntityInstanceDraftTest.java @@ -0,0 +1,144 @@ +package uk.co.compendiumdev.thingifier.core.domain.instances; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; + +import java.util.UUID; + +class EntityInstanceDraftTest { + + @Test + void normalFieldsMustExistOnTheEntityDefinition() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withField("missing", "value")); + + Assertions.assertTrue(error.getMessage().contains("Could not find field: missing")); + } + + @Test + void protectedFieldsMustExistOnTheEntityDefinition() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withProtectedField("missing", "1")); + + Assertions.assertTrue(error.getMessage().contains("Could not find field: missing")); + } + + @Test + void normalFieldsValidateSuppliedValuesAgainstFieldType() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withField("estimate", "lots")); + + Assertions.assertTrue( + error.getMessage().contains("estimate : lots does not match type INTEGER")); + } + + @Test + void nestedObjectFieldPathsCanBeSetWhenTheyExist() { + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entity()). + withField("person.age", "42"). + withField("person.name", "Connie")); + + Assertions.assertEquals( + "42", + instance.getFieldValue("person").asObject(). + getFieldValue("age").asString()); + Assertions.assertEquals( + "Connie", + instance.getFieldValue("person").asObject(). + getFieldValue("name").asString()); + } + + @Test + void nestedObjectFieldPathsMustExistOnTheObjectDefinition() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withField("person.missing", "value")); + + Assertions.assertTrue(error.getMessage().contains("Could not find field: missing")); + } + + @Test + void nestedObjectFieldPathsMustTraverseObjectFields() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withField("name.first", "value")); + + Assertions.assertTrue( + error.getMessage().contains("Cannot reference fields on non object fields: name")); + } + + @Test + void normalFieldsCannotSetAutoIncrementFields() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withField("id", "12")); + + Assertions.assertTrue( + error.getMessage().contains( + "id : field is protected and can only be set with withProtectedField")); + } + + @Test + void normalFieldsCannotSetAutoGuidFields() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withField("guid", UUID.randomUUID().toString())); + + Assertions.assertTrue( + error.getMessage().contains( + "guid : field is protected and can only be set with withProtectedField")); + } + + @Test + void protectedFieldsMustActuallyBeProtectedFields() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withProtectedField("name", "Connie")); + + Assertions.assertTrue( + error.getMessage().contains( + "name : field is not protected and should be set with withField")); + } + + @Test + void protectedAutoIncrementFieldsValidateSuppliedValuesAgainstIntegerType() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withProtectedField("id", "not-an-int")); + + Assertions.assertTrue( + error.getMessage().contains( + "id : not-an-int does not match type AUTO_INCREMENT")); + } + + @Test + void protectedAutoGuidFieldsValidateSuppliedValuesAgainstUuidType() { + IllegalArgumentException error = assertDraftFailure( + () -> EntityInstanceDraft.forEntity(entity()).withProtectedField("guid", "not-a-guid")); + + Assertions.assertTrue( + error.getMessage().contains( + "guid : not-a-guid does not match type AUTO_GUID")); + } + + @Test + void protectedFieldsCanBeNullForDocumentationSnapshots() { + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entity()). + withProtectedField("id", null)); + + Assertions.assertNull(instance.getFieldValue("id").asString()); + } + + private IllegalArgumentException assertDraftFailure(final Runnable operation) { + return Assertions.assertThrows(IllegalArgumentException.class, operation::run); + } + + private EntityDefinition entity() { + EntityDefinition entity = new EntityDefinition("thing", "things"); + entity.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); + entity.addField(Field.is("guid", FieldType.AUTO_GUID)); + entity.addField(Field.is("name", FieldType.STRING)); + entity.addField(Field.is("estimate", FieldType.INTEGER)); + entity.addField(Field.is("person", FieldType.OBJECT). + withField(Field.is("name", FieldType.STRING)). + withField(Field.is("age", FieldType.INTEGER))); + return entity; + } +} diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java index 1854aad4..b6e48a61 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/InstanceFieldsTest.java @@ -10,9 +10,7 @@ import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; class InstanceFieldsTest { @@ -98,21 +96,6 @@ void byDefaultAutoIncrementIdFieldsAreSetToOne() { Assertions.assertEquals(1, instance.getFieldValue("id").asInteger()); } - @Test - void weCanInstantiateTheIdsAfterCreation() { - - EntityDefinition entity = new EntityDefinition("thing", "things"); - entity.addField(Field.is("id", FieldType.AUTO_INCREMENT)); - EntityInstance instance = new EntityInstance(entity); - Map counters = new HashMap<>(); - counters.put("id", new AutoIncrement("id", 1)); - - instance.addAutoIncrementIdsToInstance(counters); - - Assertions.assertNotNull(instance.getFieldValue("id")); - Assertions.assertEquals("1", instance.getFieldValue("id").asString()); - } - @Test void errorForGetUnknownField() { diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/ThingInstanceCreationTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/ThingInstanceCreationTest.java index 51e6ac04..827f0e30 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/ThingInstanceCreationTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/ThingInstanceCreationTest.java @@ -40,24 +40,6 @@ public void canCreateNewEntityInstance() { System.out.println(session.toString()); } - - @Test - public void canCreateUniqueGUIDs(){ - EntityInstance session = new EntityInstance(entityTestSession); - session.addAutoGUIDstoInstance(); - - Assertions.assertNotNull(session.getPrimaryKeyValue()); - - EntityInstance session2 = new EntityInstance(entityTestSession); - session2.addAutoGUIDstoInstance(); - - Assertions.assertNotNull(session2.getPrimaryKeyValue()); - Assertions.assertTrue(session2.getPrimaryKeyValue().length()>10); - - Assertions.assertNotEquals(session.getPrimaryKeyValue(), session2.getPrimaryKeyValue()); - } - - @Test public void canCreateAThingWithAGUID(){ diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoGuidTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoGuidTest.java index fbff9c68..7e481e85 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoGuidTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoGuidTest.java @@ -3,29 +3,36 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepository; public class AutoGuidTest { EntityDefinition entityTestSession; + ThingRepository repository; @BeforeEach public void createEntity(){ - entityTestSession = new EntityDefinition("Test Session", "Test Sessions"); + ERSchema schema = new ERSchema(); + entityTestSession = schema.defineEntity("Test Session", "Test Sessions", -1); entityTestSession.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); entityTestSession.addField(Field.is("Title", FieldType.STRING)); + repository = new InMemoryThingRepository("test"); + repository.initializeFrom(schema); } @Test public void anInstanceHasAGuid() { EntityInstance session; - session = new EntityInstance(entityTestSession); - session.addAutoGUIDstoInstance(); + session = repository.createInstance(EntityInstanceDraft.forEntity(entityTestSession)); Assertions.assertNotNull(session.getPrimaryKeyValue()); Assertions.assertTrue( @@ -40,8 +47,7 @@ public void anInstanceHasAGuid() { public void anInstanceCanAccessGuidAsFieldOrMethod() { EntityInstance session; - session = new EntityInstance(entityTestSession); - session.addAutoGUIDstoInstance(); + session = repository.createInstance(EntityInstanceDraft.forEntity(entityTestSession)); Assertions.assertEquals(session.getPrimaryKeyValue(), session.getFieldValue("guid").asString()); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoIncrementIdFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoIncrementIdFieldTest.java index 9bf8c3b3..53a723c2 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoIncrementIdFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/AutoIncrementIdFieldTest.java @@ -2,28 +2,26 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; - -import java.util.HashMap; -import java.util.Map; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; +import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepository; public class AutoIncrementIdFieldTest { @Test public void byDefaultAnIDFieldIsOneWhenInstantiated(){ - EntityDefinition entity = new EntityDefinition("thing", "things"); + ERSchema schema = new ERSchema(); + EntityDefinition entity = schema.defineEntity("thing", "things", -1); entity.addFields(Field.is("id", FieldType.AUTO_INCREMENT)); - - Map autos = new HashMap<>(); - autos.put("id", new AutoIncrement("id", entity.getField("id").getDefaultValue().asInteger())); - - EntityInstance instance = new EntityInstance(entity); - instance.addAutoIncrementIdsToInstance(autos); + ThingRepository repository = new InMemoryThingRepository("test"); + repository.initializeFrom(schema); + EntityInstance instance = repository.createInstance(EntityInstanceDraft.forEntity(entity)); Assertions.assertEquals("1", instance.getFieldValue("id").asString()); } @@ -33,35 +31,32 @@ public void byDefaultAnIDFieldIsOneWhenInstantiated(){ @Test public void idsShouldAutoIncrementWhenInstancesCreated(){ - EntityDefinition entity = new EntityDefinition("thing", "things"); + ERSchema schema = new ERSchema(); + EntityDefinition entity = schema.defineEntity("thing", "things", -1); entity.addFields(Field.is("id", FieldType.AUTO_INCREMENT)); + ThingRepository repository = new InMemoryThingRepository("test"); + repository.initializeFrom(schema); - Map autos = new HashMap<>(); - autos.put("id", new AutoIncrement("id", 1)); - - EntityInstance instance = new EntityInstance(entity); - instance.addAutoIncrementIdsToInstance(autos); + EntityInstance instance = repository.createInstance(EntityInstanceDraft.forEntity(entity)); Assertions.assertEquals("1", instance.getFieldValue("id").asString()); - EntityInstance instance2 = new EntityInstance(entity); - instance2.addAutoIncrementIdsToInstance(autos); + EntityInstance instance2 = repository.createInstance(EntityInstanceDraft.forEntity(entity)); Assertions.assertEquals("2", instance2.getFieldValue("id").asString()); } @Test public void notAllowedToAmendIdOfInstance(){ - EntityDefinition entity = new EntityDefinition("thing", "things"); + ERSchema schema = new ERSchema(); + EntityDefinition entity = schema.defineEntity("thing", "things", -1); entity.addFields(Field.is("id", FieldType.AUTO_INCREMENT)); + ThingRepository repository = new InMemoryThingRepository("test"); + repository.initializeFrom(schema); - EntityInstance instance = new EntityInstance(entity); - Map autos = new HashMap<>(); - autos.put("id", new AutoIncrement("id", 1)); - - instance.addAutoIncrementIdsToInstance(autos); + EntityInstance instance = repository.createInstance(EntityInstanceDraft.forEntity(entity)); Assertions.assertEquals("1", instance.getFieldValue("id").asString()); Assertions.assertThrows(IllegalArgumentException.class, ()-> { - instance.setValue("id", "2"); + repository.patchInstance(instance, EntityInstanceDraft.forEntity(entity).withField("id", "2")); }); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/BooleanFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/BooleanFieldTest.java index 9530e6fe..01244dd0 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/BooleanFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/BooleanFieldTest.java @@ -7,6 +7,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class BooleanFieldTest { @@ -26,31 +27,32 @@ public void createEntity(){ @Test public void booleanFieldsCanOnlyBeSetAsTrueOrFalse() { - EntityInstance session = new EntityInstance(entityTestSession); - - session.setValue("review", "false"); + EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession). + withField("review", "false")); Assertions.assertEquals("false", session.getFieldValue("review").asString()); - session.setValue("review", "faLSE"); + session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession). + withField("review", "faLSE")); Assertions.assertEquals("false", session.getFieldValue("review").asString()); - session.setValue("review", "true"); + session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession). + withField("review", "true")); Assertions.assertEquals("true", session.getFieldValue("review").asString()); - session.setValue("review", "TRUE"); + session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession). + withField("review", "TRUE")); Assertions.assertEquals("true", session.getFieldValue("review").asString()); } @Test public void booleanFieldsRaiseExceptionForInvalidValue(){ - EntityInstance session = new EntityInstance(entityTestSession); - Assertions.assertThrows(IllegalArgumentException.class, ()->{ - session.setValue("review", "BOB"); + EntityInstanceDraft.forEntity(entityTestSession).withField("review", "BOB"); }); // unchanged from default + EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession)); Assertions.assertEquals("true", session.getFieldValue("review").asString()); } @@ -58,7 +60,7 @@ public void booleanFieldsRaiseExceptionForInvalidValue(){ @Test public void booleanFieldsByDefaultAreFalse(){ - EntityInstance session = new EntityInstance(entityTestSession); + EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession)); // false by default Assertions.assertEquals("false", session.getFieldValue("falsey").asString()); @@ -67,7 +69,7 @@ public void booleanFieldsByDefaultAreFalse(){ @Test public void booleanFieldsDefaultCanBeConfigured(){ - EntityInstance session = new EntityInstance(entityTestSession); + EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession)); // false by default Assertions.assertEquals("true", session.getFieldValue("review").asString()); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/EnumFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/EnumFieldTest.java index e10d2511..f8a4f229 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/EnumFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/EnumFieldTest.java @@ -6,6 +6,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class EnumFieldTest { @@ -16,7 +17,7 @@ public void byDefaultAnEnumFieldIsEmpty(){ enumFieldEntity.addFields(Field.is("enum", FieldType.ENUM)); // TODO: have a validation process for the definition and make it a syntax error for Enum fields to have no values - EntityInstance instance = new EntityInstance(enumFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(enumFieldEntity)); Assertions.assertEquals("", instance.getFieldValue("enum").asString()); } @@ -30,14 +31,14 @@ public void canSetAllowableValuesOfEnumFields(){ withExample("dobbs") ); - EntityInstance instance = new EntityInstance(stringFieldEntity); - // use example - instance.setValue("enum", "dukes"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("enum", "dukes")); Assertions.assertEquals("dukes", instance.getFieldValue("enum").asString()); // use default - instance.setValue("enum", "bob"); + instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("enum", "bob")); Assertions.assertEquals("bob", instance.getFieldValue("enum").asString()); } @@ -51,10 +52,8 @@ public void canNotSetUndefinedValueOfEnumFields(){ withExample("dobbs") ); - EntityInstance instance = new EntityInstance(stringFieldEntity); - Assertions.assertThrows(IllegalArgumentException.class, ()-> { - instance.setValue("enum", "connie"); + EntityInstanceDraft.forEntity(stringFieldEntity).withField("enum", "connie"); }); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/FloatFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/FloatFieldTest.java index cfd44562..a9459a98 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/FloatFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/FloatFieldTest.java @@ -6,6 +6,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class FloatFieldTest { @@ -16,7 +17,7 @@ public void byDefaultAFloatFieldIsZero(){ enumFieldEntity.addFields(Field.is("float", FieldType.FLOAT)); // TODO: allow nullable optional floats - EntityInstance instance = new EntityInstance(enumFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(enumFieldEntity)); Assertions.assertEquals("0.0", instance.getFieldValue("float").asString()); } @@ -26,11 +27,9 @@ public void floatShouldValidate(){ EntityDefinition enumFieldEntity = new EntityDefinition("thing", "things"); enumFieldEntity.addFields(Field.is("float", FieldType.FLOAT)); - EntityInstance instance = new EntityInstance(enumFieldEntity); - final IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, () -> { - instance.setValue("float", "bob"); + EntityInstanceDraft.forEntity(enumFieldEntity).withField("float", "bob"); }); Assertions.assertTrue(e.getMessage().contains("float : bob does not match type FLOAT"), @@ -43,9 +42,8 @@ public void canSetGetFloat(){ EntityDefinition enumFieldEntity = new EntityDefinition("thing", "things"); enumFieldEntity.addFields(Field.is("float", FieldType.FLOAT)); - EntityInstance instance = new EntityInstance(enumFieldEntity); - - instance.setValue("float", "4.3"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(enumFieldEntity). + withField("float", "4.3")); Assertions.assertEquals("4.3", instance.getFieldValue("float").asString()); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/IntegerFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/IntegerFieldTest.java index bc47dea0..8370029d 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/IntegerFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/IntegerFieldTest.java @@ -6,6 +6,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class IntegerFieldTest { @@ -16,7 +17,7 @@ public void byDefaultAnIntegerFieldIsZero(){ enumFieldEntity.addFields(Field.is("integer", FieldType.INTEGER)); // TODO: allow nullable optional integers - EntityInstance instance = new EntityInstance(enumFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(enumFieldEntity)); Assertions.assertEquals("0", instance.getFieldValue("integer").asString()); } @@ -29,14 +30,12 @@ public void cannotSetAValueOutwithMaxMinValueForIntegerFields(){ withMinMaxValues(50,100) ); - EntityInstance instance = new EntityInstance(stringFieldEntity); - Assertions.assertThrows(IllegalArgumentException.class, ()->{ - instance.setValue("integer", "101"); + EntityInstanceDraft.forEntity(stringFieldEntity).withField("integer", "101"); }); Assertions.assertThrows(IllegalArgumentException.class, ()->{ - instance.setValue("integer", "49"); + EntityInstanceDraft.forEntity(stringFieldEntity).withField("integer", "49"); }); } @@ -48,9 +47,8 @@ public void canSetIntegerField(){ withMinMaxValues(50,100) ); - EntityInstance instance = new EntityInstance(stringFieldEntity); - - instance.setValue("integer", "75"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("integer", "75")); Assertions.assertEquals(instance.getFieldValue("integer").asString(),"75"); } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/ObjectFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/ObjectFieldTest.java index 7af3966c..b2767825 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/ObjectFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/ObjectFieldTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -29,9 +30,9 @@ public void createThingWithNestedObjectField(){ )); - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Connie"); - instance.setValue("person.surname", "Dobbs"); + instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(defn). + withField("person.firstname", "Connie"). + withField("person.surname", "Dobbs")); } @Test @@ -51,8 +52,8 @@ public void canValidateAtObjectFieldLevel() { getObjectDefinition(). getField("surname").makeMandatory(); - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Eris"); + instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(defn). + withField("person.firstname", "Eris")); final ValidationReport validation = instance.validate(); @@ -68,15 +69,13 @@ public void canValidateAtObjectFieldLevelWithValidationRules() { getField("surname").makeMandatory(). withValidation(VRule.notEmpty()); - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Eris"); - - // bypass set validation rules and see if validation picks it up - instance.overrideValue("person.surname", ""); + final IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, + () -> EntityInstanceDraft.forEntity(defn). + withField("person.firstname", "Eris"). + withField("person.surname", "") + ); - final ValidationReport validation = instance.validate(); - Assertions.assertFalse(validation.isValid(), "surname should fail validation"); - Assertions.assertTrue(validation.getCombinedErrorMessages().contains("surname : can not be empty")); + Assertions.assertTrue(e.getMessage().contains("surname : can not be empty")); } @Test @@ -87,13 +86,10 @@ public void canValidateAtObjectFieldLevelWhenSettingValues() { getField("surname"). withValidation(VRule.notEmpty()); - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Eris"); - final IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, - () -> { - instance.setValue("person.surname", ""); - } + () -> EntityInstanceDraft.forEntity(defn). + withField("person.firstname", "Eris"). + withField("person.surname", "") ); Assertions.assertTrue(e.getMessage().contains("surname : can not be empty")); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/StringFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/StringFieldTest.java index 9e01ed5f..bab2be8f 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/StringFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/StringFieldTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; @@ -20,7 +21,7 @@ public void byDefaultAStringFieldIsEmpty(){ EntityDefinition stringFieldEntity = new EntityDefinition("Test Session", "Test Sessions"); stringFieldEntity.addFields(Field.is("defaultString", FieldType.STRING)); - EntityInstance instance = new EntityInstance(stringFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity)); Assertions.assertEquals("", instance.getFieldValue("defaultString").asString()); } @@ -31,7 +32,7 @@ public void canSetDefaultValuesOfStringFields(){ EntityDefinition stringFieldEntity = new EntityDefinition("Test Session", "Test Sessions"); stringFieldEntity.addFields(Field.is("defaultString", FieldType.STRING).withDefaultValue("bob")); - EntityInstance instance = new EntityInstance(stringFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity)); Assertions.assertEquals("bob", instance.getFieldValue("defaultString").asString()); } @@ -45,12 +46,13 @@ public void canConfigureStringsToValidateOnNotEmpty(){ withDefaultValue(""). withValidation(VRule.notEmpty())); - EntityInstance instance = new EntityInstance(stringFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity)); // defaultString is not valid because it has an empty string Assertions.assertFalse(instance.validate().isValid()); - instance.setValue("defaultString", "Eris"); + instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("defaultString", "Eris")); Assertions.assertTrue(instance.validate().isValid()); Assertions.assertEquals("Eris", instance.getFieldValue("defaultString").asString()); } @@ -64,9 +66,8 @@ public void canConfigureStringsToTruncateIfTooLargeWhenSetting(){ withDefaultValue(""). withValidation(VRule.notEmpty()).truncateStringTo(10)); - EntityInstance instance = new EntityInstance(stringFieldEntity); - - instance.setValue("field", "This is too long"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("field", "This is too long")); String fieldValue = instance.getFieldValue("field").asString(); Assertions.assertEquals(10, fieldValue.length()); @@ -82,13 +83,12 @@ public void canConfigureStringsToThrowErrorValidationErrorIfTooLarge(){ withDefaultValue(""). withValidation(VRule.maximumLength(10))); - EntityInstance instance = new EntityInstance(stringFieldEntity); - instance.overrideValue("field","12345678901"); - - ValidationReport report = instance.validate(); + IllegalArgumentException e = Assertions.assertThrows( + IllegalArgumentException.class, + () -> EntityInstanceDraft.forEntity(stringFieldEntity). + withField("field","12345678901")); - Assertions.assertFalse(report.isValid()); - report.getCombinedErrorMessages().contains("Maximum allowable length exceeded"); + Assertions.assertTrue(e.getMessage().contains("Maximum allowable length exceeded")); } @Test @@ -100,10 +100,10 @@ public void canConfigureStringsToValidateonSetting(){ withDefaultValue(""). withValidation(VRule.matchesRegex("^Bug:.*"))); - EntityInstance instance = new EntityInstance(stringFieldEntity); IllegalArgumentException e = Assertions.assertThrows(IllegalArgumentException.class, () ->{ - instance.setValue("field", "ISSUE: reporting a bug - this is a bug"); + EntityInstanceDraft.forEntity(stringFieldEntity). + withField("field", "ISSUE: reporting a bug - this is a bug"); }); System.out.println(e.getMessage()); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/UniqueFieldTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/UniqueFieldTest.java index 107e4d5c..a46c31cf 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/UniqueFieldTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/domain/instances/fields/UniqueFieldTest.java @@ -6,6 +6,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class UniqueFieldTest { @@ -15,7 +16,7 @@ public void byDefaultAFieldIsNotUnique(){ EntityDefinition stringFieldEntity = new EntityDefinition("Entity", "Entities"); stringFieldEntity.addFields(Field.is("field", FieldType.STRING)); - EntityInstance instance = new EntityInstance(stringFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity)); Assertions.assertFalse(instance.getEntity().getField("field").mustBeUnique()); } @@ -26,7 +27,7 @@ public void aFieldCanBeSetToBeUnique(){ EntityDefinition stringFieldEntity = new EntityDefinition("Entity", "Entities"); stringFieldEntity.addFields(Field.is("field", FieldType.STRING).setMustBeUnique(true)); - EntityInstance instance = new EntityInstance(stringFieldEntity); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity)); Assertions.assertTrue(instance.getEntity().getField("field").mustBeUnique()); } @@ -42,8 +43,8 @@ public void aUniqueFieldCanBeUniqueAfterATransform(){ (s) -> s.replace("-", "") )); - EntityInstance instance = new EntityInstance(stringFieldEntity); - instance.setValue("field", "1-2-3"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("field", "1-2-3")); Assertions.assertTrue(instance.getEntity().getField("field").mustBeUnique()); Assertions.assertEquals("1-2-3",instance.getFieldValue("field").asString()); @@ -58,8 +59,8 @@ public void aUniqueFieldDoesNotNeedAUniqueTransformFunction(){ Field.is("field", FieldType.STRING). setMustBeUnique(true)); - EntityInstance instance = new EntityInstance(stringFieldEntity); - instance.setValue("field", "1-2-3"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("field", "1-2-3")); Assertions.assertTrue(instance.getEntity().getField("field").mustBeUnique()); Assertions.assertEquals("1-2-3",instance.getFieldValue("field").asString()); @@ -77,8 +78,8 @@ public void reportErrorsInTransformationFunctionResult(){ (s) -> {throw new RuntimeException("bob");} )); - EntityInstance instance = new EntityInstance(stringFieldEntity); - instance.setValue("field", "1-2-3"); + EntityInstance instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(stringFieldEntity). + withField("field", "1-2-3")); Assertions.assertTrue(instance.getEntity().getField("field").mustBeUnique()); Assertions.assertEquals("1-2-3",instance.getFieldValue("field").asString()); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java index 70c43d00..7df020ff 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersBooleanTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; @@ -32,10 +33,10 @@ public void setupCollectionTestData(){ .addFields( Field.is("truefalse", FieldType.BOOLEAN) ); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "true"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "true"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "true"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "truefalse", "false"); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("truefalse", "true")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("truefalse", "true")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("truefalse", "true")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("truefalse", "false")); } @@ -45,7 +46,7 @@ public void canFilterBooleanMatchesTrue() { QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "true"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -59,7 +60,7 @@ public void canFilterBooleanMatchesFalse() { QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "false"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -72,7 +73,7 @@ public void canFilterBooleanMatchesNotFalse() { QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "!false"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -85,7 +86,7 @@ public void canFilterBooleanMatchesNotTrue(){ QueryFilterParams params = new QueryFilterParams(); params.put("truefalse", "!true"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -98,7 +99,7 @@ public void canSortBooleanMatchesAsc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "+truefalse"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -116,7 +117,7 @@ public void canSortBooleanMatchesDesc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-truefalse"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java index ec173e69..28fe611c 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersFloatTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; @@ -26,10 +27,10 @@ public void setupCollectionTestData(){ Field.is("float", FieldType.FLOAT) ); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "4.4"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "1.1"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "3.3"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "float", "2.2"); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("float", "4.4")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("float", "1.1")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("float", "3.3")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("float", "2.2")); } @@ -39,7 +40,7 @@ public void canFilterFloatEquals() { QueryFilterParams params = new QueryFilterParams(); params.put("float", "1.1"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(1, instances.size(), "expected 1 value"); @@ -53,7 +54,7 @@ public void canFilterFloatNotEquals() { params.put("float", "!1.1"); params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(3, instances.size(), "expected 3 value"); @@ -72,7 +73,7 @@ public void canFilterFloatCombinationOfConditions() { params.put("float", "!3.3"); // and not equal to 3 params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(2, instances.size(), "expected 2 value"); @@ -86,7 +87,7 @@ public void canFilterFloatGreaterThan() { params.put("float", ">1.1"); params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -102,7 +103,7 @@ public void canFilterFloatLessThan() { params.put("float", "<2.0"); params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -116,7 +117,7 @@ public void canFilterFloatLessThanNotMatching() { params.put("float", "<1.1"); params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -129,7 +130,7 @@ public void canFilterFloatGreaterThanEquals(){ params.put("float", ">=3.3"); params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -145,7 +146,7 @@ public void canFilterFloatLessThanEquals(){ params.put("float", "<=3.3"); params.put("sortby", "+float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -162,7 +163,7 @@ public void canFilterFloatLessThanEqualsSortDesc(){ params.put("float", "<=3.3"); params.put("sortby", "-float"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java index bb115c71..084396ee 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIdTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; @@ -27,10 +28,10 @@ public void setupCollectionTestData(){ ); // fakeid is a proxy for the actual id which always starts at 1 and auto increments - RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "1"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "2"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "3"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "fakeid", "4"); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("fakeid", "1")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("fakeid", "2")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("fakeid", "3")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("fakeid", "4")); } @@ -41,7 +42,7 @@ public void canFilterAndSortIdAsc() { params.put("id", ">=3"); params.put("sortBy", "+id"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -58,7 +59,7 @@ public void canFilterAndSortIdDesc() { params.put("id", "<3"); params.put("sortBy", "-id"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java index 2cc196de..d875dcc1 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersIntegerTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; @@ -30,10 +31,10 @@ public void setupCollectionTestData(){ Field.is("int", FieldType.INTEGER) ); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "3"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "1"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "4"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "int", "2"); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("int", "3")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("int", "1")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("int", "4")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("int", "2")); } @@ -43,7 +44,7 @@ public void canFilterIntegerEquals() { QueryFilterParams params = new QueryFilterParams(); params.put("int", "1"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(1, instances.size(), "expected 1 value"); @@ -57,7 +58,7 @@ public void canFilterIntegerNotEquals() { params.put("int", "!1"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(3, instances.size(), "expected 3 value"); @@ -75,7 +76,7 @@ public void canFilterIntegerCombinationOfConditions() { params.put("int", "!3"); // and not equal to 3 params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); Assertions.assertEquals(2, instances.size(), "expected 2 value"); @@ -89,7 +90,7 @@ public void canFilterIntegerGreaterThan() { params.put("int", ">1"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -105,7 +106,7 @@ public void canFilterIntegerLessThan() { params.put("int", "<2"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -119,7 +120,7 @@ public void canFilterIntegerLessThanNotMatching() { params.put("int", "<1"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things").performQuery(params); + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things").performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); List instances = queryResults.getListEntityInstances(); @@ -132,7 +133,7 @@ public void canFilterIntegerGreaterThanEquals(){ params.put("int", ">=3"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -148,7 +149,7 @@ public void canFilterIntegerLessThanEquals(){ params.put("int", "<=3"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -165,7 +166,7 @@ public void canFilterIntegerLessThanSortedDesc(){ params.put("int", "<3"); params.put("sortby", "-int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -181,7 +182,7 @@ public void canIntegerSortedDesc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortby", "-int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -198,7 +199,7 @@ public void canIntegerSortedAsc(){ QueryFilterParams params = new QueryFilterParams(); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -217,7 +218,7 @@ public void canRegexFilterInteger(){ params.put("int", "~=[1,2]"); params.put("sortby", "+int"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java index 6cce0d86..53596d11 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/QueryFiltersStringTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; @@ -25,10 +26,10 @@ public void setupCollectionTestData(){ .addFields( Field.is("string", FieldType.STRING) ); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "one"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "two"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "three"); - RepositoryUrlQueryTestSupport.add(erModel, "thing", "string", "four"); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("string", "one")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("string", "two")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("string", "three")); + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(erModel.getSchema().getEntityDefinitionNamed("thing")).withField("string", "four")); } @@ -38,7 +39,7 @@ public void canSortStringAsc() { QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "+string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -56,7 +57,7 @@ public void canSortStringDesc() { QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -75,7 +76,7 @@ public void canFilterStringEquals() { QueryFilterParams params = new QueryFilterParams(); params.put("string", "=one"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -91,7 +92,7 @@ public void canFilterAndSortStringUsingRegexAsc() { params.put("string", "~=.*o.*"); params.put("sortBy", "+string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -110,7 +111,7 @@ public void canFilterAndSortStringUsingRegexDesc() { params.put("string", "~=.*o.*"); params.put("sortBy", "-string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -130,7 +131,7 @@ public void canFilterAndSortStringUsingWildcardAsc() { params.put("string", "*=*e"); params.put("sortBy", "+string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -148,7 +149,7 @@ public void canFilterAndSortStringUsingWildcardAscForT() { params.put("string", "*=t*"); params.put("sortBy", "+string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -166,7 +167,7 @@ public void canFilterAndSortStringUsingWildcardAscForTBit() { params.put("string", "*=t?*"); params.put("sortBy", "+string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); @@ -184,7 +185,7 @@ public void canFilterAndSortStringUsingWildcardAscForTBitO() { params.put("string", "*=t?o"); params.put("sortBy", "+string"); - RepositoryUrlQuery queryResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). + RepositoryUrlQuery queryResults = new RepositoryUrlQuery(erModel.getSchema(), erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), "things"). performQuery(params); Assertions.assertTrue(queryResults.isResultACollection(), "result should be a collection"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java deleted file mode 100644 index 4e0f3681..00000000 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/RepositoryUrlQueryTestSupport.java +++ /dev/null @@ -1,28 +0,0 @@ -package uk.co.compendiumdev.thingifier.core.query; - -import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; - -final class RepositoryUrlQueryTestSupport { - - private RepositoryUrlQueryTestSupport() { - } - - static RepositoryUrlQuery query(final EntityRelModel model, final String query) { - return new RepositoryUrlQuery( - model.getSchema(), - model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), - query); - } - - static EntityInstance add( - final EntityRelModel model, - final String entityName, - final String fieldName, - final String value) { - EntityDefinition definition = model.getSchema().getEntityDefinitionNamed(entityName); - EntityInstance instance = new EntityInstance(definition).setValue(fieldName, value); - return model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).addInstance(instance); - } -} diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java index e8cf3e36..09bffd96 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/SortingViaQueryFiltersTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; @@ -35,19 +36,21 @@ public void setupThingifier(){ public void canSortIntViaAQuery(){ final EntityInstance thing1 = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). - addInstance(new EntityInstance(thing).setValue("int", "1")); + createInstance(EntityInstanceDraft.forEntity(thing).withField("int", "1")); final EntityInstance thing2 = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). - addInstance(new EntityInstance(thing).setValue("int", "2")); + createInstance(EntityInstanceDraft.forEntity(thing).withField("int", "2")); final EntityInstance thing3 = erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). - addInstance(new EntityInstance(thing).setValue("int", "3")); + createInstance(EntityInstanceDraft.forEntity(thing).withField("int", "3")); QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-int"); - RepositoryUrlQuery ascSortedResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). - performQuery(params); + RepositoryUrlQuery ascSortedResults = new RepositoryUrlQuery( + erModel.getSchema(), + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "things").performQuery(params); Assertions.assertTrue(ascSortedResults.isResultACollection(), "result should be a collection"); final List instances = ascSortedResults.getListEntityInstances(); @@ -60,8 +63,10 @@ public void canSortIntViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "+int"); - RepositoryUrlQuery descSortedResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). - performQuery(params); + RepositoryUrlQuery descSortedResults = new RepositoryUrlQuery( + erModel.getSchema(), + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "things").performQuery(params); final List descInstances = descSortedResults.getListEntityInstances(); Assertions.assertEquals(3, descInstances.size(), "expected 3 values"); @@ -73,8 +78,10 @@ public void canSortIntViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "int"); - RepositoryUrlQuery defaultSortedResults = RepositoryUrlQueryTestSupport.query(erModel, "things"). - performQuery(params); + RepositoryUrlQuery defaultSortedResults = new RepositoryUrlQuery( + erModel.getSchema(), + erModel.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "things").performQuery(params); final List defaultSortedInstances = defaultSortedResults.getListEntityInstances(); Assertions.assertEquals(3, defaultSortedInstances.size(), "expected 3 values"); @@ -92,17 +99,19 @@ public void canSortViaAQuery(){ final EntityInstance trueThing = aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). - addInstance(new EntityInstance(thing).setValue("truefalse", "true")); + createInstance(EntityInstanceDraft.forEntity(thing).withField("truefalse", "true")); final EntityInstance falseThing = aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME). - addInstance(new EntityInstance(thing).setValue("truefalse", "false")); + createInstance(EntityInstanceDraft.forEntity(thing).withField("truefalse", "false")); QueryFilterParams params = new QueryFilterParams(); params.put("sortBy", "-truefalse"); - RepositoryUrlQuery ascSortedResults = RepositoryUrlQueryTestSupport.query(aThingifier, "things"). - performQuery(params); + RepositoryUrlQuery ascSortedResults = new RepositoryUrlQuery( + aThingifier.getSchema(), + aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "things").performQuery(params); Assertions.assertTrue(ascSortedResults.isResultACollection(), "result should be a collection"); final List instances = ascSortedResults.getListEntityInstances(); @@ -115,8 +124,10 @@ public void canSortViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "+truefalse"); - RepositoryUrlQuery descSortedResults = RepositoryUrlQueryTestSupport.query(aThingifier, "things"). - performQuery(params); + RepositoryUrlQuery descSortedResults = new RepositoryUrlQuery( + aThingifier.getSchema(), + aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "things").performQuery(params); final List descInstances = descSortedResults.getListEntityInstances(); Assertions.assertEquals(2, descInstances.size(), "expected 2 values"); @@ -127,8 +138,10 @@ public void canSortViaAQuery(){ params = new QueryFilterParams(); params.put("sortBy", "truefalse"); - RepositoryUrlQuery defaultSortedResults = RepositoryUrlQueryTestSupport.query(aThingifier, "things"). - performQuery(params); + RepositoryUrlQuery defaultSortedResults = new RepositoryUrlQuery( + aThingifier.getSchema(), + aThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "things").performQuery(params); final List defaultSortedInstances = defaultSortedResults.getListEntityInstances(); Assertions.assertEquals(2, defaultSortedInstances.size(), "expected 2 values"); diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java index 115bd5e3..8ba00cbb 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/query/TodoManagerQueryEngineTest.java @@ -9,6 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.Cardinality; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.List; @@ -56,18 +57,19 @@ public void createDefinitions(){ ThingRepository repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - paperwork = repository.addInstance( - new EntityInstance(todo).setValue("title", "scan paperwork")); + paperwork = repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", "scan paperwork")); //System.out.println(new Gson().toJson(JsonThing.asJsonObject(paperwork))); - filework = repository.addInstance( - new EntityInstance(todo).setValue("title", "file paperwork")); + filework = repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", "file paperwork")); - officeCategory = repository.addInstance( - new EntityInstance(category).setValue("title", "Office")); + officeCategory = repository.createInstance( + EntityInstanceDraft.forEntity(category).withField("title", "Office")); - repository.addInstance(new EntityInstance(category).setValue("title", "Home")); + repository.createInstance( + EntityInstanceDraft.forEntity(category).withField("title", "Home")); repository.connectRelationship(paperwork, "categories", officeCategory); @@ -82,7 +84,10 @@ public void createDefinitions(){ public void canGetListOfEntityInstancesViaName(){ // to do - final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todo"); + final RepositoryUrlQuery query = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "todo"); List queryResults = query.performQuery().getListEntityInstances(); @@ -97,7 +102,10 @@ public void canGetListOfEntityInstancesViaName(){ @Test public void canGetListOfEntityInstancesViaPluralName(){ // todos - final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todos"); + final RepositoryUrlQuery query = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "todos"); List queryResults = query.performQuery().getListEntityInstances(); @@ -115,7 +123,10 @@ public void canGetSpecificEntityInstanceUsingGUID(){ // to do/_GUID_ - final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todo/" + paperwork.getPrimaryKeyValue()); + final RepositoryUrlQuery query = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "todo/" + paperwork.getPrimaryKeyValue()); queryResults = query.performQuery().getListEntityInstances(); @@ -134,7 +145,10 @@ public void canGetSpecificEntityInstanceUsingGUIDOnPlural(){ // to do/_GUID_ - final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todos/" + paperwork.getPrimaryKeyValue()); + final RepositoryUrlQuery query = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "todos/" + paperwork.getPrimaryKeyValue()); queryResults = query.performQuery().getListEntityInstances(); @@ -155,7 +169,10 @@ public void cannotGetGuidThatDoesNotExist(){ // to do/_GUID_ - final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todo/" + paperwork.getPrimaryKeyValue() + "bob"); + final RepositoryUrlQuery query = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "todo/" + paperwork.getPrimaryKeyValue() + "bob"); queryResults = query.performQuery().getListEntityInstances(); @@ -174,7 +191,10 @@ public void cannotGetGuidThatDoesNotExistWithPlural(){ // to do/_GUID_ - final RepositoryUrlQuery query = RepositoryUrlQueryTestSupport.query(todoManager, "todos/" + paperwork.getPrimaryKeyValue() + "bob"); + final RepositoryUrlQuery query = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + "todos/" + paperwork.getPrimaryKeyValue() + "bob"); queryResults = query.performQuery().getListEntityInstances(); @@ -199,8 +219,8 @@ public void connectionTesting() { // ThingRepository repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance officeWork = repository.addInstance( - new EntityInstance(project).setValue("title", "Office Work")); + EntityInstance officeWork = repository.createInstance( + EntityInstanceDraft.forEntity(project).withField("title", "Office Work")); repository.connectRelationship(officeWork, "tasks", paperwork); repository.connectRelationship(officeWork, "tasks", filework); @@ -209,7 +229,11 @@ public void connectionTesting() { // match on relationships // project/_GUID_/tasks - queryResults = RepositoryUrlQueryTestSupport.query(todoManager, String.format("project/%s/tasks", officeWork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); + queryResults = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + String.format("project/%s/tasks", officeWork.getPrimaryKeyValue())). + performQuery().getListEntityInstances(); Assertions.assertEquals(2, queryResults.size()); Assertions.assertTrue(queryResults.contains(paperwork)); @@ -218,7 +242,11 @@ public void connectionTesting() { // should be able to get projects for a task - queryResults = RepositoryUrlQueryTestSupport.query(todoManager, String.format("todo/%s/task-of", paperwork.getPrimaryKeyValue())).performQuery().getListEntityInstances(); + queryResults = new RepositoryUrlQuery( + todoManager.getSchema(), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME), + String.format("todo/%s/task-of", paperwork.getPrimaryKeyValue())). + performQuery().getListEntityInstances(); Assertions.assertEquals(1, queryResults.size()); Assertions.assertTrue(queryResults.contains(officeWork)); @@ -242,5 +270,4 @@ public void connectionTesting() { } - } diff --git a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java index 4813685f..fc67cb35 100644 --- a/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java +++ b/ercoremodel/src/test/java/uk/co/compendiumdev/thingifier/core/repository/ThingRepositoryContractTest.java @@ -10,6 +10,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.query.RepositoryUrlQuery; import uk.co.compendiumdev.thingifier.core.repository.inmemory.InMemoryThingRepository; @@ -19,6 +20,7 @@ import java.nio.file.Path; import java.util.List; +import java.util.UUID; public class ThingRepositoryContractTest { @@ -77,11 +79,8 @@ public void sqliteFileBackedRepositorySurvivesCloseAndReopen() { EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); EntityDefinition taskDefinition = schema.getEntityDefinitionNamed("task"); - EntityInstance project = new EntityInstance(projectDefinition).setValue("title", "Persisted"); - EntityInstance task = new EntityInstance(taskDefinition).setValue("title", "Loaded relationship"); - - repository.addInstance(project); - repository.addInstance(task); + EntityInstance project = create(repository, projectDefinition, "Persisted"); + EntityInstance task = create(repository, taskDefinition, "Loaded relationship"); repository.connectRelationship(task, "task-of", project); } @@ -114,9 +113,8 @@ public void sqliteRepositoryQuotesGeneratedSqlIdentifiers() { try (ThingRepository repository = SqliteThingRepository.inMemory("quoted")) { repository.initializeFrom(schema); - EntityInstance instance = new EntityInstance(select); - instance.setValue("field with space", "works"); - repository.addInstance(instance); + repository.createInstance( + EntityInstanceDraft.forEntity(select).withField("field with space", "works")); EntityInstance found = repository.findInstanceByFieldNameAndValue( select, "field with space", "works"); @@ -126,6 +124,21 @@ public void sqliteRepositoryQuotesGeneratedSqlIdentifiers() { } } + @Test + public void inMemoryRepositoryGeneratesAutoIdsThroughTheContract() { + ThingRepository repository = new InMemoryThingRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + + exerciseAutoIdGeneration(repository); + } + + @Test + public void sqliteRepositoryGeneratesAutoIdsThroughTheContract() { + try (ThingRepository repository = + SqliteThingRepository.inMemory(EntityRelModel.DEFAULT_DATABASE_NAME)) { + exerciseAutoIdGeneration(repository); + } + } + @Test public void sqliteFileBackedRepositoryFiltersAfterReopen() { ERSchema schema = todoSchema(); @@ -136,8 +149,8 @@ public void sqliteFileBackedRepositoryFiltersAfterReopen() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition).setValue("title", "First")); - repository.addInstance(new EntityInstance(projectDefinition).setValue("title", "Second")); + create(repository, projectDefinition, "First"); + create(repository, projectDefinition, "Second"); } try (ThingRepository reopened = @@ -165,8 +178,8 @@ public void sqliteCountInstancesWorksAfterReopen() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition).setValue("title", "First")); - repository.addInstance(new EntityInstance(projectDefinition).setValue("title", "Second")); + create(repository, projectDefinition, "First"); + create(repository, projectDefinition, "Second"); } try (ThingRepository reopened = @@ -247,12 +260,9 @@ public void sqliteRelationshipReadsWorkAfterReopen() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { repository.initializeFrom(schema); - EntityInstance project = repository.addInstance( - new EntityInstance(projectDefinition).setValue("title", "SQLite project")); - EntityInstance firstTask = repository.addInstance( - new EntityInstance(taskDefinition).setValue("title", "Wire repository")); - EntityInstance secondTask = repository.addInstance( - new EntityInstance(taskDefinition).setValue("title", "Write tests")); + EntityInstance project = create(repository, projectDefinition, "SQLite project"); + EntityInstance firstTask = create(repository, taskDefinition, "Wire repository"); + EntityInstance secondTask = create(repository, taskDefinition, "Write tests"); repository.connectRelationship(project, "tasks", firstTask); repository.connectRelationship(project, "tasks", secondTask); @@ -316,12 +326,9 @@ public void sqliteConvertibleRegexFilterWorksAfterReopen() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Repository project")); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "repository project")); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Another project")); + create(repository, projectDefinition, "Repository project"); + create(repository, projectDefinition, "repository project"); + create(repository, projectDefinition, "Another project"); } try (ThingRepository reopened = @@ -351,10 +358,8 @@ public void sqliteRegexpFilterWorksAfterReopen() { SqliteThingRepository.fileBacked(EntityRelModel.DEFAULT_DATABASE_NAME, databasePath)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Repository project")); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Another project")); + create(repository, projectDefinition, "Repository project"); + create(repository, projectDefinition, "Another project"); } try (ThingRepository reopened = @@ -383,10 +388,8 @@ public void sqliteInMemoryRegexpFilterWorks() { SqliteThingRepository.inMemory(EntityRelModel.DEFAULT_DATABASE_NAME)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Repository project")); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Another project")); + create(repository, projectDefinition, "Repository project"); + create(repository, projectDefinition, "Another project"); QueryFilterParams regexParams = new QueryFilterParams(); regexParams.put("title", "~=Repository [Pp]roject"); @@ -408,8 +411,7 @@ public void sqliteInvalidRegexFilterFails() { try (ThingRepository repository = SqliteThingRepository.inMemory(EntityRelModel.DEFAULT_DATABASE_NAME)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Repository project")); + create(repository, projectDefinition, "Repository project"); QueryFilterParams regexParams = new QueryFilterParams(); regexParams.put("title", "~=["); @@ -428,8 +430,7 @@ public void sqliteTooComplexRegexFilterFails() { try (ThingRepository repository = SqliteThingRepository.inMemory(EntityRelModel.DEFAULT_DATABASE_NAME)) { repository.initializeFrom(schema); - repository.addInstance(new EntityInstance(projectDefinition). - setValue("title", "Repository project")); + create(repository, projectDefinition, "Repository project"); QueryFilterParams regexParams = new QueryFilterParams(); regexParams.put("title", "~=(.+)+"); @@ -447,12 +448,16 @@ private void exerciseRepositoryContract(final ThingRepository repository) { EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); EntityDefinition taskDefinition = schema.getEntityDefinitionNamed("task"); - EntityInstance project = new EntityInstance(projectDefinition); - project.setValue("title", "Repository project"); - - repository.addInstance(project); + EntityInstance project = create(repository, projectDefinition, "Repository project"); Assertions.assertEquals("1", project.getPrimaryKeyValue()); + Assertions.assertThrows( + NoSuchMethodException.class, + () -> EntityInstance.class.getMethod("setValue", String.class, String.class)); + Assertions.assertEquals( + "Repository project", + repository.findInstanceByPrimaryKey( + projectDefinition, "1").getFieldValue("title").asString()); Assertions.assertEquals(project, repository.findInstanceByPrimaryKey(projectDefinition, "1")); Assertions.assertEquals(project, repository.findInstanceByQueryIdentifier(projectDefinition, "1")); Assertions.assertEquals(project, repository.findInstanceByFieldNameAndValue( @@ -461,14 +466,20 @@ private void exerciseRepositoryContract(final ThingRepository repository) { Assertions.assertEquals(1, repository.countInstances(projectDefinition)); Assertions.assertEquals(0, repository.countInstances(taskDefinition)); - EntityInstance duplicate = new EntityInstance(projectDefinition); - duplicate.setValue("title", "Repository project"); + EntityInstance duplicate = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(projectDefinition). + withField("title", "Repository project")); Assertions.assertFalse(repository.checkFieldsForUniqueNess(duplicate, false).isValid()); - EntityInstance secondProject = new EntityInstance(projectDefinition); - secondProject.setValue("title", "Another project"); - repository.addInstance(secondProject); + EntityInstance secondProject = create(repository, projectDefinition, "Another project"); Assertions.assertEquals(2, repository.countInstances(projectDefinition)); + secondProject = repository.replaceInstance( + secondProject, + EntityInstanceDraft.forEntity(projectDefinition).withField("title", "Replacement project")); + Assertions.assertEquals("2", secondProject.getPrimaryKeyValue()); + Assertions.assertEquals( + "Replacement project", + repository.findInstanceByPrimaryKey(projectDefinition, "2"). + getFieldValue("title").asString()); QueryFilterParams filteredParams = new QueryFilterParams(); filteredParams.put("id", ">=2"); @@ -491,16 +502,19 @@ private void exerciseRepositoryContract(final ThingRepository repository) { Assertions.assertEquals(1, regexProjects.size()); Assertions.assertEquals(project, regexProjects.get(0)); - EntityInstance task = new EntityInstance(taskDefinition); - task.setValue("title", "Wire repository"); - repository.addInstance(task); + EntityInstance task = create(repository, taskDefinition, "Wire repository"); repository.connectRelationship(project, "tasks", task); - EntityInstance secondTask = new EntityInstance(taskDefinition); - secondTask.setValue("title", "Write tests"); - repository.addInstance(secondTask); + EntityInstance secondTask = create(repository, taskDefinition, "Write tests"); repository.connectRelationship(project, "tasks", secondTask); + Assertions.assertThrows( + NoSuchMethodException.class, + () -> EntityInstance.class.getMethod("getRelationships")); + Assertions.assertThrows( + NoSuchMethodException.class, + () -> EntityInstance.class.getMethod( + "clearAllFields")); Assertions.assertTrue(repository.getConnectedItems(task, "task-of").contains(project)); Assertions.assertTrue(repository.getConnectedItems(project, "tasks").contains(task)); @@ -550,9 +564,7 @@ private void exerciseRepositoryContract(final ThingRepository repository) { repository.resetAutoIncrementCounter(projectDefinition, "id"); - EntityInstance resetProject = new EntityInstance(projectDefinition); - resetProject.setValue("title", "Reset project"); - repository.addInstance(resetProject); + EntityInstance resetProject = create(repository, projectDefinition, "Reset project"); Assertions.assertEquals("1", resetProject.getPrimaryKeyValue()); } @@ -564,10 +576,7 @@ private void exerciseProviderIsolation(final ThingRepositoryProvider provider) { provider.createRepository("session-one", schema); EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); - EntityInstance project = new EntityInstance(projectDefinition); - project.setValue("title", "Only default"); - - defaultRepository.addInstance(project); + create(defaultRepository, projectDefinition, "Only default"); Assertions.assertEquals(1, defaultRepository.listInstances(projectDefinition).size()); Assertions.assertEquals( @@ -575,22 +584,76 @@ private void exerciseProviderIsolation(final ThingRepositoryProvider provider) { provider.getRepository("session-one").listInstances(projectDefinition).size()); } + private void exerciseAutoIdGeneration(final ThingRepository repository) { + ERSchema schema = autoIdSchema(); + repository.initializeFrom(schema); + + EntityDefinition session = schema.getEntityDefinitionNamed("session"); + EntityInstance firstSession = repository.createInstance(EntityInstanceDraft.forEntity(session)); + EntityInstance secondSession = repository.createInstance(EntityInstanceDraft.forEntity(session)); + + Assertions.assertNotNull(firstSession.getPrimaryKeyValue()); + Assertions.assertNotNull(secondSession.getPrimaryKeyValue()); + Assertions.assertDoesNotThrow(() -> UUID.fromString(firstSession.getPrimaryKeyValue())); + Assertions.assertDoesNotThrow(() -> UUID.fromString(secondSession.getPrimaryKeyValue())); + Assertions.assertNotEquals( + firstSession.getPrimaryKeyValue(), + secondSession.getPrimaryKeyValue()); + + String explicitGuid = "12345678-1234-1234-1234-123456789abc"; + EntityInstance explicitSession = repository.createInstance( + EntityInstanceDraft.forEntity(session). + withProtectedField("guid", explicitGuid)); + Assertions.assertEquals(explicitGuid, explicitSession.getPrimaryKeyValue()); + + EntityDefinition ticket = schema.getEntityDefinitionNamed("ticket"); + EntityInstance firstTicket = repository.createInstance(EntityInstanceDraft.forEntity(ticket)); + EntityInstance secondTicket = repository.createInstance(EntityInstanceDraft.forEntity(ticket)); + + Assertions.assertEquals("1", firstTicket.getPrimaryKeyValue()); + Assertions.assertEquals("2", secondTicket.getPrimaryKeyValue()); + + EntityInstance explicitTicket = repository.createInstance( + EntityInstanceDraft.forEntity(ticket). + withProtectedField("id", "25")); + EntityInstance ticketAfterExplicit = repository.createInstance( + EntityInstanceDraft.forEntity(ticket)); + + Assertions.assertEquals("25", explicitTicket.getPrimaryKeyValue()); + Assertions.assertEquals("26", ticketAfterExplicit.getPrimaryKeyValue()); + } + private void addProjectAndTask( final ThingRepository repository, final ERSchema schema) { EntityDefinition projectDefinition = schema.getEntityDefinitionNamed("project"); EntityDefinition taskDefinition = schema.getEntityDefinitionNamed("task"); - EntityInstance project = new EntityInstance(projectDefinition). - setValue("title", "Repository project"); - EntityInstance task = new EntityInstance(taskDefinition). - setValue("title", "Wire repository"); - - repository.addInstance(project); - repository.addInstance(task); + EntityInstance project = create(repository, projectDefinition, "Repository project"); + EntityInstance task = create(repository, taskDefinition, "Wire repository"); repository.connectRelationship(project, "tasks", task); } + private EntityInstance create( + final ThingRepository repository, + final EntityDefinition entity, + final String title) { + return repository.createInstance( + EntityInstanceDraft.forEntity(entity).withField("title", title)); + } + + private ERSchema autoIdSchema() { + ERSchema schema = new ERSchema(); + + EntityDefinition session = schema.defineEntity("session", "sessions", -1); + session.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); + + EntityDefinition ticket = schema.defineEntity("ticket", "tickets", -1); + ticket.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); + + return schema; + } + private void assertExportedJsonContainsProjectAndTask(final String json) { Assertions.assertTrue(json.contains("\"projects\"")); Assertions.assertTrue(json.contains("\"tasks\"")); diff --git a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java index e49380c6..c162eaef 100644 --- a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java +++ b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoAPIDataPopulator.java @@ -3,7 +3,7 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoAPIDataPopulator implements RepositoryDataPopulator { @@ -23,8 +23,8 @@ public void populate(final ERSchema schema, final ThingRepository repository) { EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); for (String todoItem : TODOS) { - repository.addInstance(new EntityInstance(todo). - setValue("title", todoItem)); + repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", todoItem)); } } } diff --git a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java index f21d2242..be1402fb 100644 --- a/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java +++ b/examplemodels/src/main/java/uk/co/compendiumdev/thingifier/application/data/TodoManagerAPIDataPopulator.java @@ -4,6 +4,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoManagerAPIDataPopulator implements RepositoryDataPopulator { @@ -13,20 +14,20 @@ public void populate(final ERSchema schema, final ThingRepository repository) { EntityDefinition category = schema.getEntityDefinitionNamed("category"); EntityDefinition project = schema.getEntityDefinitionNamed("project"); - EntityInstance paperwork = repository.addInstance(new EntityInstance(todo). - setValue("title", "scan paperwork")); + EntityInstance paperwork = repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", "scan paperwork")); - EntityInstance filework = repository.addInstance(new EntityInstance(todo). - setValue("title", "file paperwork")); + EntityInstance filework = repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", "file paperwork")); - EntityInstance officeCategory = repository.addInstance(new EntityInstance(category). - setValue("title", "Office")); + EntityInstance officeCategory = repository.createInstance( + EntityInstanceDraft.forEntity(category).withField("title", "Office")); - repository.addInstance(new EntityInstance(category). - setValue("title", "Home")); + repository.createInstance( + EntityInstanceDraft.forEntity(category).withField("title", "Home")); - EntityInstance officeWork = repository.addInstance(new EntityInstance(project). - setValue("title", "Office Work")); + EntityInstance officeWork = repository.createInstance( + EntityInstanceDraft.forEntity(project).withField("title", "Office Work")); repository.connectRelationship(officeWork, "tasks", paperwork); repository.connectRelationship(officeWork, "tasks", filework); diff --git a/pom.xml b/pom.xml index ff189c65..17a18f55 100644 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ ${maven.multiModuleProjectDirectory}/checkstyle-project-fqn.xml + true true true true diff --git a/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java b/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java index e2239421..56296a26 100644 --- a/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java +++ b/swaggerizer/src/test/java/uk/co/compendiumdev/thingifier/swaggerizer/TodoAPITestDataPopulator.java @@ -3,7 +3,7 @@ import uk.co.compendiumdev.thingifier.core.domain.datapopulator.RepositoryDataPopulator; import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class TodoAPITestDataPopulator implements RepositoryDataPopulator { @@ -24,7 +24,8 @@ public void populate(final ERSchema schema, final ThingRepository repository) { EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); for(String todoItem : todos){ - repository.addInstance(new EntityInstance(todo).setValue("title", todoItem)); + repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", todoItem)); } } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java index 4cfc533b..7d63667f 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonPopulator.java @@ -8,11 +8,9 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.ERSchema; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; -import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; -import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; -import java.util.ArrayList; import java.util.Map; import java.util.UUID; @@ -78,27 +76,24 @@ private void populateAsInstanceOf(String entityPluralName, JsonElement instance) } }); - EntityInstance entityInstance = new EntityInstance(entityDefn); + EntityInstanceDraft draft = EntityInstanceDraft.forEntity(entityDefn); obj.entrySet().forEach(value -> { Field fieldDefn = entityDefn.getField(value.getKey()); switch (fieldDefn.getType()){ case AUTO_GUID: - entityInstance.overrideValue(fieldDefn.getName(), UUID.fromString(value.getValue().getAsString()).toString()); + draft.withProtectedField( + fieldDefn.getName(), + UUID.fromString(value.getValue().getAsString()).toString()); break; case AUTO_INCREMENT: - entityInstance.overrideValue(fieldDefn.getName(), String.valueOf(value.getValue().getAsInt())); + draft.withProtectedField(fieldDefn.getName(), String.valueOf(value.getValue().getAsInt())); break; default: - entityInstance.setValue(value.getKey(), value.getValue().getAsString()); + draft.withField(value.getKey(), value.getValue().getAsString()); } }); - ValidationReport validation = entityInstance.validateFieldValues(new ArrayList<>(), true); - if(!validation.isValid()){ - throw new RuntimeException(String.format("ERROR: Invalid %s entity instance because %s", entityDefn.getName(), validation.getCombinedErrorMessages())); - } - // instance is valid, so add it - repository.addInstance(entityInstance); + repository.createInstance(draft); } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java index 56240bf9..52404810 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/ermodelconversion/JsonThing.java @@ -137,6 +137,62 @@ public JsonObject asJsonObject(final InstanceFields fields){ return jsonobj; } + private JsonObject asFieldJsonObject(final EntityInstance instance){ + final JsonObject jsonobj = new JsonObject(); + + if (instance == null) { + return jsonobj; + } + + for (String fieldName : instance.getFieldNames()) { + Field theField = instance.getEntity().getField(fieldName); + + String fieldValue = ""; + + try { + fieldValue = instance.getFieldValue(theField.getName()).asString(); + + if(apiConfig.willRenderFieldsAsDefinedTypes()) { + switch (theField.getType()) { + case BOOLEAN: + jsonobj.addProperty(fieldName, Boolean.valueOf(fieldValue)); + break; + case INTEGER: + case AUTO_INCREMENT: + jsonobj.addProperty(fieldName, Integer.valueOf(fieldValue)); + break; + case FLOAT: + jsonobj.addProperty(fieldName, Float.valueOf(fieldValue)); + break; + case OBJECT: + final FieldValue objectFieldValue = instance.getFieldValue(fieldName); + if(objectFieldValue!=null) { + jsonobj.add(fieldName, asJsonObject( + objectFieldValue.asObject())); + } + break; + default: + jsonobj.addProperty(fieldName, fieldValue); + } + }else { + if(theField.getType()==FieldType.OBJECT){ + final FieldValue objectFieldValue = instance.getFieldValue(fieldName); + if(objectFieldValue!=null) { + jsonobj.add(fieldName, asJsonObject( + objectFieldValue.asObject())); + } + }else { + jsonobj.addProperty(fieldName, fieldValue); + } + } + }catch(Exception e){ + // ignore missing optional/default fields + } + } + + return jsonobj; + } + /** * Suitable for JSON Output as it is just the object * @param thingInstance @@ -153,7 +209,7 @@ public JsonObject asJsonObject(final EntityInstance thingInstance) { return new JsonObject(); } - final JsonObject jsonobj = asJsonObject(thingInstance.getFields()); + final JsonObject jsonobj = asFieldJsonObject(thingInstance); /* @@ -181,12 +237,13 @@ public JsonObject asJsonObject(final EntityInstance thingInstance) { Boolean allowCompressedRelationships = apiConfig.willRenderRelationshipsAsCompressed(); // "relationships" : [ - if(relationships.size()>0 && thingInstance.getRelationships().hasAnyRelationshipInstances()){ + if(relationships.size()>0 && thingInstance.hasRelationshipInstances()){ final JsonArray relationshipsArray = new JsonArray(); // fill the array "relationship_name" : [ for(RelationshipVectorDefinition relationship : relationships){ - final Collection relatedItems = thingInstance.getRelationships().getConnectedItems(relationship.getName()); + final Collection relatedItems = + thingInstance.getRelatedItems(relationship.getName()); boolean isCompressedRelationship=true; if(thingInstance.getEntity().hasFieldNameDefined(relationship.getName())){ diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyArgsProcessor.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyArgsProcessor.java index 9af76633..d19d2528 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyArgsProcessor.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/BodyArgsProcessor.java @@ -22,11 +22,17 @@ public BodyArgsProcessor(Thingifier thingifier, BodyParser bodyargs){ public List> removeRelationshipsFrom(final EntityInstance instance, final String database) { + return removeRelationshipsFrom(instance.getEntity(), database); + } + + public List> removeRelationshipsFrom( + final EntityDefinition entity, + final String database) { List> fullargs = bodyargs.getFlattenedStringMap(); RelationshipCollector collectedRelationships = new RelationshipCollector(); - identifyRelationships(fullargs, instance, collectedRelationships, database); + identifyRelationships(fullargs, entity, collectedRelationships, database); for(Map.Entry removeMe : collectedRelationships.getRelationshipsKeys()) { fullargs.remove(removeMe); @@ -40,6 +46,13 @@ public void identifyRelationships(List> fullargs, final EntityInstance instance, RelationshipCollector collector, final String database){ + identifyRelationships(fullargs, instance.getEntity(), collector, database); + } + + public void identifyRelationships(List> fullargs, + final EntityDefinition entity, + RelationshipCollector collector, + final String database){ // assume any relationships errors already reported @@ -68,9 +81,9 @@ public void identifyRelationships(List> fullargs, if(instanceToRelateTo ==null){ // but it might not be // TODO: find other usages of this pattern and refactor to - if(instance.getEntity().related().hasRelationship(relationshipName)){ + if(entity.related().hasRelationship(relationshipName)){ final List relationshipsAre = - instance.getEntity().related().getRelationships(relationshipName); + entity.related().getRelationships(relationshipName); for(RelationshipVectorDefinition relate : relationshipsAre){ final EntityDefinition typeOfThing = relate.getTo(); instanceToRelateTo = thingifier.getRepository(database). diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityInstanceBulkUpdater.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityInstanceBulkUpdater.java index 0a2d5641..7515e1fa 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityInstanceBulkUpdater.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/EntityInstanceBulkUpdater.java @@ -1,51 +1,82 @@ package uk.co.compendiumdev.thingifier.api.restapihandlers; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; +import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceRepositoryAccess; import java.util.List; public class EntityInstanceBulkUpdater { + private final EntityDefinition entity; private final EntityInstance instance; + public EntityInstanceBulkUpdater(EntityDefinition entity) { + this.entity = entity; + this.instance = null; + } + public EntityInstanceBulkUpdater(EntityInstance instance) { + this.entity = instance.getEntity(); this.instance = instance; } - public void setFieldValuesFrom(List fieldValues) { + public EntityInstanceDraft setFieldValuesFrom(List fieldValues) { - final List anyErrors = instance.getFields().findAnyGuidOrIdDifferences(fieldValues); - if(anyErrors.size()>0){ - throw new RuntimeException(anyErrors.get(0)); + if (instance != null) { + final List anyErrors = + EntityInstanceRepositoryAccess.findAnyGuidOrIdDifferences(instance, fieldValues); + if(anyErrors.size()>0){ + throw new RuntimeException(anyErrors.get(0)); + } } - setFieldValuesFromArgsIgnoring(fieldValues, instance.getEntity().getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID)); + return setFieldValuesFromArgsIgnoring( + fieldValues, + entity.getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID)); } - public void setFieldValuesFromArgsIgnoring(List fieldValues, + public EntityInstanceDraft setFieldValuesFromArgsIgnoring(List fieldValues, final List ignoreFields) { + EntityInstanceDraft draft = EntityInstanceDraft.forEntity(entity); for (NamedValue entry : fieldValues) { // Handle attempt to amend a protected field if (!ignoreFields.contains(entry.getName())) { // set the value because it is not protected - instance.setValue(entry.getName(), entry.asString()); + draft.withField(entry.getName(), entry.asString()); } } + return draft; } - public void overrideFieldValuesFromArgsIgnoring(final List fieldValues, + public EntityInstanceDraft overrideFieldValuesFromArgsIgnoring(final List fieldValues, final List ignoreFields) { + EntityInstanceDraft draft = EntityInstanceDraft.forEntity(entity); for (NamedValue entry : fieldValues) { // Handle attempt to amend a protected field if (!ignoreFields.contains(entry.getName())) { // set the value because it is not protected - instance.overrideValue(entry.getName(), entry.asString()); + if (isProtectedField(entry.getName())) { + draft.withProtectedField(entry.getName(), entry.asString()); + } else { + draft.withField(entry.getName(), entry.asString()); + } } } + return draft; + } + + private boolean isProtectedField(final String fieldName) { + Field field = entity.getField(fieldName); + return field != null && + (field.getType() == FieldType.AUTO_INCREMENT || + field.getType() == FieldType.AUTO_GUID); } } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java index 0a0747af..f71f50c8 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RelationshipCreation.java @@ -9,6 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.relationship.RelationshipVectorDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.validation.EntityInstanceStateValidator; import java.util.List; import java.util.Map; @@ -16,9 +17,11 @@ public class RelationshipCreation { private final Thingifier thingifier; + private final EntityInstanceStateValidator stateValidator; public RelationshipCreation(final Thingifier aThingifier) { this.thingifier = aThingifier; + this.stateValidator = new EntityInstanceStateValidator(); } public ApiResponse create( @@ -138,7 +141,7 @@ public ApiResponse create( connectThis, relationshipToUse.getName(), relatedItem); // enforce cardinality on relationship - ValidationReport validNow = relatedItem.validateRelationships(); + ValidationReport validNow = stateValidator.validateRelationships(relatedItem); if(!validNow.isValid()){ response = ApiResponse.error(400, validNow.getErrorMessages()); thingifier.deleteThing(relatedItem, database); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java index 9bf6bcfb..eba9e61d 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RestApiPostHandler.java @@ -9,14 +9,17 @@ import uk.co.compendiumdev.thingifier.api.restapihandlers.commonerrorresponse.NoSuchEntity; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.validation.EntityInstanceStateValidator; import java.util.List; public class RestApiPostHandler { private final Thingifier thingifier; + private final EntityInstanceStateValidator stateValidator; public RestApiPostHandler(final Thingifier aThingifier) { thingifier = aThingifier; + stateValidator = new EntityInstanceStateValidator(); } public ApiResponse handle(final String url, final BodyParser args, final HttpHeadersBlock requestHeaders) { @@ -41,8 +44,9 @@ public ApiResponse handle(final String url, final BodyParser args, final HttpHea EntityInstance returnedInstance = response.getReturnedInstance(); final List protectedFieldNames = returnedInstance.getEntity().getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); - ValidationReport validity = returnedInstance.validateFieldValues(protectedFieldNames, false); - validity.combine(returnedInstance.validateRelationships()); + ValidationReport validity = stateValidator.validateFields( + returnedInstance, protectedFieldNames, false); + validity.combine(stateValidator.validateRelationships(returnedInstance)); if (validity.isValid()) { return response; diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingAmendment.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingAmendment.java index 7b323185..be260a86 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingAmendment.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingAmendment.java @@ -7,6 +7,7 @@ import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.List; import java.util.Map; @@ -37,60 +38,44 @@ public ApiResponse amendInstance(final BodyParser bodyargs, final EntityInstance } } - EntityInstance cloned = null; + EntityInstanceDraft draft; try { - - cloned = instance.createDuplicateWithoutRelationships(); - - if(clearFieldsBeforeSettingFromArgs){ - // if you want an idempotent amend then clear it down prior to amending - cloned.clearAllFields(); - - } List fieldValues = FieldValues. fromListMapEntryStringString( new BodyArgsProcessor(thingifier, bodyargs). removeRelationshipsFrom(instance, database)); - new EntityInstanceBulkUpdater(cloned).setFieldValuesFrom(fieldValues); + draft = new EntityInstanceBulkUpdater(instance).setFieldValuesFrom(fieldValues); } catch (Exception e) { return ApiResponse.error(400, e.getMessage()); } - final List protectedFieldNames = cloned.getEntity().getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); - ValidationReport validation = cloned.validateFieldValues(protectedFieldNames, false); - // validate the relationships as well - ValidationReport relationshipsValidation = new BodyRelationshipValidator(thingifier).validate(bodyargs, cloned.getEntity(), database); - validation.combine(relationshipsValidation); - - ValidationReport uniquenessCheck = thingifier.getRepository(database). - checkFieldsForUniqueNess(cloned, true); - validation.combine(uniquenessCheck); + ValidationReport validation = new BodyRelationshipValidator(thingifier). + validate(bodyargs, instance.getEntity(), database); if (validation.isValid()) { - if(clearFieldsBeforeSettingFromArgs){ - instance.clearAllFields(); - // delete all existing relationships for idempotent amend - // todo: this returns a list of 'items' to be removed based on relationship - thingifier.getRepository(database).removeAllRelationships(instance); + final EntityInstance updated; + try { + if(clearFieldsBeforeSettingFromArgs){ + updated = thingifier.getRepository(database).replaceInstance(instance, draft); + // delete all existing relationships for idempotent amend + // todo: this returns a list of 'items' to be removed based on relationship + thingifier.getRepository(database).removeAllRelationships(updated); + } else { + updated = thingifier.getRepository(database).patchInstance(instance, draft); + } + } catch (Exception e) { + return ApiResponse.error(400, e.getMessage()); } - List fieldValues = FieldValues. - fromListMapEntryStringString( - new BodyArgsProcessor(thingifier, bodyargs). - removeRelationshipsFrom(instance, database)); - - new EntityInstanceBulkUpdater(instance).setFieldValuesFrom(fieldValues); - - thingifier.getRepository(database).updateInstance(instance); // todo: should we check that this was actually a success? - final ApiResponse relresponse = new RelationshipCreator(thingifier).createRelationships(bodyargs, instance, database); + final ApiResponse relresponse = new RelationshipCreator(thingifier).createRelationships(bodyargs, updated, database); // todo: should check if any of the 'removed items due to relationship removal' need to be removed // and remove them if we do - return ApiResponse.success().returnSingleInstance(instance); + return ApiResponse.success().returnSingleInstance(updated); } else { // do not add it, report the errors return ApiResponse.error(400, validation.getErrorMessages()); diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java index 3d36e431..510cc5d7 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/api/restapihandlers/ThingCreation.java @@ -8,8 +8,8 @@ import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; -import java.util.ArrayList; import java.util.List; public class ThingCreation { @@ -33,19 +33,13 @@ public ApiResponse with(final BodyParser bodyargs, final EntityDefinition thing, return ApiResponse.error(400, String.format("Invalid Creation: %s",validated.getCombinedErrorMessages())); } - // todo: separate validation for creation of 'cannot' create with ID, or cannot create with GUID - EntityInstance instance = new EntityInstance(thing); - instance.addAutoGUIDstoInstance(); - instance.addAutoIncrementIdsToInstance( - thingifier.getRepository(database).countersFor(thing)); - return addNewThingWithFields(bodyargs, instance, thing, database); + return addNewThingWithFields(bodyargs, EntityInstanceDraft.forEntity(thing), thing, database); } // create with GUID and IDs is normally associated with PUT or 'insert' public ApiResponse withPrimaryKey(final String primaryKey, final BodyParser bodyargs, final EntityDefinition thing, final String database) { - EntityInstance instance; ValidationReport validated; validated = new BodyCreationValidator(thingifier). @@ -59,12 +53,12 @@ public ApiResponse withPrimaryKey(final String primaryKey, final BodyParser body validated.getCombinedErrorMessages()); } - instance = new EntityInstance(thing); - instance.overrideValue(thing.getPrimaryKeyField().getName(), primaryKey); - - // TODO: should we really do this for a PUT when everything needs to exist, suspect this is a bug - instance.addAutoIncrementIdsToInstance( - thingifier.getRepository(database).countersFor(thing)); + EntityInstanceDraft draft = EntityInstanceDraft.forEntity(thing); + if (isProtectedField(thing.getPrimaryKeyField().getType())) { + draft.withProtectedField(thing.getPrimaryKeyField().getName(), primaryKey); + } else { + draft.withField(thing.getPrimaryKeyField().getName(), primaryKey); + } validated = new BodyRelationshipValidator(thingifier).validate(bodyargs, thing, database); @@ -83,94 +77,114 @@ public ApiResponse withPrimaryKey(final String primaryKey, final BodyParser body thingifier.getRepository(database).setNextIdCountersToAccomodate(thing, fieldValues); - return insertNewThingWithFields(bodyargs, instance, thing, database); + return insertNewThingWithFields(bodyargs, draft, thing, database); } - private ApiResponse addNewThingWithFields(final BodyParser bodyargs, final EntityInstance instance, + private ApiResponse addNewThingWithFields(final BodyParser bodyargs, final EntityInstanceDraft baseDraft, final EntityDefinition thing, final String database) { if(thingifier.apiConfig().willApiEnforceDeclaredTypesInInput()) { - ValidationReport validatedTypes = bodyargs.validateAgainstType(instance.getEntity()); + ValidationReport validatedTypes = bodyargs.validateAgainstType(thing); if(!validatedTypes.isValid()){ return ApiResponse.error(400, validatedTypes.getCombinedErrorMessages()); } } // todo: need to separate relationships from field in the path prior to sending through to setFields + EntityInstanceDraft draft; try { // if any guids or ids then throw an error if they are not the same List fieldValues = FieldValues. fromListMapEntryStringString( new BodyArgsProcessor(thingifier, bodyargs). - removeRelationshipsFrom(instance, database)); + removeRelationshipsFrom(thing, database)); - new EntityInstanceBulkUpdater(instance).setFieldValuesFrom(fieldValues); + draft = new EntityInstanceBulkUpdater(thing).setFieldValuesFrom(fieldValues); + copyBaseDraftValues(baseDraft, draft); } catch (Exception e) { return ApiResponse.error(400, e.getMessage()); } - - final List protectedFieldNames = instance.getEntity(). - getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); - - ValidationReport validation = instance.validateFieldValues(protectedFieldNames, false); - - return addValidatedInstance(bodyargs, instance, thing, database, validation); + return addValidatedInstance(bodyargs, draft, thing, database); } - private ApiResponse insertNewThingWithFields(final BodyParser bodyargs, final EntityInstance instance, + private ApiResponse insertNewThingWithFields(final BodyParser bodyargs, final EntityInstanceDraft baseDraft, final EntityDefinition thing, final String database) { if(thingifier.apiConfig().willApiEnforceDeclaredTypesInInput()) { - ValidationReport validatedTypes = bodyargs.validateAgainstType(instance.getEntity()); + ValidationReport validatedTypes = bodyargs.validateAgainstType(thing); if(!validatedTypes.isValid()){ return ApiResponse.error(400, validatedTypes.getCombinedErrorMessages()); } } + EntityInstanceDraft draft; try { // set all the fields and values, except guids - List ignoreFields = instance.getEntity(). + List ignoreFields = thing. getFieldNamesOfType(FieldType.AUTO_GUID); List fieldValues = FieldValues. fromListMapEntryStringString( new BodyArgsProcessor(thingifier, bodyargs). - removeRelationshipsFrom(instance, database)); + removeRelationshipsFrom(thing, database)); - new EntityInstanceBulkUpdater(instance).overrideFieldValuesFromArgsIgnoring(fieldValues, ignoreFields); + draft = new EntityInstanceBulkUpdater(thing). + overrideFieldValuesFromArgsIgnoring(fieldValues, ignoreFields); + copyBaseDraftValues(baseDraft, draft); } catch (Exception e) { return ApiResponse.error(400, e.getMessage()); } - ValidationReport validation = instance.validateFieldValues(new ArrayList<>(), true); - - return addValidatedInstance(bodyargs, instance, thing, database, validation); + return addValidatedInstance(bodyargs, draft, thing, database); } - private ApiResponse addValidatedInstance(BodyParser bodyargs, EntityInstance instance, EntityDefinition thing, String database, ValidationReport validation) { + private ApiResponse addValidatedInstance( + BodyParser bodyargs, + EntityInstanceDraft draft, + EntityDefinition thing, + String database) { - // check for uniqueness prior to adding - ValidationReport uniquenessCheck = thingifier.getRepository(database). - checkFieldsForUniqueNess(instance, false); - validation.combine(uniquenessCheck); + try { + EntityInstance instance = thingifier.getRepository(database).createInstance(draft); + return new RelationshipCreator(thingifier).createRelationships(bodyargs, instance, database); + }catch(Exception e){ + return ApiResponse.error(400, creationErrorMessage(e)); + } + } - if (validation.isValid()) { - try { - thingifier.getRepository(database).addInstance(instance); - }catch(Exception e){ - return ApiResponse.error(400, e.getMessage()); - } + private String creationErrorMessage(final Exception e) { + String message = e.getMessage(); + if (message == null) { + return ""; + } - return new RelationshipCreator(thingifier).createRelationships(bodyargs, instance, database); + String validationPrefix = "Failed Validation: "; + if (message.startsWith(validationPrefix) && + message.substring(validationPrefix.length()).endsWith(" : field is mandatory") && + !message.substring(validationPrefix.length()).contains(", ")) { + return message.substring(validationPrefix.length()); + } + return message; + } - } else { - // do not add it, report the errors - return ApiResponse.error(400, validation.getErrorMessages()); + private void copyBaseDraftValues( + final EntityInstanceDraft baseDraft, + final EntityInstanceDraft draft) { + for (NamedValue value : baseDraft.getFieldValues()) { + draft.withField(value.getName(), value.asString()); + } + for (NamedValue protectedValue : baseDraft.getProtectedFieldValues()) { + draft.withProtectedField(protectedValue.getName(), protectedValue.asString()); } } + private boolean isProtectedField(final FieldType fieldType) { + return fieldType == FieldType.AUTO_INCREMENT || + fieldType == FieldType.AUTO_GUID; + } + } diff --git a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DocumentationThingInstance.java b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DocumentationThingInstance.java index b1c9f8dc..2c208ff4 100644 --- a/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DocumentationThingInstance.java +++ b/thingifier/src/main/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DocumentationThingInstance.java @@ -1,31 +1,40 @@ package uk.co.compendiumdev.thingifier.htmlgui.htmlgen; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; public class DocumentationThingInstance{ - private final EntityInstance instance; + private final EntityInstanceDraft draft; private final EntityDefinition definition; public DocumentationThingInstance(final EntityDefinition eDefn) { this.definition = eDefn; - this.instance = new EntityInstance(eDefn); + this.draft = EntityInstanceDraft.forEntity(eDefn); } public EntityInstance getInstance(){ - return this.instance; + return EntityInstance.fromDraft(this.draft); } public EntityInstance withoutIDsOrGUIDs(){ for(String name : definition.getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID)){ overrideValue(name, null); } - return instance; + return getInstance(); } public void overrideValue(final String name, final String exampleValue) { - this.instance.overrideValue(name, exampleValue); + Field field = definition.getField(name); + if (field != null && + (field.getType() == FieldType.AUTO_INCREMENT || + field.getType() == FieldType.AUTO_GUID)) { + this.draft.withProtectedField(name, exampleValue); + return; + } + this.draft.withField(name, exampleValue); } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java b/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java index a8e22b22..081e12f3 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/UsageTest.java @@ -1,14 +1,14 @@ package uk.co.compendiumdev; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import static uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType.STRING; @@ -44,22 +44,25 @@ public void thingifierCanManageThings(){ addFields(Field.is("url", STRING),Field.is("name",STRING) ); - EntityDefinition urls = ThingifierRepositoryTestSupport.entity(things, "URL"); + EntityDefinition urls = things.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("URL"); + ThingRepository repository = things.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); Assertions.assertTrue(urls.hasFieldNameDefined("url")); Assertions.assertTrue(urls.hasFieldNameDefined("name")); - ThingifierRepositoryTestSupport.repository(things).addInstance(new EntityInstance(urls)). - setValue("name","EvilTester.com"). - setValue("url","http://eviltester.com"); + repository.createInstance(EntityInstanceDraft.forEntity(urls). + withField("name", "EvilTester.com"). + withField("url", "http://eviltester.com")); EntityDefinition user = things.defineThing("USER", "users"); user.addFields(Field.is("name", STRING)); - EntityDefinition users = ThingifierRepositoryTestSupport.entity(things, "USER"); - EntityInstance alan = ThingifierRepositoryTestSupport.repository(things).addInstance(new EntityInstance(users)). - setValue("name","alan"); + EntityDefinition users = things.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("USER"); + EntityInstance alan = repository.createInstance( + EntityInstanceDraft.forEntity(users).withField("name", "alan")); // TODO fix relationshps so that they have values diff --git a/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java index d2516010..3790ce7e 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/api_non_http/SimpleVerbPutEntityInstanceApiNonHttpTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.api_non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -13,7 +14,6 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import java.util.HashMap; import java.util.Map; @@ -60,15 +60,14 @@ public void putCanAmendExistingInstance() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); + EntityDefinition instances = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT requestBody = new HashMap(); requestBody.put("title", "My Office Work"); - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). - setValue("title", "An Existing instances"); + EntityInstance officeWork = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(instances).withField("title", "An Existing instances")); String officeWorkGuid = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(officeWorkGuid); @@ -78,7 +77,7 @@ public void putCanAmendExistingInstance() { Assertions.assertEquals(200, apiresponse.getStatusCode()); Assertions.assertEquals("My Office Work", officeWork.getFieldValue("title").asString()); - officeWork.setValue("title", "office"); + officeWork = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).patchInstance(officeWork, EntityInstanceDraft.forEntity(officeWork.getEntity()).withField("title", "office")); Assertions.assertEquals("office", officeWork.getFieldValue("title").asString()); Assertions.assertNotNull(officeWorkGuid); @@ -95,12 +94,10 @@ public void putCanAmendExistingInstancesWhenOptionalFieldsAreMissing() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); + EntityDefinition instances = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // create something to amend with PUT - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). - setValue("title", "An Existing instances"). - setValue("description", "Existing Description"); + EntityInstance officeWork = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(instances).withField("title", "An Existing instances").withField("description", "Existing Description")); String officeWorkGuid = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(officeWorkGuid); @@ -124,11 +121,10 @@ public void putFailCanNotAmendExistingInstancesWhenMandatoryFieldsAreMissing() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); + EntityDefinition instances = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // create something to amend with PUT - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). - setValue("description", "An Existing instance title"); + EntityInstance officeWork = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(instances).withField("title", "An Existing instance").withField("description", "An Existing instance title")); String officeWorkGuid = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(officeWorkGuid); @@ -141,7 +137,9 @@ public void putFailCanNotAmendExistingInstancesWhenMandatoryFieldsAreMissing() { apiresponse = thingifier.api().put(String.format("entities/%s", officeWork.getPrimaryKeyValue()), getSimpleParser(requestBody, thingifier), new HttpHeadersBlock()); Assertions.assertEquals(400, apiresponse.getStatusCode()); - Assertions.assertTrue(apiresponse.getErrorMessages().contains("title : field is mandatory")); + Assertions.assertTrue(apiresponse.getErrorMessages(). + stream(). + anyMatch(error -> error.contains("title : field is mandatory"))); Assertions.assertEquals("An Existing instance title",officeWork.getFieldValue("description").asString()); } @@ -153,14 +151,12 @@ public void putFailCanNotAmendPrimaryKey() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); + EntityDefinition instances = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(instances)). - setValue("title", "An Existing instances"). - setValue("description", "my original description"); + EntityInstance officeWork = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(instances).withField("title", "An Existing instances").withField("description", "my original description")); String originalID = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(originalID); @@ -194,7 +190,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { ApiResponse apiresponse; Thingifier thingifier = thingifierWithAutoFields(); - EntityDefinition instances = ThingifierRepositoryTestSupport.entity(thingifier, "entity"); + EntityDefinition instances = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT @@ -203,7 +199,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { requestBody.put("title", title); - int currentinstances = ThingifierRepositoryTestSupport.repository(thingifier).countInstances(instances); + int currentinstances = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(instances); Assertions.assertEquals(0, currentinstances); // create with a PUT and a given ID @@ -213,7 +209,7 @@ public void putFailCanNotCreateAnEntityInstanceWithAutoFields() { Assertions.assertEquals(400, apiresponse.getStatusCode()); - Assertions.assertEquals(currentinstances, ThingifierRepositoryTestSupport.repository(thingifier).countInstances(instances)); + Assertions.assertEquals(currentinstances, thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(instances)); Assertions.assertTrue(apiresponse.hasABody()); @@ -238,13 +234,12 @@ public void putFailCanNotAmendAutoGuid() { entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); entityDefn.addField(Field.is("guid", FieldType.AUTO_GUID)); - EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); + EntityDefinition myInstances = myThingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(new EntityInstance(myInstances)). - setValue("title", "An Existing instance"); + EntityInstance officeWork = myThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(myInstances).withField("title", "An Existing instance")); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("guid").asString(); @@ -286,14 +281,11 @@ public void putCanCreateAnInstanceWhenNoAutoFields() { entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); + EntityDefinition myInstances = myThingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT - EntityInstance officeWork = new EntityInstance(entityDefn).setValue("id", "one"). - setValue("title", "An Existing instance"); - - ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(officeWork); + EntityInstance officeWork = myThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(entityDefn).withField("id", "one").withField("title", "An Existing instance")); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("id").asString(); @@ -316,7 +308,7 @@ public void putCanCreateAnInstanceWhenNoAutoFields() { Assertions.assertEquals("An Existing instance", officeWork.getFieldValue("title").asString()); Assertions.assertEquals(originalID, officeWork.getFieldValue("id").asString()); - EntityInstance newInstance = ThingifierRepositoryTestSupport.repository(myThingifier).findInstanceByPrimaryKey(myInstances, newGuid); + EntityInstance newInstance = myThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(myInstances, newGuid); Assertions.assertEquals("My Office Work", newInstance.getFieldValue("title").asString()); Assertions.assertEquals(newGuid, newInstance.getFieldValue("id").asString()); } @@ -334,14 +326,11 @@ public void putCanCreateAnInstanceWhenNoAutoFieldsAndMissingPrimaryKeyFromBody() entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); + EntityDefinition myInstances = myThingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT - EntityInstance officeWork = new EntityInstance(entityDefn).setValue("id", "one"). - setValue("title", "An Existing instance"); - - ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(officeWork); + EntityInstance officeWork = myThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(entityDefn).withField("id", "one").withField("title", "An Existing instance")); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("id").asString(); @@ -365,7 +354,7 @@ public void putCanCreateAnInstanceWhenNoAutoFieldsAndMissingPrimaryKeyFromBody() Assertions.assertEquals("An Existing instance", officeWork.getFieldValue("title").asString()); Assertions.assertEquals(originalID, officeWork.getFieldValue("id").asString()); - EntityInstance newInstance = ThingifierRepositoryTestSupport.repository(myThingifier).findInstanceByPrimaryKey(myInstances, newGuid); + EntityInstance newInstance = myThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(myInstances, newGuid); Assertions.assertEquals("My Office Work", newInstance.getFieldValue("title").asString()); Assertions.assertEquals(newGuid, newInstance.getFieldValue("id").asString()); } @@ -383,14 +372,11 @@ public void putFailCanNotCreateAnInstanceWhenKeyDoesNotMatchBody() { entityDefn.addAsPrimaryKeyField(Field.is("id", FieldType.STRING)); entityDefn.addField(Field.is("title", FieldType.STRING).makeMandatory()); - EntityDefinition myInstances = ThingifierRepositoryTestSupport.entity(myThingifier, "entity"); + EntityDefinition myInstances = myThingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("entity"); // PUT - EntityInstance officeWork = new EntityInstance(entityDefn).setValue("id", "one"). - setValue("title", "An Existing instance"); - - ThingifierRepositoryTestSupport.repository(myThingifier).addInstance(officeWork); + EntityInstance officeWork = myThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(entityDefn).withField("id", "one").withField("title", "An Existing instance")); String originalID = officeWork.getPrimaryKeyValue(); String originalGuid = officeWork.getFieldValue("id").asString(); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java index f4790b5f..0c2a6e5b 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/ModelsTest.java @@ -4,9 +4,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.Collection; import java.util.Random; @@ -14,6 +15,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class ModelsTest { private Thingifier todoManager; + private ThingRepository repository; // create a set of models to build up the interface and usage @@ -31,64 +33,74 @@ public class ModelsTest { public void createDefinitions(){ todoManager = TodoManagerModel.definedAsThingifier(); + repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); } @Test public void createAndDelete(){ - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + final EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); for(int todoCount=0; todoCount < 100; todoCount++){ - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "title " + System.nanoTime()); + repository.createInstance(EntityInstanceDraft.forEntity(todos). + withField("title", "title " + System.nanoTime())); } - Assertions.assertEquals(100, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); + Assertions.assertEquals(100, repository.countInstances(todos)); todoManager.clearAllData(); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); + Assertions.assertEquals(0, repository.countInstances(todos)); } @Test public void createAndDeleteRelationships(){ - final EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + final EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); for(int todoCount=0; todoCount < 100; todoCount++){ - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "title " + System.nanoTime()); + repository.createInstance(EntityInstanceDraft.forEntity(todos). + withField("title", "title " + System.nanoTime())); } - final EntityDefinition projects = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + final EntityDefinition projects = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("project"); for(int todoCount=0; todoCount < 50; todoCount++){ - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(projects)). - setValue("title", "title " + System.nanoTime()); + repository.createInstance(EntityInstanceDraft.forEntity(projects). + withField("title", "title " + System.nanoTime())); } - Assertions.assertEquals(100, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); - Assertions.assertEquals(50, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(projects)); + Assertions.assertEquals(100, repository.countInstances(todos)); + Assertions.assertEquals(50, repository.countInstances(projects)); - for(EntityInstance project : ThingifierRepositoryTestSupport.repository(todoManager).listInstances(projects)){ + for(EntityInstance project : repository.listInstances(projects)){ - project.getRelationships().connect("tasks", getRandomThingInstance(ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos))); + repository.connectRelationship( + project, + "tasks", + getRandomThingInstance(repository.listInstances(todos))); } - for(EntityInstance todo : ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos)){ + for(EntityInstance todo : repository.listInstances(todos)){ - todo.getRelationships().connect("task-of", getRandomThingInstance(ThingifierRepositoryTestSupport.repository(todoManager).listInstances(projects))); + repository.connectRelationship( + todo, + "task-of", + getRandomThingInstance(repository.listInstances(projects))); } System.out.println(todoManager.toString()); todoManager.clearAllData(); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(projects)); + Assertions.assertEquals(0, repository.countInstances(todos)); + Assertions.assertEquals(0, repository.countInstances(projects)); System.out.println(todoManager.toString()); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java index d5de321f..b833854e 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiNonHttpTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.casestudy.todomanager.api_non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -7,7 +8,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -37,8 +37,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); jsonThing = new JsonThing(todoManager.apiConfig().jsonOutput()); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -78,15 +78,13 @@ public void getCanReturnInstancesOfARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for relating"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for relating")); - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "My New Project " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "My New Project " + System.currentTimeMillis())); - myNewProject.getRelationships().connect("tasks", paperwork); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(myNewProject, "tasks", paperwork); - int numberOfTasks = myNewProject.getRelationships().getConnectedItems("tasks").size(); + int numberOfTasks = myNewProject.getRelatedItems("tasks").size(); Assertions.assertEquals(1, numberOfTasks); apiresponse = todoManager.api().get(String.format("project/%s/tasks", myNewProject.getPrimaryKeyValue()), new QueryFilterParams(), new HttpHeadersBlock()); @@ -96,7 +94,7 @@ public void getCanReturnInstancesOfARelationship(){ Assertions.assertTrue(apiresponse.isCollection()); Assertions.assertFalse(apiresponse.isErrorResponse()); - EntityInstance foundInstance = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()); + EntityInstance foundInstance = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()); Assertions.assertNotNull( foundInstance, "Task should exist, only the relationship should be deleted"); @@ -115,27 +113,25 @@ public void deleteCanDeleteARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for amending"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "My New Project " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "My New Project " + System.currentTimeMillis())); - myNewProject.getRelationships().connect("tasks", paperwork); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(myNewProject, "tasks", paperwork); // DELETE a Relationship // DELETE project/_GUID_/tasks/_GUID_ - int numberOfTasks = myNewProject.getRelationships().getConnectedItems("tasks").size(); + int numberOfTasks = myNewProject.getRelatedItems("tasks").size(); Assertions.assertEquals(1, numberOfTasks); apiresponse = todoManager.api().delete(String.format("project/%s/tasks/%s", myNewProject.getPrimaryKeyValue(), paperwork.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(200, apiresponse.getStatusCode()); - Assertions.assertEquals(numberOfTasks - 1, myNewProject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(numberOfTasks - 1, myNewProject.getRelatedItems("tasks").size()); Assertions.assertNotNull( - ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()), "Task should exist, only the relationship should be deleted"); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); @@ -154,18 +150,16 @@ public void deleteCanDeleteAThingInARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for amending"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "My New Project " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "My New Project " + System.currentTimeMillis())); - myNewProject.getRelationships().connect("tasks", paperwork); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(myNewProject, "tasks", paperwork); // DELETE the todo // DELETE todo/_guid_ - int numberOfTasks = myNewProject.getRelationships().getConnectedItems("tasks").size(); + int numberOfTasks = myNewProject.getRelatedItems("tasks").size(); Assertions.assertEquals(1, numberOfTasks); apiresponse = todoManager.api().delete(String.format("todo/%s", paperwork.getPrimaryKeyValue()), new HttpHeadersBlock()); @@ -175,12 +169,12 @@ public void deleteCanDeleteAThingInARelationship(){ Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); - Assertions.assertEquals(numberOfTasks - 1, myNewProject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(numberOfTasks - 1, myNewProject.getRelatedItems("tasks").size()); Assertions.assertNull( - ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()), + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByFieldNameAndValue(todo, "guid", paperwork.getPrimaryKeyValue()), "Task should not exist"); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); System.out.println(todoManager); @@ -196,11 +190,9 @@ public void postCanCreateARelationship(){ ApiResponse apiresponse; - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for amending"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "My New Project " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "My New Project " + System.currentTimeMillis())); // Create a relationship with POST @@ -211,7 +203,7 @@ public void postCanCreateARelationship(){ // Create a relationship with POST and just a GUID //myNewProject - int numberOfTasks = myNewProject.getRelationships().getConnectedItems("tasks").size(); + int numberOfTasks = myNewProject.getRelatedItems("tasks").size(); requestBody = new HashMap(); requestBody.put("guid", paperwork.getPrimaryKeyValue()); @@ -222,10 +214,10 @@ public void postCanCreateARelationship(){ Assertions.assertEquals(201, apiresponse.getStatusCode()); - Assertions.assertEquals(numberOfTasks + 1, myNewProject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(numberOfTasks + 1, myNewProject.getRelatedItems("tasks").size()); // todo should also be connected to project via the associated task-of relationship vector - Collection projects = paperwork.getRelationships().getConnectedItems("task-of"); + Collection projects = paperwork.getRelatedItems("task-of"); Assertions.assertEquals(1, projects.size()); List listOfProjects = new ArrayList(projects); @@ -245,11 +237,9 @@ public void postCanCreateARelationship(){ public void postCanCreateARelationshipUsingAPI() { - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "Project For Relationships"); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Project For Relationships")); - EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for relationship testing"); + EntityInstance relTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for relationship testing")); // Create a relationship with POST @@ -262,13 +252,13 @@ public void postCanCreateARelationshipUsingAPI() { HashMap requestBody = new HashMap(); requestBody.put("guid", relTodo.getPrimaryKeyValue()); - int numberOfTasks = myNewProject.getRelationships().getConnectedItems("tasks").size(); + int numberOfTasks = myNewProject.getRelatedItems("tasks").size(); ApiResponse apiresponse = todoManager.api().post(String.format("project/%s/tasks", myNewProject.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(201, apiresponse.getStatusCode()); - Assertions.assertEquals(numberOfTasks + 1, myNewProject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(numberOfTasks + 1, myNewProject.getRelatedItems("tasks").size()); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); Assertions.assertEquals( "", new ApiResponseAsXml(apiresponse, jsonThing).getXml().trim(),"Should have no body"); @@ -282,12 +272,10 @@ public void postCanCreateARelationshipUsingAPI() { public void postCanCreateARelationshipUsingReversalRelationshipAPI() { - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "Project For Relationships " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Project For Relationships " + System.currentTimeMillis())); - EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); + EntityInstance relTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for relationship testing " + System.currentTimeMillis())); // Create a relationship with POST // POST todo/_GUID_/task-of @@ -299,7 +287,7 @@ public void postCanCreateARelationshipUsingReversalRelationshipAPI() { HashMap requestBody = new HashMap(); requestBody.put("guid", myNewProject.getPrimaryKeyValue()); - int numberOfProjects = relTodo.getRelationships().getConnectedItems("task-of").size(); + int numberOfProjects = relTodo.getRelatedItems("task-of").size(); Assertions.assertEquals(0, numberOfProjects); // get current related projects through api @@ -322,7 +310,7 @@ public void postCanCreateARelationshipUsingReversalRelationshipAPI() { apiresponse = todoManager.api().post(String.format("todo/%s/task-of", relTodo.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(201, apiresponse.getStatusCode()); - Assertions.assertEquals(1, relTodo.getRelationships().getConnectedItems("task-of").size()); + Assertions.assertEquals(1, relTodo.getRelatedItems("task-of").size()); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); Assertions.assertEquals( "", new ApiResponseAsXml(apiresponse, jsonThing).getXml().trim(),"Should have no body"); Assertions.assertEquals( "", new ApiResponseAsJson(apiresponse, jsonThing).getJson().trim(),"Should have no body"); @@ -353,11 +341,9 @@ public void postCanCreateARelationshipUsingReversalRelationshipAPI() { public void deleteARelationshipUsingReversalRelationshipAPI() { - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "Project For Relationships " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Project For Relationships " + System.currentTimeMillis())); - EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); + EntityInstance relTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for relationship testing " + System.currentTimeMillis())); // Create a relationship with POST // POST todo/_GUID_/task-of @@ -369,14 +355,14 @@ public void deleteARelationshipUsingReversalRelationshipAPI() { HashMap requestBody = new HashMap(); requestBody.put("guid", myNewProject.getPrimaryKeyValue()); - int numberOfProjects = relTodo.getRelationships().getConnectedItems("task-of").size(); + int numberOfProjects = relTodo.getRelatedItems("task-of").size(); Assertions.assertEquals(0, numberOfProjects); // Create a relationship ApiResponse apiresponse = todoManager.api().post(String.format("todo/%s/task-of", relTodo.getPrimaryKeyValue()),getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(201, apiresponse.getStatusCode()); - Assertions.assertEquals(1, relTodo.getRelationships().getConnectedItems("task-of").size()); + Assertions.assertEquals(1, relTodo.getRelatedItems("task-of").size()); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); Assertions.assertFalse(apiresponse.hasABody()); @@ -389,11 +375,11 @@ public void deleteARelationshipUsingReversalRelationshipAPI() { Assertions.assertEquals(200, apiresponse.getStatusCode()); // project should be related to nothing - Collection items = myNewProject.getRelationships().getConnectedItems("tasks"); + Collection items = myNewProject.getRelatedItems("tasks"); Assertions.assertEquals(0, items.size()); // todo should be related to nothing - items = relTodo.getRelationships().getConnectedItems("task-of"); + items = relTodo.getRelatedItems("task-of"); Assertions.assertEquals(0, items.size()); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); @@ -411,11 +397,9 @@ public void deleteARelationshipUsingReversalRelationshipAPI() { public void deleteAThingInAReversalRelationship() { - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "Project For Relationships " + System.currentTimeMillis()); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Project For Relationships " + System.currentTimeMillis())); - EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); + EntityInstance relTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for relationship testing " + System.currentTimeMillis())); // Create a relationship with POST // POST todo/_GUID_/task-of @@ -427,14 +411,14 @@ public void deleteAThingInAReversalRelationship() { HashMap requestBody = new HashMap(); requestBody.put("guid", myNewProject.getPrimaryKeyValue()); - int numberOfProjects = relTodo.getRelationships().getConnectedItems("task-of").size(); + int numberOfProjects = relTodo.getRelatedItems("task-of").size(); Assertions.assertEquals(0, numberOfProjects); // Create relationship ApiResponse apiresponse = todoManager.api().post(String.format("todo/%s/task-of", relTodo.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(201, apiresponse.getStatusCode()); - Assertions.assertEquals(1, relTodo.getRelationships().getConnectedItems("task-of").size()); + Assertions.assertEquals(1, relTodo.getRelatedItems("task-of").size()); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); Assertions.assertFalse(apiresponse.hasABody()); Assertions.assertEquals( "", new ApiResponseAsXml(apiresponse, jsonThing).getXml().trim(), "Should have no body"); @@ -445,7 +429,7 @@ public void deleteAThingInAReversalRelationship() { apiresponse = todoManager.api().delete(String.format("todo/%s", relTodo.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(200, apiresponse.getStatusCode()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todo).size(),"Should be no stored todos"); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).listInstances(todo).size(),"Should be no stored todos"); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); Assertions.assertFalse(apiresponse.hasABody()); Assertions.assertEquals( "", new ApiResponseAsXml(apiresponse, jsonThing).getXml().trim(), "Should have no body"); @@ -454,11 +438,11 @@ public void deleteAThingInAReversalRelationship() { // project should be related to nothing - Collection items = myNewProject.getRelationships().getConnectedItems("tasks"); + Collection items = myNewProject.getRelatedItems("tasks"); Assertions.assertEquals(0, items.size()); // todo should be related to nothing - items = relTodo.getRelationships().getConnectedItems("task-of"); + items = relTodo.getRelatedItems("task-of"); Assertions.assertEquals(0, items.size()); @@ -471,8 +455,7 @@ public void deleteAThingInAReversalRelationship() { public void postCanCreateARelationshipAndTodoAtSameTimeUsingAPI() { - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "Project For Relationships"); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Project For Relationships")); // Createa a relationship and a thing with a POST and no GUID @@ -483,7 +466,7 @@ public void postCanCreateARelationshipAndTodoAtSameTimeUsingAPI() { requestBody.put("title", expectedTitle); - int numberOfTasks = myNewProject.getRelationships().getConnectedItems("tasks").size(); + int numberOfTasks = myNewProject.getRelatedItems("tasks").size(); ApiResponse apiresponse = todoManager.api().post(String.format("project/%s/tasks", myNewProject.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(201, apiresponse.getStatusCode()); @@ -495,7 +478,7 @@ public void postCanCreateARelationshipAndTodoAtSameTimeUsingAPI() { "Expected location header to contain the same GUID as the X- GUID header"); Assertions.assertEquals( - numberOfTasks + 1, myNewProject.getRelationships().getConnectedItems("tasks").size(), + numberOfTasks + 1, myNewProject.getRelatedItems("tasks").size(), "Expected number of tasks in project to increase by 1"); Assertions.assertTrue(apiresponse.hasABody()); @@ -507,11 +490,11 @@ public void postCanCreateARelationshipAndTodoAtSameTimeUsingAPI() { // check todo exists - EntityInstance myCreatedTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, locationGuid); + EntityInstance myCreatedTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, locationGuid); Assertions.assertEquals(expectedTitle, myCreatedTodo.getFieldValue("title").asString()); // check todo is also related to the project since relationship is two way - Collection items = myCreatedTodo.getRelationships().getConnectedItems("task-of"); + Collection items = myCreatedTodo.getRelatedItems("task-of"); Assertions.assertEquals(1, items.size(), "Expected task be connected to only 1 project"); @@ -528,8 +511,7 @@ public void postCanCreateAReverseRelationshipAndProjectAtSameTimeUsingAPI() { // Create a thing and relate through a reverse relationship e.g. POST todo/GUID/task-of - EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for relationship testing " + System.currentTimeMillis()); + EntityInstance relTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for relationship testing " + System.currentTimeMillis())); // Createa a relationship and a thing with a POST and no GUID @@ -541,30 +523,30 @@ public void postCanCreateAReverseRelationshipAndProjectAtSameTimeUsingAPI() { requestBody.put("title", expectedTitle); - int numberOfProjects = relTodo.getRelationships().getConnectedItems("task-of").size(); + int numberOfProjects = relTodo.getRelatedItems("task-of").size(); Assertions.assertEquals(0, numberOfProjects); // Create it ApiResponse apiresponse = todoManager.api().post(String.format("todo/%s/task-of", relTodo.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(201, apiresponse.getStatusCode()); Assertions.assertTrue(apiresponse.getErrorMessages().size()==0); - Assertions.assertEquals(1, relTodo.getRelationships().getConnectedItems("task-of").size()); + Assertions.assertEquals(1, relTodo.getRelatedItems("task-of").size()); Assertions.assertTrue(apiresponse.hasABody()); Assertions.assertNotEquals("Should have a body", "", new ApiResponseAsXml(apiresponse, jsonThing).getXml().trim()); Assertions.assertNotEquals("Should have a body", "", new ApiResponseAsJson(apiresponse, jsonThing).getJson().trim()); Assertions.assertFalse(apiresponse.isCollection()); - EntityInstance myNewProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(project, apiresponse.getHeaderValue(ApiResponse.PRIMARY_KEY_HEADER)); + EntityInstance myNewProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(project, apiresponse.getHeaderValue(ApiResponse.PRIMARY_KEY_HEADER)); Assertions.assertNotNull(myNewProject); // project should be related to task - Collection items = myNewProject.getRelationships().getConnectedItems("tasks"); + Collection items = myNewProject.getRelatedItems("tasks"); Assertions.assertEquals(1, items.size()); // a todo instance should be related to project - items = relTodo.getRelationships().getConnectedItems("task-of"); + items = relTodo.getRelatedItems("task-of"); Assertions.assertEquals(1, items.size()); @@ -586,11 +568,10 @@ public void postCanCreateAReverseRelationshipAndProjectAtSameTimeUsingAPI() { public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsingAPI() { - EntityInstance myTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "an estimated todo"); + EntityInstance myTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "an estimated todo")); - final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - int numberOfEstimates = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates); + final EntityDefinition estimates = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"); + int numberOfEstimates = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates); Assertions.assertEquals(0, numberOfEstimates ); @@ -614,7 +595,7 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin "Expected location header to contain the same GUID as the X- GUID header"); Assertions.assertEquals( - numberOfEstimates + 1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates), + numberOfEstimates + 1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates), "Expected number of estimates in project to increase by 1"); Assertions.assertTrue(apiresponse.hasABody()); @@ -626,17 +607,17 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin // check estimate exists - EntityInstance myCreatedItem = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(estimates, locationGuid); + EntityInstance myCreatedItem = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(estimates, locationGuid); Assertions.assertEquals(expectedDescription, myCreatedItem.getFieldValue("description").asString()); Assertions.assertEquals("3", myCreatedItem.getFieldValue("duration").asString()); // check estimate is related to the todo since relationship is two way - Collection items = myCreatedItem.getRelationships().getConnectedItems("estimate"); + Collection items = myCreatedItem.getRelatedItems("estimate"); Assertions.assertEquals( 1, items.size(), "Expected estimate to be connected to only 1 todo"); Assertions.assertTrue(items.contains(myTodo)); // check todo also recognises the relationship - items = myTodo.getRelationships().getConnectedItems("estimates"); + items = myTodo.getRelatedItems("estimates"); Assertions.assertEquals( 1, items.size(), "Expected todo to be connected to only 1 estimate"); Assertions.assertTrue(items.contains(myCreatedItem)); @@ -645,11 +626,10 @@ public void postCanCreateAMandatoryRelationshipFromEstimateAndTodoAtSameTimeUsin @Test public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { - EntityInstance myTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "an estimated todo"); + EntityInstance myTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "an estimated todo")); - final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - int numberOfEstimates = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates); + final EntityDefinition estimates = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"); + int numberOfEstimates = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates); Assertions.assertEquals(0, numberOfEstimates ); @@ -663,7 +643,7 @@ public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { Assertions.assertEquals(400, apiresponse.getStatusCode()); Assertions.assertEquals( - numberOfEstimates, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates), + numberOfEstimates, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates), "Expected number of estimates in project to not increase"); } @@ -671,17 +651,15 @@ public void postCanNotCreateEstimateWithoutAMandatoryRelationshipUsingAPI() { @Test public void postCanCreateEstimateAMandatoryRelationshipUsingAPI() { - EntityInstance myTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", - "an estimated todo"); + EntityInstance myTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "an estimated todo")); // todo has no estimates Assertions.assertEquals( - 0, myTodo.getRelationships().getConnectedItems("estimates").size()); + 0, myTodo.getRelatedItems("estimates").size()); // there are no estimates at all - final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - int numberOfEstimates = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates); + final EntityDefinition estimates = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"); + int numberOfEstimates = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates); Assertions.assertEquals(0, numberOfEstimates ); @@ -703,16 +681,16 @@ public void postCanCreateEstimateAMandatoryRelationshipUsingAPI() { Assertions.assertEquals(201, apiresponse.getStatusCode()); Assertions.assertEquals( - numberOfEstimates+1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates), + numberOfEstimates+1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates), "Expected number of estimates in project to increase"); // todo now has an estimate Assertions.assertEquals( - 1, myTodo.getRelationships().getConnectedItems("estimates").size()); + 1, myTodo.getRelatedItems("estimates").size()); // and it is the estimate we expected final ArrayList estimatesList = new ArrayList(); - estimatesList.addAll(myTodo.getRelationships().getConnectedItems("estimates")); + estimatesList.addAll(myTodo.getRelatedItems("estimates")); Assertions.assertEquals(expectedDescription, estimatesList.get(0).getFieldValue("description").asString()); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java index d4d98cad..ab914c95 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/RelationshipApiSqliteRepositoryTest.java @@ -14,6 +14,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; @@ -42,10 +43,8 @@ public void getRelationshipPathsUseRepositoryWithoutLoadingCompatibilitySnapshot ThingRepository repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance task = repository.addInstance(new EntityInstance(todo). - setValue("title", "SQLite relationship task")); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance task = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); repository.connectRelationship(projectInstance, "tasks", task); @@ -81,10 +80,8 @@ public void getUnsupportedRelationshipTraversalDoesNotUseCompatibilityFallback() EntityDefinition todo = todoManager.getDefinitionNamed("todo"); EntityDefinition project = todoManager.getDefinitionNamed("project"); - EntityInstance task = repository.addInstance(new EntityInstance(todo). - setValue("title", "SQLite relationship task")); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance task = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); repository.connectRelationship(projectInstance, "tasks", task); ApiResponse response = todoManager.api().get( @@ -104,10 +101,8 @@ public void postRelationshipPathConnectsExistingItemWithoutLoadingCompatibilityS EntityDefinition todo = todoManager.getDefinitionNamed("todo"); EntityDefinition project = todoManager.getDefinitionNamed("project"); - EntityInstance task = repository.addInstance(new EntityInstance(todo). - setValue("title", "SQLite relationship task")); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance task = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); Map body = new HashMap<>(); body.put("guid", task.getPrimaryKeyValue()); @@ -136,10 +131,8 @@ public void postReverseRelationshipPathConnectsExistingItemWithoutLoadingCompati EntityDefinition todo = todoManager.getDefinitionNamed("todo"); EntityDefinition project = todoManager.getDefinitionNamed("project"); - EntityInstance task = repository.addInstance(new EntityInstance(todo). - setValue("title", "SQLite relationship task")); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance task = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); Map body = new HashMap<>(); body.put("guid", projectInstance.getPrimaryKeyValue()); @@ -168,8 +161,7 @@ public void postRelationshipPathCreatesAndConnectsNewItemWithoutLoadingCompatibi EntityDefinition todo = todoManager.getDefinitionNamed("todo"); EntityDefinition project = todoManager.getDefinitionNamed("project"); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); Map body = new HashMap<>(); body.put("title", "created through relationship path"); @@ -198,10 +190,8 @@ public void deleteRelationshipPathRemovesOnlyRelationshipWithoutLoadingCompatibi EntityDefinition todo = todoManager.getDefinitionNamed("todo"); EntityDefinition project = todoManager.getDefinitionNamed("project"); - EntityInstance task = repository.addInstance(new EntityInstance(todo). - setValue("title", "SQLite relationship task")); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance task = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); repository.connectRelationship(projectInstance, "tasks", task); ApiResponse response = todoManager.api().delete( @@ -227,10 +217,8 @@ public void deleteMissingRelationshipPathReturns404WithoutLoadingCompatibilitySn EntityDefinition todo = todoManager.getDefinitionNamed("todo"); EntityDefinition project = todoManager.getDefinitionNamed("project"); - EntityInstance task = repository.addInstance(new EntityInstance(todo). - setValue("title", "SQLite relationship task")); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "SQLite relationship project")); + EntityInstance task = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "SQLite relationship task")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "SQLite relationship project")); ApiResponse response = todoManager.api().delete( String.format( diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java index dd6375e6..8a46038b 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbDeleteEntityInstanceApiNonHttpTest.java @@ -1,12 +1,12 @@ package uk.co.compendiumdev.casestudy.todomanager.api_non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -33,8 +33,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -53,10 +53,9 @@ public void createDefinitions() { public void deleteAnEntityInstanceAPI() { ApiResponse apiresponse; - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "An Existing Project"); + EntityInstance officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "An Existing Project")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); apiresponse = todoManager.api().delete(String.format("project/%s", officeWork.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(200, apiresponse.getStatusCode()); @@ -64,7 +63,7 @@ public void deleteAnEntityInstanceAPI() { Assertions.assertFalse(apiresponse.hasABody()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); apiresponse = todoManager.api().delete(String.format("project/%s", officeWork.getPrimaryKeyValue()), new HttpHeadersBlock()); Assertions.assertEquals(404, apiresponse.getStatusCode()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java index c16b55db..6563dd24 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbGetEntityInstanceApiNonHttpTest.java @@ -1,12 +1,12 @@ package uk.co.compendiumdev.casestudy.todomanager.api_non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -36,8 +36,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -56,14 +56,13 @@ public void getCanReturnASingleEntityInstance() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(false); // add some data - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); - EntityInstance findThis = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "My Title" + System.nanoTime()); + EntityInstance findThis = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); ApiResponse apiResponse = todoManager.api().get("/todo/" + findThis.getPrimaryKeyValue(), new QueryFilterParams(), new HttpHeadersBlock()); @@ -87,14 +86,13 @@ public void getCanReturnASingleEntityInstanceAsACollection() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(true); // add some data - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); - EntityInstance findThis = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "My Title" + System.nanoTime()); + EntityInstance findThis = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); ApiResponse apiResponse = todoManager.api().get("/todo/" + findThis.getPrimaryKeyValue(), new QueryFilterParams(), new HttpHeadersBlock()); @@ -118,10 +116,10 @@ public void getCanReturnMultipleEntityInstances() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(false); // add some data - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); ApiResponse apiResponse = todoManager.api().get("/todo", new QueryFilterParams(), new HttpHeadersBlock()); @@ -131,16 +129,16 @@ public void getCanReturnMultipleEntityInstances() { "Should be a collection"); Assertions.assertTrue(apiResponse.hasABody()); - Assertions.assertEquals(ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo), apiResponse.getReturnedInstanceCollection().size()); + Assertions.assertEquals(todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo), apiResponse.getReturnedInstanceCollection().size()); Set guidSet = new HashSet<>(); for (EntityInstance item : apiResponse.getReturnedInstanceCollection()) { guidSet.add(item.getPrimaryKeyValue()); - Assertions.assertNotNull(ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, item.getPrimaryKeyValue())); + Assertions.assertNotNull(todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, item.getPrimaryKeyValue())); } - Assertions.assertEquals(guidSet.size(), ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(guidSet.size(), todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); Assertions.assertEquals(guidSet.size(), apiResponse.getReturnedInstanceCollection().size()); Assertions.assertEquals(0, apiResponse.getErrorMessages().size()); @@ -153,10 +151,10 @@ public void getCanReturnMultipleEntityInstancesFromEndpoint() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(true); // add some data - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "My Title" + System.nanoTime()); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "My Title" + System.nanoTime())); ApiResponse apiResponse = todoManager.api().get("/todo", new QueryFilterParams(), new HttpHeadersBlock()); @@ -166,16 +164,16 @@ public void getCanReturnMultipleEntityInstancesFromEndpoint() { "Should be a collection"); Assertions.assertTrue(apiResponse.hasABody()); - Assertions.assertEquals(ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo), apiResponse.getReturnedInstanceCollection().size()); + Assertions.assertEquals(todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo), apiResponse.getReturnedInstanceCollection().size()); Set guidSet = new HashSet<>(); for (EntityInstance item : apiResponse.getReturnedInstanceCollection()) { guidSet.add(item.getPrimaryKeyValue()); - Assertions.assertNotNull(ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, item.getPrimaryKeyValue())); + Assertions.assertNotNull(todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, item.getPrimaryKeyValue())); } - Assertions.assertEquals(guidSet.size(), ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(guidSet.size(), todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); Assertions.assertEquals(guidSet.size(), apiResponse.getReturnedInstanceCollection().size()); Assertions.assertEquals(0, apiResponse.getErrorMessages().size()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java index e555500b..1b392ab2 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPostEntityInstanceApiNonHttpTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.casestudy.todomanager.api_non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -7,7 +8,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -36,8 +36,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -89,7 +89,7 @@ public void postCanCreateAnEntityWhichPassesValidationWithAllFields() { // check that it is created in the model - EntityInstance createdProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, headerGUID); + EntityInstance createdProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, headerGUID); Assertions.assertEquals(createdProject, createdInstance); @@ -135,7 +135,7 @@ public void postCanCreateAnEntityWhichPassesValidationWithMinimumFields() { // check that it is created in the model - EntityInstance createdProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, headerGUID); + EntityInstance createdProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, headerGUID); Assertions.assertEquals(createdProject, createdInstance); @@ -145,8 +145,7 @@ public void postCanCreateAnEntityWhichPassesValidationWithMinimumFields() { @Test public void postCanAmendAnExistingEntity() { - EntityInstance relTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for amending"); + EntityInstance relTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); // POST project @@ -180,7 +179,7 @@ public void postCanAmendAnExistingEntity() { @Test public void postFailCannotCreateProjectWithGuidInUrl() { - int currentProjects = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project); + int currentProjects = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project); Map requestBody = new HashMap<>(); requestBody.put("title", "My Office Work"); @@ -193,7 +192,7 @@ public void postFailCannotCreateProjectWithGuidInUrl() { Assertions.assertTrue(apiresponse.hasABody()); - Assertions.assertEquals(currentProjects, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(currentProjects, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); } @@ -206,8 +205,7 @@ public void postFailCannotAmendEntityInstanceWhenValidationErrorsAPI() { String originalTitle = "Todo for amending " + System.currentTimeMillis(); String originalDescription = "my description " + System.currentTimeMillis(); - EntityInstance amendTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", originalTitle).setValue("description", originalDescription); + EntityInstance amendTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", originalTitle).withField("description", originalDescription)); // Mandatory field validation @@ -254,8 +252,7 @@ public void putCanAmendExistingProject() { requestBody = new HashMap<>(); requestBody.put("title", "My Office Work"); - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "An Existing Project"); + EntityInstance officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "An Existing Project")); String officeWorkGuid = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(officeWorkGuid); @@ -265,7 +262,7 @@ public void putCanAmendExistingProject() { Assertions.assertEquals(200, apiresponse.getStatusCode()); Assertions.assertEquals("My Office Work", officeWork.getFieldValue("title").asString()); - officeWork.setValue("title", "office"); + officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).patchInstance(officeWork, EntityInstanceDraft.forEntity(officeWork.getEntity()).withField("title", "office")); Assertions.assertEquals("office", officeWork.getFieldValue("title").asString()); Assertions.assertNotNull(officeWorkGuid); @@ -289,6 +286,7 @@ public void postFailCannotCreateEntityInstanceWhenValidationErrorsAPI() { apiresponse = todoManager.api().post(String.format("todo"), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(400, apiresponse.getStatusCode()); Assertions.assertFalse(apiresponse.getErrorMessages().isEmpty()); + Assertions.assertEquals("title : field is mandatory", apiresponse.getErrorMessages().iterator().next()); Assertions.assertTrue(apiresponse.hasABody()); // Field validation on boolean for Create with POST @@ -306,8 +304,7 @@ public void postFailCannotCreateEntityInstanceWhenValidationErrorsAPI() { requestBody.put("title", "A new TODO Item"); requestBody.put("doneStatus", "FALSEY"); - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)). - setValue("title", "Todo for amending"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); apiresponse = todoManager.api().post(String.format("todo/%s", paperwork.getPrimaryKeyValue()), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(400, apiresponse.getStatusCode()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java index b80728af..7b4e3e23 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/api_non_http/VerbPutEntityInstanceApiNonHttpTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.casestudy.todomanager.api_non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -7,7 +8,6 @@ import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; @@ -38,8 +38,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -70,8 +70,7 @@ public void putCanAmendExistingProject() { requestBody = new HashMap(); requestBody.put("title", "My Office Work"); - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "An Existing Project"); + EntityInstance officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "An Existing Project")); String officeWorkGuid = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(officeWorkGuid); @@ -81,7 +80,7 @@ public void putCanAmendExistingProject() { Assertions.assertEquals(200, apiresponse.getStatusCode()); Assertions.assertEquals("My Office Work", officeWork.getFieldValue("title").asString()); - officeWork.setValue("title", "office"); + officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).patchInstance(officeWork, EntityInstanceDraft.forEntity(officeWork.getEntity()).withField("title", "office")); Assertions.assertEquals("office", officeWork.getFieldValue("title").asString()); Assertions.assertNotNull(officeWorkGuid); @@ -101,9 +100,7 @@ public void putCanAmendExistingProjectByUsingDefaultFieldValues() { // PUT - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "An Existing Project"). - setValue("description", "my original description"); + EntityInstance officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "An Existing Project").withField("description", "my original description")); String officeWorkGuid = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(officeWorkGuid); @@ -136,9 +133,7 @@ public void putCanNotAmendGUID() { // PUT - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)). - setValue("title", "An Existing Project"). - setValue("description", "my original description"); + EntityInstance officeWork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "An Existing Project").withField("description", "my original description")); String originalGUID = officeWork.getPrimaryKeyValue(); Assertions.assertNotNull(originalGUID); @@ -177,7 +172,7 @@ public void putCanNotAmendGUID() { // requestBody.put("title", title); // // -// int currentProjects = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project); +// int currentProjects = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project); // Assertions.assertEquals(0, currentProjects); // // // create with a PUT and a given GUID @@ -191,10 +186,10 @@ public void putCanNotAmendGUID() { // Assertions.assertEquals(guid, apiresponse.getHeaderValue(ApiResponse.PRIMARY_KEY_HEADER)); // Assertions.assertTrue(apiresponse.getHeaderValue("Location").endsWith(guid)); // -// Assertions.assertEquals(currentProjects + 1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); +// Assertions.assertEquals(currentProjects + 1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); // // -// EntityInstance newProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(project, "guid", guid); +// EntityInstance newProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByFieldNameAndValue(project, "guid", guid); // // Assertions.assertEquals(title, newProject.getFieldValue("title").asString()); // Assertions.assertEquals(guid, newProject.getFieldValue("guid").asString()); @@ -212,7 +207,7 @@ public void putCanNotAmendGUID() { // Map requestBody; // ApiResponse apiresponse; // -// project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); +// project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); // final Field anIdField = Field.is("id", FieldType.AUTO_INCREMENT); // project.addField(anIdField); // @@ -223,7 +218,7 @@ public void putCanNotAmendGUID() { // requestBody.put("id", "12"); // // -// int currentProjects = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project); +// int currentProjects = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project); // Assertions.assertEquals(0, currentProjects); // // // create with a PUT and a given GUID @@ -234,7 +229,7 @@ public void putCanNotAmendGUID() { // Assertions.assertEquals(201, apiresponse.getStatusCode()); // // -// EntityInstance newProject = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(project, "guid", guid); +// EntityInstance newProject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByFieldNameAndValue(project, "guid", guid); // Assertions.assertEquals("12", newProject.getFieldValue("id").asString()); // // } @@ -246,11 +241,12 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { Map requestBody; ApiResponse apiresponse; - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); final Field anIdField = Field.is("id", FieldType.AUTO_INCREMENT); project.addField(anIdField); - final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)); + final EntityInstance instance = + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Existing project")); // Want to PUT requestBody = new HashMap(); @@ -259,7 +255,7 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { // duplicate id requestBody.put("id", String.valueOf(instance.getFieldValue("id").asString())); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); // create with a PUT and a given GUID String guid = UUID.randomUUID().toString(); @@ -267,7 +263,7 @@ public void putCanNotCreateAnEntityInstanceWithADuplicateId() { apiresponse = todoManager.api().put(String.format("project/%s", guid), getSimpleParser(requestBody), new HttpHeadersBlock()); Assertions.assertEquals(400, apiresponse.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); } @@ -288,7 +284,7 @@ public void putFailValidationEntityInstanceAPI() { Assertions.assertTrue(apiresponse.hasABody()); - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "Todo for amending"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); // Mandatory field validation PUT amend requestBody = new HashMap(); @@ -338,7 +334,7 @@ public void putFailCannotAmendEntityInstanceWhenValidationErrors() { ApiResponse apiresponse; - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "Todo for amending"); + EntityInstance paperwork = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Todo for amending")); // Mandatory field validation PUT amend requestBody = new HashMap(); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java index f299d2eb..9a4a65a1 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/DeleteRequestTest.java @@ -1,12 +1,12 @@ package uk.co.compendiumdev.casestudy.todomanager.http_api; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -29,8 +29,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -39,9 +39,9 @@ public void createDefinitions() { public void canDeleteItem(){ - final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance instance = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("/todos/" + instance.getPrimaryKeyValue()); @@ -49,7 +49,7 @@ public void canDeleteItem(){ Assertions.assertEquals(200, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); } @@ -57,9 +57,9 @@ public void canDeleteItem(){ public void cannotDeleteItemThatDoesNotExist(){ - final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance instance = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("/todos/" + instance.getPrimaryKeyValue()+"bob"); @@ -67,7 +67,7 @@ public void cannotDeleteItemThatDoesNotExist(){ Assertions.assertEquals(404, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); } @@ -75,9 +75,9 @@ public void cannotDeleteItemThatDoesNotExist(){ public void cannotDeleteRootItem(){ - final EntityInstance instance = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance instance = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("/todos"); @@ -85,7 +85,7 @@ public void cannotDeleteRootItem(){ Assertions.assertEquals(405, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); final ErrorMessages errors = new Gson().fromJson(response.getBody(), ErrorMessages.class); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java index f156c150..2f26e8c1 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/JsonRequestResponseTest.java @@ -1,12 +1,12 @@ package uk.co.compendiumdev.casestudy.todomanager.http_api; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -33,8 +33,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); todoManager.apiConfig().setApiToEnforceAcceptHeaderForResponses(false); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -57,7 +57,7 @@ public void canGetAnEmptyJsonItemsCollection() { public void canGetJsonItems() { - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); HttpApiRequest request = new HttpApiRequest("/todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -78,7 +78,7 @@ public void canGetJsonItems() { public void canGetJsonItemAsACollection() { todoManager.apiConfig().setReturnSingleGetItemsAsCollection(true); - final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance aTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); HttpApiRequest request = new HttpApiRequest("/todos/" + aTodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -99,8 +99,8 @@ public void canGetJsonItemAsACollection() { public void canGetMultipleJsonItems() { - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my other title"); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my other title")); HttpApiRequest request = new HttpApiRequest("todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -112,8 +112,8 @@ public void canGetMultipleJsonItems() { final TodoCollectionResponse todos = new Gson().fromJson(response.getBody(), TodoCollectionResponse.class); Assertions.assertEquals(2, todos.todos.length); - Assertions.assertEquals(todos.todos[0].title, ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, todos.todos[0].guid).getFieldValue("title").asString()); - Assertions.assertEquals(todos.todos[1].title, ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, todos.todos[1].guid).getFieldValue("title").asString()); + Assertions.assertEquals(todos.todos[0].title, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, todos.todos[0].guid).getFieldValue("title").asString()); + Assertions.assertEquals(todos.todos[1].title, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, todos.todos[1].guid).getFieldValue("title").asString()); } @@ -121,8 +121,8 @@ public void canGetMultipleJsonItems() { public void cannotGetFromMissingEndpoint() { - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my other title"); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my other title")); HttpApiRequest request = new HttpApiRequest("todos" + System.nanoTime()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -159,19 +159,19 @@ public void canPostAndCreateAnItemWithJson() { //{"title":"title from json"} request.setBody("{\"title\":\"title from json\"}"); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); + final EntityInstance aTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("title from json", aTodo.getFieldValue("title").asString()); @@ -199,7 +199,7 @@ public void canPostAndCreateAnItemWithJsonAndReceiveXml() { //{"title":"title from json"} request.setBody("{\"title\":\"title from json\"}"); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); @@ -208,12 +208,12 @@ public void canPostAndCreateAnItemWithJsonAndReceiveXml() { Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); + final EntityInstance aTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("false", aTodo.getFieldValue("doneStatus").asString()); Assertions.assertEquals("title from json", aTodo.getFieldValue("title").asString()); @@ -236,9 +236,9 @@ public void canPostAndCreateAnItemWithJsonAndReceiveXml() { @Test public void canPostToAmendAnItemWithJson() { - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -255,7 +255,7 @@ public void canPostToAmendAnItemWithJson() { System.out.println(response.getBody()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); Assertions.assertEquals("title from json", atodo.getFieldValue("title").asString()); @@ -272,9 +272,9 @@ public void canPostToAmendAnItemWithJson() { @Test public void canPutToAmendAnItemWithJson() { - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -292,7 +292,7 @@ public void canPutToAmendAnItemWithJson() { Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); Assertions.assertEquals("title from json", atodo.getFieldValue("title").asString()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java index 574e0fa4..edcfb992 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/RelationshipHttpTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.casestudy.todomanager.http_api; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; @@ -7,7 +8,6 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -30,9 +30,9 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); - categories = ThingifierRepositoryTestSupport.entity(todoManager, "category"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); + categories = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("category"); } @@ -40,11 +40,11 @@ public void createDefinitions() { @Test public void canCreateARelationshipBetweenProjectAndTodoViaTasks(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -57,18 +57,18 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasks(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(1, aproject.getRelatedItems("tasks").size()); } @Test public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingID(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -81,7 +81,7 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingID(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(1, aproject.getRelatedItems("tasks").size()); } @@ -91,10 +91,10 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingID(){ @Test public void canCreateARelationshipAndTodoBetweenProjectAndTodoViaTasks(){ - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); + Assertions.assertEquals(0,todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -108,10 +108,10 @@ public void canCreateARelationshipAndTodoBetweenProjectAndTodoViaTasks(){ Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(1,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, aproject.getRelatedItems("tasks").size()); + Assertions.assertEquals(1,todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); - final EntityInstance inMemoryTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); + final EntityInstance inMemoryTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); Assertions.assertTrue(response.getBody().contains(inMemoryTodo.getPrimaryKeyValue()), response.getBody()); @@ -121,11 +121,11 @@ public void canCreateARelationshipAndTodoBetweenProjectAndTodoViaTasks(){ public void cannotCreateARelationshipBetweenProjectAndCategoryViaTasks(){ - final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); + final EntityInstance acategory = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(categories).withField("title", "a Category")); - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -138,7 +138,7 @@ public void cannotCreateARelationshipBetweenProjectAndCategoryViaTasks(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(404, response.getStatusCode()); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); final ErrorMessages errors = new Gson().fromJson(response.getBody(), ErrorMessages.class); @@ -151,11 +151,11 @@ public void cannotCreateARelationshipBetweenProjectAndCategoryViaTasks(){ public void cannotCreateARelationshipWhenGivenGuidDoesNotExist(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -180,11 +180,11 @@ public void cannotCreateARelationshipWhenGivenGuidDoesNotExist(){ public void canCreateARelationshipBetweenCategoryAndTodoViaTodos(){ - final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); + final EntityInstance acategory = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(categories).withField("title", "a Category")); - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); + Assertions.assertEquals(0, acategory.getRelatedItems("todos").size()); HttpApiRequest request = new HttpApiRequest("categories/" + acategory.getPrimaryKeyValue() + "/todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -197,17 +197,17 @@ public void canCreateARelationshipBetweenCategoryAndTodoViaTodos(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, acategory.getRelationships().getConnectedItems("todos").size()); + Assertions.assertEquals(1, acategory.getRelatedItems("todos").size()); } @Test public void canCreateARelationshipAndTodoBetweenCategoryAndTodoViaTodos(){ - final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); + final EntityInstance acategory = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(categories).withField("title", "a Category")); - Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, acategory.getRelatedItems("todos").size()); + Assertions.assertEquals(0,todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("categories/" + acategory.getPrimaryKeyValue() + "/todos"); request.getHeaders().putAll(HeadersSupport.acceptJson()); @@ -222,10 +222,10 @@ public void canCreateARelationshipAndTodoBetweenCategoryAndTodoViaTodos(){ Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1,ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, acategory.getRelatedItems("todos").size()); + Assertions.assertEquals(1,todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); - final EntityInstance inMemoryTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); + final EntityInstance inMemoryTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER)); Assertions.assertTrue(response.getBody().contains(inMemoryTodo.getPrimaryKeyValue()), response.getBody()); @@ -234,11 +234,11 @@ public void canCreateARelationshipAndTodoBetweenCategoryAndTodoViaTodos(){ @Test public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingXml(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks"); request.getHeaders().putAll(HeadersSupport.containsXml()); @@ -249,22 +249,22 @@ public void canCreateARelationshipBetweenProjectAndTodoViaTasksUsingXml(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); + Assertions.assertEquals(1, aproject.getRelatedItems("tasks").size()); } @Test public void canDeleteARelationshipBetweenProjectAndTodoViaTasks(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - final EntityInstance aproject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "a Project"); + final EntityInstance aproject = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(project).withField("title", "a Project")); - aproject.getRelationships().connect("tasks", atodo); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(aproject, "tasks", atodo); - Assertions.assertEquals(1, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(1, aproject.getRelatedItems("tasks").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); HttpApiRequest request = new HttpApiRequest("projects/" + aproject.getPrimaryKeyValue() + "/tasks/" + atodo.getPrimaryKeyValue()); @@ -272,9 +272,9 @@ public void canDeleteARelationshipBetweenProjectAndTodoViaTasks(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).delete(request); Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(0, aproject.getRelationships().getConnectedItems("tasks").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(project)); + Assertions.assertEquals(0, aproject.getRelatedItems("tasks").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(project)); } @@ -283,32 +283,32 @@ public void canDeleteARelationshipBetweenProjectAndTodoViaTasks(){ public void canDeleteARelationshipBetweenCategoryAndTodoViaTodos(){ - final EntityInstance acategory = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(categories)).setValue("title", "a Category"); + final EntityInstance acategory = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(categories).withField("title", "a Category")); - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO")); - acategory.getRelationships().connect("todos", atodo); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(acategory, "todos", atodo); - Assertions.assertEquals(1, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(categories)); + Assertions.assertEquals(1, acategory.getRelatedItems("todos").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(categories)); final HttpApiRequest request = new HttpApiRequest("categories/" + acategory.getPrimaryKeyValue() + "/todos/" + atodo.getPrimaryKeyValue()); HttpApiResponse response = new ThingifierHttpApi(todoManager).delete(request); Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(categories)); + Assertions.assertEquals(0, acategory.getRelatedItems("todos").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(categories)); // if relationship doesn't exist, I should get a 404 if I reissue therequest response = new ThingifierHttpApi(todoManager).delete(request); Assertions.assertEquals(404, response.getStatusCode()); - Assertions.assertEquals(0, acategory.getRelationships().getConnectedItems("todos").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(categories)); + Assertions.assertEquals(0, acategory.getRelatedItems("todos").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(categories)); } /** @@ -339,14 +339,14 @@ public void canNotCreateEstimateWithoutMandatoryRelationship(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(400, response.getStatusCode()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances( - ThingifierRepositoryTestSupport.entity(todoManager, "estimate"))); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( + todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"))); } @Test public void canCreateAnEstimateForTodoMandatoryRelationship(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO for estimating"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO for estimating")); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue() + "/estimates" ); @@ -359,9 +359,9 @@ public void canCreateAnEstimateForTodoMandatoryRelationship(){ final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances( - ThingifierRepositoryTestSupport.entity(todoManager, "estimate"))); - Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( + todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"))); + Assertions.assertEquals(1, atodo.getRelatedItems("estimates").size()); } @@ -370,16 +370,16 @@ public void canCreateAnEstimateForTodoMandatoryRelationship(){ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO for estimating"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO for estimating")); - final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - final EntityInstance anEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)).setValue("duration", "7"); + final EntityDefinition estimates = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"); + final EntityInstance anEstimate = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(estimates).withField("duration", "7")); - anEstimate.getRelationships().connect("estimate", atodo); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(anEstimate, "estimate", atodo); - Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, atodo.getRelatedItems("estimates").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); final HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); @@ -388,8 +388,8 @@ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates)); } @@ -397,16 +397,16 @@ public void canDeleteAnEstimateWhenTodoDeletedBecauseOfMandatoryRelationship(){ public void canGetEstimatesViaRelationship(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "a TODO for estimating"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "a TODO for estimating")); - final EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); - final EntityInstance anEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)).setValue("duration", "7").setValue("description", "an estimate"); + final EntityDefinition estimates = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("estimate"); + final EntityInstance anEstimate = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(estimates).withField("duration", "7").withField("description", "an estimate")); - anEstimate.getRelationships().connect("estimate", atodo); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).connectRelationship(anEstimate, "estimate", atodo); - Assertions.assertEquals(1, atodo.getRelationships().getConnectedItems("estimates").size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(estimates)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, atodo.getRelatedItems("estimates").size()); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(estimates)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue() + "/estimates"); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java index 32505ab5..3060425c 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/http_api/XmlRequestResponseTest.java @@ -1,11 +1,11 @@ package uk.co.compendiumdev.casestudy.todomanager.http_api; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -31,8 +31,8 @@ public void createDefinitions() { todoManager = TodoManagerModel.definedAsThingifier(); - todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + todo = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); + project = todoManager.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("project"); } @@ -57,7 +57,7 @@ public void canGetAnEmptyXmlItemsCollection(){ public void canGetXmlItemsWhenAskedForXml(){ - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); HttpApiRequest request = new HttpApiRequest("todos"); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -73,7 +73,7 @@ public void canGetXmlItemsWhenAskedForXml(){ public void canGetXmlErrorMessagesWhenAskedForXml(){ - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); HttpApiRequest request = new HttpApiRequest("todosyoohoo"); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -108,19 +108,19 @@ public void canPostAndCreateAnItemWithXml(){ request.setBody("test title"); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); Assertions.assertEquals(201, response.getStatusCode()); System.out.println(response.getBody()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); + final EntityInstance aTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("test title", aTodo.getFieldValue("title").asString()); @@ -132,9 +132,9 @@ public void canPostAndCreateAnItemWithXml(){ @Test public void canPostAndAmendAnItemWithXml(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/" + atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -150,7 +150,7 @@ public void canPostAndAmendAnItemWithXml(){ System.out.println(response.getBody()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); Assertions.assertEquals("test title", atodo.getFieldValue("title").asString()); @@ -173,7 +173,7 @@ public void canPutAndCreateAnItemWithXmlAndReceiveJson(){ request.setBody("test title"); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(0, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); final HttpApiResponse response = new ThingifierHttpApi(todoManager).post(request); @@ -182,12 +182,12 @@ public void canPutAndCreateAnItemWithXmlAndReceiveJson(){ Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); // header should give me the guid String guid = response.getHeaders().get(ApiResponse.PRIMARY_KEY_HEADER); - final EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todo, guid); + final EntityInstance aTodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).findInstanceByPrimaryKey(todo, guid); Assertions.assertEquals("test title", aTodo.getFieldValue("title").asString()); @@ -209,9 +209,9 @@ public void canPutAndCreateAnItemWithXmlAndReceiveJson(){ @Test public void canPutToAmendAnItemWithJson(){ - final EntityInstance atodo = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "my title"); + final EntityInstance atodo = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "my title")); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); HttpApiRequest request = new HttpApiRequest("todos/"+atodo.getPrimaryKeyValue()); request.getHeaders().putAll(HeadersSupport.acceptXml()); @@ -227,7 +227,7 @@ public void canPutToAmendAnItemWithJson(){ Assertions.assertEquals(200, response.getStatusCode()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todo)); + Assertions.assertEquals(1, todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(todo)); Assertions.assertEquals("test title", atodo.getFieldValue("title").asString()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java index 50b71e69..53e0c170 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/BasicTodoManagerTest.java @@ -5,20 +5,23 @@ import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.Collection; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; public class BasicTodoManagerTest { private Thingifier todoManager; + private ThingRepository repository; @BeforeEach public void createDefinitions(){ todoManager = TodoManagerModel.definedAsThingifier(); + repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); } @@ -26,7 +29,8 @@ public void createDefinitions(){ public void todoModelDefinitionCheck(){ - EntityDefinition todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todo = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); Assertions.assertTrue(todo.hasFieldNameDefined("title")); Assertions.assertTrue(todo.hasFieldNameDefined("description")); @@ -43,31 +47,36 @@ public void todoModelDefinitionCheck(){ public void relationshipDefinitionCheck(){ - EntityDefinition todo = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); - EntityDefinition project = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + EntityDefinition todo = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); + EntityDefinition project = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("project"); - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "scan paperwork"); - EntityInstance filework = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todo)).setValue("title", "file paperwork"); + EntityInstance paperwork = repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", "scan paperwork")); + EntityInstance filework = repository.createInstance( + EntityInstanceDraft.forEntity(todo).withField("title", "file paperwork")); - EntityInstance officeWork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(project)).setValue("title", "Office Work"); + EntityInstance officeWork = repository.createInstance( + EntityInstanceDraft.forEntity(project).withField("title", "Office Work")); - officeWork.getRelationships().connect("tasks", paperwork); - officeWork.getRelationships().connect("tasks", filework); + repository.connectRelationship(officeWork, "tasks", paperwork); + repository.connectRelationship(officeWork, "tasks", filework); - Collection relatedItems = officeWork.getRelationships().getConnectedItems("tasks"); + Collection relatedItems = officeWork.getRelatedItems("tasks"); Assertions.assertTrue(relatedItems.contains(paperwork)); Assertions.assertTrue(relatedItems.contains(filework)); - relatedItems = officeWork.getRelationships().getConnectedItems("tasks"); + relatedItems = officeWork.getRelatedItems("tasks"); Assertions.assertTrue(relatedItems.contains(paperwork)); Assertions.assertTrue(relatedItems.contains(filework)); todoManager.deleteThing(paperwork, EntityRelModel.DEFAULT_DATABASE_NAME); - relatedItems = officeWork.getRelationships().getConnectedItems("tasks"); + relatedItems = officeWork.getRelatedItems("tasks"); Assertions.assertFalse(relatedItems.contains(paperwork)); Assertions.assertTrue(relatedItems.contains(filework)); } @@ -76,21 +85,26 @@ public void relationshipDefinitionCheck(){ @Test public void createAndAmendSomeTodos(){ - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "Tidy up my room"). - setValue("description", "I need to tidy up my room because it is a mess"); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Tidy up my room"). + withField("description", "I need to tidy up my room because it is a mess")); - EntityInstance paperwork = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title","Do Paperwork"). - setValue("description", "Scan everything in, upload to document management system and file paperwork"); + EntityInstance paperwork = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Do Paperwork"). + withField("description", "Scan everything in, upload to document management system and file paperwork")); Assertions.assertEquals("false", paperwork.getFieldValue("doneStatus").asString()); System.out.println(todoManager.toString()); - tidy.setValue("doneStatus", "true"); + tidy = repository.patchInstance( + tidy, + EntityInstanceDraft.forEntity(todos).withField("doneStatus", "true")); Assertions.assertEquals("true", tidy.getFieldValue("doneStatus").asString()); System.out.println(todoManager.toString()); @@ -99,23 +113,25 @@ public void createAndAmendSomeTodos(){ @Test public void createAndDeleteTodos(){ - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - int originalTodosCount = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos); + int originalTodosCount = repository.countInstances(todos); - EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title","Delete this todo"). - setValue("description", "I need to be deleted"); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Delete this todo"). + withField("description", "I need to be deleted")); - EntityInstance foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todos, tidy.getPrimaryKeyValue()); + EntityInstance foundit = repository.findInstanceByPrimaryKey(todos, tidy.getPrimaryKeyValue()); Assertions.assertEquals("Delete this todo", foundit.getFieldValue("title").asString()); todoManager.deleteThing(foundit, EntityRelModel.DEFAULT_DATABASE_NAME); - Assertions.assertEquals(originalTodosCount, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); + Assertions.assertEquals(originalTodosCount, repository.countInstances(todos)); - foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByPrimaryKey(todos, tidy.getPrimaryKeyValue()); + foundit = repository.findInstanceByPrimaryKey(todos, tidy.getPrimaryKeyValue()); Assertions.assertNull(foundit); @@ -132,30 +148,29 @@ public void createAndDeleteTodos(){ @Test public void createAmendAndDeleteATodoWithAGivenGUID(){ - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - int originalTodosCount = ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos); + int originalTodosCount = repository.countInstances(todos); String guid="6fd86e2d-7c52-4dea-85bb-34760ef66d9d"; - EntityInstance tidy = new EntityInstance(todos); - tidy.overrideValue("guid", guid); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withProtectedField("guid", guid). + withField("title", "Delete this todo"). + withField("description", "I need to be deleted")); - tidy.setValue("title", "Delete this todo"). - setValue("description", "I need to be deleted"); - - ThingifierRepositoryTestSupport.repository(todoManager).addInstance(tidy); - - EntityInstance foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todos, "guid", guid); + EntityInstance foundit = repository.findInstanceByFieldNameAndValue(todos, "guid", guid); Assertions.assertEquals("Delete this todo", foundit.getFieldValue("title").asString()); todoManager.deleteThing(foundit, EntityRelModel.DEFAULT_DATABASE_NAME); - Assertions.assertEquals(originalTodosCount, ThingifierRepositoryTestSupport.repository(todoManager).countInstances(todos)); + Assertions.assertEquals(originalTodosCount, repository.countInstances(todos)); - foundit = ThingifierRepositoryTestSupport.repository(todoManager).findInstanceByFieldNameAndValue(todos, "guid", guid); + foundit = repository.findInstanceByFieldNameAndValue(todos, "guid", guid); Assertions.assertNull(foundit); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java index 0793fd59..5d4c1050 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/casestudy/todomanager/unit/OptionalityRelationshipTest.java @@ -1,14 +1,14 @@ package uk.co.compendiumdev.casestudy.todomanager.unit; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.casestudy.todomanager.TodoManagerModel; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; +import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; import java.util.ArrayList; import java.util.Collection; @@ -21,11 +21,13 @@ public class OptionalityRelationshipTest { // we need to enforce this during creation, updates and deletion private Thingifier todoManager; + private ThingRepository repository; @BeforeEach public void createDefinitions(){ todoManager = TodoManagerModel.definedAsThingifier(); + repository = todoManager.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); } @@ -34,18 +36,22 @@ public void createDefinitions(){ public void byDefaultRelationshipsAreOptional(){ - EntityDefinition projects = ThingifierRepositoryTestSupport.entity(todoManager, "project"); + EntityDefinition projects = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("project"); - EntityInstance aProject = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(projects)).setValue("title", "myproject"); + EntityInstance aProject = repository.createInstance( + EntityInstanceDraft.forEntity(projects).withField("title", "myproject")); Assertions.assertTrue(aProject.validate().isValid()); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "Tidy up my room"). - setValue("description", "I need to tidy up my room because it is a mess"); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Tidy up my room"). + withField("description", "I need to tidy up my room because it is a mess")); Assertions.assertTrue(tidy.validateFieldValues(new ArrayList<>(), true).isValid()); @@ -55,16 +61,19 @@ public void byDefaultRelationshipsAreOptional(){ @Test public void anEstimateWithoutATodoIsInvalid(){ - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "Tidy up my room"). - setValue("description", "I need to tidy up my room because it is a mess"); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Tidy up my room"). + withField("description", "I need to tidy up my room because it is a mess")); - EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + EntityDefinition estimates = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("estimate"); - EntityInstance tidyRoomEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)). - setValue("duration", "1"); + EntityInstance tidyRoomEstimate = repository.createInstance( + EntityInstanceDraft.forEntity(estimates).withField("duration", "1")); // it should be invalid because the estimate does not have a relationship with a to do Assertions.assertFalse(tidyRoomEstimate.validate().isValid()); @@ -74,23 +83,26 @@ public void anEstimateWithoutATodoIsInvalid(){ @Test public void anEstimateMustHaveATodoToBeValid(){ - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "Tidy up my room"). - setValue("description", "I need to tidy up my room because it is a mess"); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Tidy up my room"). + withField("description", "I need to tidy up my room because it is a mess")); - EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + EntityDefinition estimates = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("estimate"); - EntityInstance tidyRoomEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)). - setValue("duration", "1"); + EntityInstance tidyRoomEstimate = repository.createInstance( + EntityInstanceDraft.forEntity(estimates).withField("duration", "1")); - tidyRoomEstimate.getRelationships().connect("estimate", tidy); + repository.connectRelationship(tidyRoomEstimate, "estimate", tidy); // it should be valid because the estimate has a relationship with a to do Assertions.assertTrue(tidyRoomEstimate.validate().isValid()); - final Collection relatedEstimates = tidy.getRelationships().getConnectedItems("estimate"); + final Collection relatedEstimates = tidy.getRelatedItems("estimate"); Assertions.assertEquals(1, relatedEstimates.size()); } @@ -101,27 +113,30 @@ public void anEstimateMustHaveATodoToBeValid(){ @Test public void deleteAlsoCoversMandatoryOptionalityRelationships(){ - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(todoManager, "todo"); + EntityDefinition todos = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance tidy = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(todos)). - setValue("title", "Tidy up my room"). - setValue("description", "I need to tidy up my room because it is a mess"); + EntityInstance tidy = repository.createInstance( + EntityInstanceDraft.forEntity(todos). + withField("title", "Tidy up my room"). + withField("description", "I need to tidy up my room because it is a mess")); - EntityDefinition estimates = ThingifierRepositoryTestSupport.entity(todoManager, "estimate"); + EntityDefinition estimates = todoManager.getERmodel().getSchema(). + getDefinitionWithSingularOrPluralNamed("estimate"); - EntityInstance tidyRoomEstimate = ThingifierRepositoryTestSupport.repository(todoManager).addInstance(new EntityInstance(estimates)). - setValue("duration", "1"); + EntityInstance tidyRoomEstimate = repository.createInstance( + EntityInstanceDraft.forEntity(estimates).withField("duration", "1")); - tidyRoomEstimate.getRelationships().connect("estimate", tidy); + repository.connectRelationship(tidyRoomEstimate, "estimate", tidy); // it should be valid because the estimate has a relationship with a to do Assertions.assertTrue(tidyRoomEstimate.validate().isValid()); - final Collection relatedEstimates = tidy.getRelationships().getConnectedItems("estimates"); + final Collection relatedEstimates = tidy.getRelatedItems("estimates"); Assertions.assertEquals(1, relatedEstimates.size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(estimates).size()); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos).size()); + Assertions.assertEquals(1, repository.listInstances(estimates).size()); + Assertions.assertEquals(1, repository.listInstances(todos).size()); // now delete the to do, and the estimate should also be deleted @@ -131,8 +146,8 @@ public void deleteAlsoCoversMandatoryOptionalityRelationships(){ // things only know about themselves and their instances, but the thingifier knows about // all things and so can delete related items as well - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(todos).size()); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(todoManager).listInstances(estimates).size()); + Assertions.assertEquals(0, repository.listInstances(todos).size()); + Assertions.assertEquals(0, repository.listInstances(estimates).size()); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java index 4f43bd9b..a91edda1 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/http/requests/ThingifierHttpApiRequestHandlingTest.java @@ -12,7 +12,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.HashMap; import java.util.Map; @@ -44,8 +44,8 @@ public void aGetRequestWillCreateNewSessionWithDatabase(){ final HttpApiResponse response = api.get(new HttpApiRequest("/things").setHeaders(headers)); // add a thing - final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); - final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(thingifier, "other_things").addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); + final EntityDefinition thing = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); + final EntityInstance existingInstance = thingifier.getRepository("other_things").createInstance(EntityInstanceDraft.forEntity(thing).withField("title", "My Title" + System.nanoTime())); final HttpApiResponse response2 = api.get(new HttpApiRequest("/things/" + existingInstance.getPrimaryKeyValue()).setHeaders(headers)); @@ -62,7 +62,7 @@ public void aRequestWillCreateNewSessionWithDatabaseUsingDefaultData(){ EntityDefinition thing = schema.getEntityDefinitionNamed("thing"); for(String thingTitle : titles){ - repository.addInstance(new EntityInstance(thing).setValue("title", thingTitle)); + repository.createInstance(EntityInstanceDraft.forEntity(thing).withField("title", thingTitle)); } }; thingifier.setDataGenerator(dataPopulator); @@ -82,9 +82,9 @@ public void aRequestWillCreateNewSessionWithDatabaseUsingDefaultData(){ Assertions.assertEquals(200, response.getStatusCode()); EntityDefinition thingInstances = - ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); - Assertions.assertEquals(3, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); + Assertions.assertEquals(3, thingifier.getRepository("other_things").countInstances(thingInstances)); } @Test @@ -108,13 +108,13 @@ public void aPostRequestWillCreateNewSessionWithDatabase(){ Assertions.assertEquals(201, response.getStatusCode()); EntityDefinition thingInstances = - ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); + Assertions.assertEquals(1, thingifier.getRepository("other_things").countInstances(thingInstances)); Assertions.assertEquals(0, - ThingifierRepositoryTestSupport.repository(thingifier).countInstances( - ThingifierRepositoryTestSupport.entity(thingifier, "thing"))); + thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"))); } @Test @@ -136,21 +136,22 @@ public void aDeleteRequestWillCreateNewSessionWithDatabase(){ Assertions.assertEquals(404, response.getStatusCode()); EntityDefinition thingInstances = - ThingifierRepositoryTestSupport.entity(thingifier, "other_things", "thing"); + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); - EntityInstance anInstance = ThingifierRepositoryTestSupport.repository(thingifier, "other_things").addInstance(new EntityInstance(thingInstances)); + EntityInstance anInstance = + thingifier.getRepository("other_things").createInstance(EntityInstanceDraft.forEntity(thingInstances)); - Assertions.assertEquals(1, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); + Assertions.assertEquals(1, thingifier.getRepository("other_things").countInstances(thingInstances)); final HttpApiResponse actualDeleteResponse = api.delete(new HttpApiRequest("/things/" + anInstance.getPrimaryKeyValue()) .setHeaders(headers) ); - Assertions.assertEquals(0, ThingifierRepositoryTestSupport.repository(thingifier, "other_things").countInstances(thingInstances)); + Assertions.assertEquals(0, thingifier.getRepository("other_things").countInstances(thingInstances)); Assertions.assertEquals(0, - ThingifierRepositoryTestSupport.repository(thingifier).countInstances( - ThingifierRepositoryTestSupport.entity(thingifier, "thing"))); + thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances( + thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"))); } // OPTIONS diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/BulkUpdateEntityInstanceTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/BulkUpdateEntityInstanceTest.java index db841681..28dbff06 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/BulkUpdateEntityInstanceTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/BulkUpdateEntityInstanceTest.java @@ -8,13 +8,11 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.NamedValue; -import uk.co.compendiumdev.thingifier.core.domain.instances.AutoIncrement; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; public class BulkUpdateEntityInstanceTest { @@ -35,27 +33,26 @@ public void createEntity() { @Test public void canSetByList() { - final EntityInstance session = new EntityInstance(entityTestSession); + final EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession)); List someFields = new ArrayList<>(); someFields.add(new NamedValue("Title", "my title")); someFields.add(new NamedValue("falsey", "true")); - new EntityInstanceBulkUpdater(session).setFieldValuesFrom(someFields); + EntityInstanceDraft draft = + new EntityInstanceBulkUpdater(session).setFieldValuesFrom(someFields); + EntityInstance updated = EntityInstance.fromDraft(draft); Assertions.assertEquals("my title", - session.getFieldValue("Title").asString()); + updated.getFieldValue("Title").asString()); Assertions.assertEquals("true", - session.getFieldValue("falsey").asString()); + updated.getFieldValue("falsey").asString()); } @Test public void canNotSetSomeFieldsByList() { - final EntityInstance session = new EntityInstance(entityTestSession); - session.addAutoGUIDstoInstance(); - Map autos = new HashMap<>(); - autos.put("anid", new AutoIncrement("anid", 1)); - session.addAutoIncrementIdsToInstance(autos); + final EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession). + withProtectedField("anid", "1")); List someFields = new ArrayList<>(); someFields.add(new NamedValue("anid", "12")); @@ -69,7 +66,7 @@ public void canNotSetSomeFieldsByList() { @Test public void canIgnoreSomeSetSomeFieldsByListToAvoidTriggeringValidation() { - final EntityInstance session = new EntityInstance(entityTestSession); + final EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession)); List someFields = new ArrayList<>(); someFields.add(new NamedValue("anid", "12")); @@ -78,16 +75,19 @@ public void canIgnoreSomeSetSomeFieldsByListToAvoidTriggeringValidation() { List ignoring = new ArrayList<>(); ignoring.add("anid"); - new EntityInstanceBulkUpdater(session).setFieldValuesFromArgsIgnoring(someFields, ignoring); + EntityInstanceDraft draft = + new EntityInstanceBulkUpdater(session). + setFieldValuesFromArgsIgnoring(someFields, ignoring); + EntityInstance updated = EntityInstance.fromDraft(draft); Assertions.assertEquals("set Title", - session.getFieldValue("Title").asString()); + updated.getFieldValue("Title").asString()); } @Test public void canIgnoreSomeOverrideFieldsWithListToAIgnore() { - final EntityInstance session = new EntityInstance(entityTestSession); + final EntityInstance session = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(entityTestSession)); List someFields = new ArrayList<>(); someFields.add(new NamedValue("anid", "12")); @@ -97,14 +97,17 @@ public void canIgnoreSomeOverrideFieldsWithListToAIgnore() { List ignoring = new ArrayList<>(); ignoring.add("falsey"); - new EntityInstanceBulkUpdater(session).overrideFieldValuesFromArgsIgnoring(someFields, ignoring); + EntityInstanceDraft draft = + new EntityInstanceBulkUpdater(session). + overrideFieldValuesFromArgsIgnoring(someFields, ignoring); + EntityInstance updated = EntityInstance.fromDraft(draft); Assertions.assertEquals("set Title", - session.getFieldValue("Title").asString()); + updated.getFieldValue("Title").asString()); Assertions.assertEquals("12", - session.getFieldValue("anId").asString()); + updated.getFieldValue("anId").asString()); Assertions.assertEquals("false", - session.getFieldValue("falsey").asString()); + updated.getFieldValue("falsey").asString()); } -} \ No newline at end of file +} diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java index 056321fb..ca26a7ff 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/IdBasedInstanceNonHttpTest.java @@ -1,12 +1,12 @@ package uk.co.compendiumdev.thingifier.api.non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi; import uk.co.compendiumdev.thingifier.api.http.headers.HttpHeadersBlock; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; @@ -41,9 +41,8 @@ public void canGetAThingUsingGuidFromDefaultSession(){ Thingifier model = getThingifier(); - final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "thing"); - final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model).addInstance(new EntityInstance(thing)).setValue("title", - "My Title" + System.nanoTime()); + final EntityDefinition thing = model.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); + final EntityInstance existingInstance = model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(thing).withField("title", "My Title" + System.nanoTime())); // no session header so use default session final ApiResponse apiResponse = model.api().get("/thing/" + existingInstance.getPrimaryKeyValue(), new QueryFilterParams(), new HttpHeadersBlock()); @@ -56,9 +55,8 @@ public void canGetAThingUsingIdFromDefaultSession(){ Thingifier model = getThingifier(); - final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "thing"); - final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model).addInstance(new EntityInstance(thing)).setValue("title", - "My Title" + System.nanoTime()); + final EntityDefinition thing = model.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); + final EntityInstance existingInstance = model.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(thing).withField("title", "My Title" + System.nanoTime())); // no session header so use default session final ApiResponse idApiResponse = model.api().get("/thing/" + existingInstance.getFieldValue("id").asString(), new QueryFilterParams(), new HttpHeadersBlock()); @@ -82,8 +80,8 @@ public void canGetAThingUsingGuidFromCustomSession(){ Assertions.assertEquals(404, idApiResponse.getStatusCode()); // add instance to custom session - final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "other_things", "thing"); - final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model, "other_things").addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); + final EntityDefinition thing = model.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); + final EntityInstance existingInstance = model.getRepository("other_things").createInstance(EntityInstanceDraft.forEntity(thing).withField("title", "My Title" + System.nanoTime())); final ApiResponse idApiResponse2 = model.api().get("/thing/" + existingInstance.getPrimaryKeyValue(), new QueryFilterParams(), headers); Assertions.assertEquals(200, idApiResponse2.getStatusCode()); @@ -105,8 +103,8 @@ public void canGetAThingUsingIdFromCustomSession(){ Assertions.assertEquals(404, idApiResponse.getStatusCode()); // add instance to custom session - final EntityDefinition thing = ThingifierRepositoryTestSupport.entity(model, "other_things", "thing"); - final EntityInstance existingInstance = ThingifierRepositoryTestSupport.repository(model, "other_things").addInstance(new EntityInstance(thing)).setValue("title", "My Title" + System.nanoTime()); + final EntityDefinition thing = model.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); + final EntityInstance existingInstance = model.getRepository("other_things").createInstance(EntityInstanceDraft.forEntity(thing).withField("title", "My Title" + System.nanoTime())); final ApiResponse idApiResponse2 = model.api().get("/thing/" + existingInstance.getFieldValue("id").asString(), new QueryFilterParams(), headers); Assertions.assertEquals(200, idApiResponse2.getStatusCode()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java index db63c3e0..dfb985fc 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/non_http/NestedObjectsApiTest.java @@ -1,11 +1,11 @@ package uk.co.compendiumdev.thingifier.api.non_http; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; @@ -36,7 +36,7 @@ public void createThingWithNestedObjectField(){ defn = thingifier.defineThing("thing", "things"); defn.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); - thing = ThingifierRepositoryTestSupport.entity(thingifier, "thing"); + thing = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("thing"); defn.addField(Field.is("person", FieldType.OBJECT) .withField( @@ -51,11 +51,7 @@ public void createThingWithNestedObjectField(){ @Test void canAmendConnie(){ - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Connie"); - instance.setValue("person.surname", "Dobbs"); - - ThingifierRepositoryTestSupport.repository(thingifier).addInstance(instance); + instance = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(defn).withField("person.firstname", "Connie").withField("person.surname", "Dobbs")); api = new ThingifierHttpApi(thingifier, null, null); @@ -80,7 +76,7 @@ void canCreateBob(){ api = new ThingifierHttpApi(thingifier, null, null); - Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); + Assertions.assertEquals(0,thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thing)); final HttpApiRequest createBobRequest = new HttpApiRequest("/things"); createBobRequest.setHeaders(Map.of("content-type", "application/json")); @@ -91,9 +87,9 @@ void canCreateBob(){ final HttpApiResponse response = api.post(createBobRequest); Assertions.assertEquals(201, response.getStatusCode()); - Assertions.assertEquals(1,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); + Assertions.assertEquals(1,thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thing)); - for(EntityInstance bob : ThingifierRepositoryTestSupport.repository(thingifier).listInstances(thing)){ + for(EntityInstance bob : thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).listInstances(thing)){ FieldValue fv = bob.getFieldValue("person"); InstanceFields obj = fv.asObject(); FieldValue fn = obj.getFieldValue("firstname"); @@ -114,7 +110,7 @@ public void failValidationAtObjectFieldLevel() { api = new ThingifierHttpApi(thingifier, null, null); - Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); + Assertions.assertEquals(0,thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thing)); final HttpApiRequest failToCreateBobRequest = new HttpApiRequest("/things"); failToCreateBobRequest.setVerb(HttpApiRequest.VERB.POST); @@ -125,9 +121,11 @@ public void failValidationAtObjectFieldLevel() { final HttpApiResponse response = api.post(failToCreateBobRequest); Assertions.assertEquals(400, response.getStatusCode()); - Assertions.assertEquals(0,ThingifierRepositoryTestSupport.repository(thingifier).countInstances(thing)); + Assertions.assertEquals(0,thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).countInstances(thing)); Assertions.assertTrue( - response.apiResponse().getErrorMessages().contains("surname : field is mandatory")); + response.apiResponse().getErrorMessages(). + stream(). + anyMatch(error -> error.contains("surname : field is mandatory"))); } } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java index 57b759be..cbc0f8d8 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsJsonTest.java @@ -1,12 +1,12 @@ package uk.co.compendiumdev.thingifier.api.response; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.google.gson.Gson; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -107,9 +107,9 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); + EntityDefinition todos = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); + EntityInstance aTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "a todo")); ApiResponse response = ApiResponse.success().returnSingleInstance(aTodo); @@ -134,12 +134,12 @@ public void response200WithInstances(){ EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); todo.addFields( Field.is("title", STRING)); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); + EntityDefinition todos = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); - EntityInstance anotherTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "another todo"); + EntityInstance aTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "a todo")); + EntityInstance anotherTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "another todo")); - ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(ThingifierRepositoryTestSupport.repository(thingifier).listInstances(todos))); + ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).listInstances(todos))); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(true, response.hasABody()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java index 53025211..ca604669 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseAsXmlTest.java @@ -1,5 +1,6 @@ package uk.co.compendiumdev.thingifier.api.response; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.StaxDriver; import org.junit.jupiter.api.Assertions; @@ -7,7 +8,6 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; @@ -124,9 +124,9 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); + EntityDefinition todos = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); + EntityInstance aTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "a todo")); ApiResponse response = ApiResponse.success().returnSingleInstance(aTodo); @@ -152,12 +152,12 @@ public void response200WithInstances(){ EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addAsPrimaryKeyField(Field.is("guid", FieldType.AUTO_GUID)); todo.addFields( Field.is("title", STRING)); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); + EntityDefinition todos = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); - EntityInstance anotherTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "another todo"); + EntityInstance aTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "a todo")); + EntityInstance anotherTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "another todo")); - ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(ThingifierRepositoryTestSupport.repository(thingifier).listInstances(todos))); + ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).listInstances(todos))); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(true, response.hasABody()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java index f5cb0a2a..4e2162ac 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/response/ApiResponseTest.java @@ -1,10 +1,10 @@ package uk.co.compendiumdev.thingifier.api.response; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.testsupport.ThingifierRepositoryTestSupport; import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; @@ -86,9 +86,9 @@ public void response200WithInstance(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); + EntityDefinition todos = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); + EntityInstance aTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "a todo")); ApiResponse response = ApiResponse.success().returnSingleInstance(aTodo); @@ -107,12 +107,12 @@ public void response200WithInstances(){ Thingifier thingifier = new Thingifier(); EntityDefinition todo = thingifier.defineThing("todo", "todos"); todo.addFields( Field.is("title", STRING)); - EntityDefinition todos = ThingifierRepositoryTestSupport.entity(thingifier, "todo"); + EntityDefinition todos = thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed("todo"); - EntityInstance aTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "a todo"); - EntityInstance anotherTodo = ThingifierRepositoryTestSupport.repository(thingifier).addInstance(new EntityInstance(todos)).setValue("title", "another todo"); + EntityInstance aTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "a todo")); + EntityInstance anotherTodo = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).createInstance(EntityInstanceDraft.forEntity(todos).withField("title", "another todo")); - ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(ThingifierRepositoryTestSupport.repository(thingifier).listInstances(todos))); + ApiResponse response = ApiResponse.success().returnInstanceCollection(new ArrayList(thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME).listInstances(todos))); Assertions.assertEquals(200, response.getStatusCode()); Assertions.assertEquals(true, response.hasABody()); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java index 858bee1e..35c95cd9 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/api/restapihandlers/RepositoryBackedRelationshipUrlResolverTest.java @@ -10,6 +10,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; public class RepositoryBackedRelationshipUrlResolverTest { @@ -40,10 +41,12 @@ public void createModel() { whenReversed(Cardinality.ONE_TO_MANY(), "task-of"); repository = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - task = repository.addInstance(new EntityInstance(todo). - setValue("title", "relationship task")); - projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "relationship project")); + task = repository.createInstance( + EntityInstanceDraft.forEntity(todo). + withField("title", "relationship task")); + projectInstance = repository.createInstance( + EntityInstanceDraft.forEntity(project). + withField("title", "relationship project")); repository.connectRelationship(projectInstance, "tasks", task); resolver = new RepositoryBackedRelationshipUrlResolver( @@ -139,8 +142,9 @@ public void resolvesRelationshipInstancePathByAutoIncrementId() { @Test public void relationshipInstancePathRequiresChildToBeConnected() { - EntityInstance unconnectedTask = repository.addInstance(new EntityInstance(todo). - setValue("title", "unconnected task")); + EntityInstance unconnectedTask = repository.createInstance( + EntityInstanceDraft.forEntity(todo). + withField("title", "unconnected task")); RepositoryBackedRelationshipUrlResolver.RelationshipUrlResolution resolution = resolver.resolveRelationshipInstance( diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java index 7c4f2967..1f9cffd4 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/htmlgui/htmlgen/DefaultGuiHtmlPagesRepositoryTest.java @@ -9,6 +9,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.repository.sqlite.SqliteThingRepositoryProvider; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; @@ -35,10 +36,8 @@ public void guiInstancePagesReadFromRepositoryWithoutLoadingCompatibilitySnapsho ThingRepository repository = thingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); - EntityInstance projectInstance = repository.addInstance(new EntityInstance(project). - setValue("title", "Repository Project")); - EntityInstance todoInstance = repository.addInstance(new EntityInstance(todo). - setValue("title", "Repository Todo")); + EntityInstance projectInstance = repository.createInstance(EntityInstanceDraft.forEntity(project).withField("title", "Repository Project")); + EntityInstance todoInstance = repository.createInstance(EntityInstanceDraft.forEntity(todo).withField("title", "Repository Todo")); repository.connectRelationship(projectInstance, "tasks", todoInstance); diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/JsonThingTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/JsonThingTest.java index 1f6dfff7..f990e6c5 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/JsonThingTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/JsonThingTest.java @@ -10,6 +10,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; public class JsonThingTest { @@ -31,9 +32,9 @@ public void createThingWithNestedObjectField(){ )); - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Connie"); - instance.setValue("person.surname", "Dobbs"); + instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(defn). + withField("person.firstname", "Connie"). + withField("person.surname", "Dobbs")); } diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/XmlThingTest.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/XmlThingTest.java index 8225273d..bb4c0513 100644 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/XmlThingTest.java +++ b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/reporting/XmlThingTest.java @@ -8,6 +8,7 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; +import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.api.ermodelconversion.XmlThing; @@ -30,9 +31,9 @@ public void createThingWithNestedObjectField(){ )); - instance = new EntityInstance(defn); - instance.setValue("person.firstname", "Connie"); - instance.setValue("person.surname", "Dobbs"); + instance = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(defn). + withField("person.firstname", "Connie"). + withField("person.surname", "Dobbs")); } @Test diff --git a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java b/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java deleted file mode 100644 index a2f24f99..00000000 --- a/thingifier/src/test/java/uk/co/compendiumdev/thingifier/testsupport/ThingifierRepositoryTestSupport.java +++ /dev/null @@ -1,35 +0,0 @@ -package uk.co.compendiumdev.thingifier.testsupport; - -import uk.co.compendiumdev.thingifier.Thingifier; -import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; - -public final class ThingifierRepositoryTestSupport { - - private ThingifierRepositoryTestSupport() { - } - - public static ThingRepository repository(final Thingifier thingifier) { - return repository(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME); - } - - public static ThingRepository repository( - final Thingifier thingifier, - final String databaseName) { - return thingifier.getRepository(databaseName); - } - - public static EntityDefinition entity( - final Thingifier thingifier, - final String entityName) { - return entity(thingifier, EntityRelModel.DEFAULT_DATABASE_NAME, entityName); - } - - public static EntityDefinition entity( - final Thingifier thingifier, - final String databaseName, - final String entityName) { - return thingifier.getERmodel().getSchema().getDefinitionWithSingularOrPluralNamed(entityName); - } -} From 7fae57c481606db3eebeb42a8f3dc382908a36b4 Mon Sep 17 00:00:00 2001 From: Alan Richardson Date: Thu, 9 Jul 2026 16:09:34 +0100 Subject: [PATCH 13/22] Add Spotless formatting enforcement --- .githooks/pre-commit | 20 + .github/workflows/run-juit-tests.yml | 10 +- .../challenge/ChallengeMain.java | 42 +- .../challenge/ChallengeRouteHandler.java | 76 +- .../challenge/ChallengerAuthData.java | 32 +- .../challenge/ChallengerConfig.java | 21 +- .../challenge/IndexNowRouteHandler.java | 41 +- .../challenge/apimodel/ChallengeApiModel.java | 37 +- .../apimodel/ChallengeThingifier.java | 52 +- .../apimodel/TodoAPIDataPopulator.java | 25 +- .../ChallengerApiRequestHook.java | 46 +- .../ChallengerApiResponseHook.java | 360 +++--- .../ChallengerInternalHTTPRequestHook.java | 77 +- .../ChallengerInternalHTTPResponseHook.java | 282 ++--- .../challenge/challengers/Challengers.java | 91 +- .../challenge/challenges/ChallengeData.java | 8 +- .../challenges/ChallengeDefinitionData.java | 11 +- .../challenges/ChallengeDefinitions.java | 513 +++++--- .../challenges/ChallengeSolutionLink.java | 26 +- .../definitions/ChallengeRenderer.java | 2 +- .../definitions/ChallengerChallenges.java | 197 ++-- .../definitions/DeleteChallenges.java | 15 +- .../challenges/definitions/GetChallenges.java | 214 ++-- .../definitions/HeadChallenges.java | 16 +- .../definitions/MiscChallenges.java | 40 +- .../definitions/OptionsChallenges.java | 12 +- .../definitions/PostChallenges.java | 321 +++-- .../challenges/definitions/PutChallenges.java | 133 ++- .../definitions/SecretTokenChallenges.java | 138 ++- .../definitions/StatusCodeChallenges.java | 132 ++- .../challengesrouting/AuthRoutes.java | 503 ++++---- .../ChallengerIpAddressTracker.java | 22 +- .../ChallengerTrackingRoutes.java | 693 ++++++----- .../challengesrouting/ChallengesRoutes.java | 145 ++- .../challengesrouting/HeartBeatRoutes.java | 25 +- .../challengesrouting/XChallengerHeader.java | 43 +- .../challenge/gui/ChallengerWebGUI.java | 840 +++++++------ .../challenge/gui/MarkdownContentManager.java | 1046 ++++++++++------- .../challenge/gui/PageNotFoundResponse.java | 2 +- .../challenge/gui/ResourceContentScanner.java | 71 +- .../challenge/gui/SiteMapXml.java | 33 +- .../challenge/persistence/AwsS3Storage.java | 141 +-- .../persistence/ChallengerFileStorage.java | 80 +- .../ChallengerPersistenceMechanism.java | 1 + .../DatabaseContentPersistenceMechanism.java | 1 + .../persistence/PersistenceLayer.java | 109 +- .../persistence/PersistenceResponse.java | 15 +- .../mirror/MirrorHttpApiRequestHandler.java | 20 +- .../MirrorHttpApiTextRequestHandler.java | 25 +- .../practicemodes/mirror/MirrorRoutes.java | 161 +-- .../practicemodes/mirror/RequestMirror.java | 28 +- .../simpleapi/AddMoreItemsIfNecessary.java | 4 +- .../simpleapi/RandomIsbnGenerator.java | 6 +- .../ResetAutoIncrementWhenTooHigh.java | 10 +- .../simpleapi/SimpleAPITestDataPopulator.java | 29 +- .../SimpleApiCorsHeadersResponseHook.java | 21 +- .../simpleapi/SimpleApiRoutes.java | 163 ++- .../simulation/SimulationRoutes.java | 405 ++++--- .../spark/SparkMessageLengthValidator.java | 27 +- .../challenge/ChallengeAuthDataTest.java | 34 +- .../ChallengerConfigRepositoryTest.java | 44 +- .../challenge/IndexNowRouteHandlerTest.java | 28 +- .../ChallengeApiModelRepositoryTest.java | 163 ++- .../ChallengerApiResponseHookTest.java | 106 +- ...ChallengerInternalHttpRequestHookTest.java | 166 +-- ...hallengerInternalHttpResponseHookTest.java | 219 ++-- .../challengers/ChallengersTest.java | 10 +- .../challengesrouting/AuthRoutesTest.java | 386 +++--- .../ChallengerIpAddressTrackerTest.java | 13 +- .../ChallengerTrackingRoutesTest.java | 115 +- .../ChallengesRoutesTest.java | 49 +- .../HeartBeatRoutesTest.java | 22 +- ...arkdownContentManagerDateMetadataTest.java | 6 +- .../SimulationRoutesRepositoryTest.java | 10 +- .../ChallengeCompleteTest.java | 670 ++++++----- .../CompleteAllChallengesMultiUserTest.java | 44 +- .../CompleteAllChallengesSingleUserTest.java | 16 +- .../MultiPlayerModeHookTest.java | 25 +- .../http/conditions/ConfigSetupTest.java | 10 +- .../http/defects/MultiUserDefectsTest.java | 47 +- .../http/httpclient/CanSendHttpRequests.java | 2 + .../httpclient/HttpClientRequestSender.java | 74 +- .../http/httpclient/HttpMessageSender.java | 61 +- .../http/httpclient/HttpRequestDetails.java | 15 +- .../http/httpclient/HttpRequestSender.java | 88 +- .../http/httpclient/HttpResponseDetails.java | 2 +- .../practicemodes/MirrorModeTest.java | 65 +- .../practicemodes/SmulationModeTest.java | 86 +- .../simpleapi/SimpleApiCrudTest.java | 153 ++- .../simpleapi/SimpleApiModeTest.java | 215 ++-- .../simpleapi/testabstractions/Item.java | 5 +- .../testabstractions/SimpleAPIApi.java | 17 +- .../compendiumdev/sparkstart/Environment.java | 36 +- .../uk/co/compendiumdev/sparkstart/Port.java | 14 +- .../SeoTitleContentValidationTest.java | 134 ++- .../uirouting/UiPagesAreReachableTest.java | 276 +++-- .../challenger/payloads/Challenges.java | 2 - .../challenger/payloads/ErrorMessages.java | 3 +- .../challenger/payloads/Todo.java | 1 - .../restassured/ChallengerAutoSmokeTest.java | 19 +- .../C001PostChallenger201Test.java | 7 +- .../C002GetChallengesTest.java | 33 +- .../C003GetTodos200Test.java | 22 +- .../C004GetTodo404NotPluralTest.java | 16 +- .../C005GetTodosIdFound200Test.java | 24 +- .../C006GetTodosIdMissing404Test.java | 20 +- .../C007GetTodosFiltered200Test.java | 35 +- .../C008HeadTodos200Test.java | 91 +- .../C009PostTodos200Test.java | 35 +- ...tTodosFailValidationDoneStatus400Test.java | 30 +- .../C011PostTodosTitleTooLong400Test.java | 29 +- ...012PostTodosDescriptionTooLong400Test.java | 39 +- .../C013PostTodosMaxContent201Test.java | 44 +- .../C014PostTodosContentTooLong413Test.java | 34 +- .../C015PostTodosExtraFields400Test.java | 30 +- .../C016PutTodosFailCreate400Test.java | 33 +- .../C017PostUpdateTodosSuccess200Test.java | 56 +- .../C018PostUpdateTodosFail404Test.java | 45 +- ...C019PutTodosUpdateFullExisting200Test.java | 35 +- ...0PutTodosUpdatePartialExisting200Test.java | 41 +- .../C021PutTodosFailUpdateNoTitle400Test.java | 32 +- .../C022PutTodosWrongId400Test.java | 41 +- .../C023DeleteExistingTodo200Test.java | 34 +- .../C024OptionsTodos200Test.java | 40 +- .../C025GetTodosAcceptXml200Test.java | 31 +- .../C026GetTodosAcceptJson200Test.java | 28 +- .../C027GetTodosAcceptAnyJson200Test.java | 27 +- .../C028GetTodosAcceptXmlPref200Test.java | 27 +- .../C029GetTodosJsonNoAccept200Test.java | 28 +- ...030GetTodosNotAcceptableHeader406Test.java | 29 +- .../C031PostTodosXmlTest.java | 48 +- .../C032PostTodosJsonTest.java | 37 +- ...ostTodosUnsupportedContentType415Test.java | 22 +- ...4GetChallengerSessionForRestoringTest.java | 39 +- .../C035PutRestoreChallengerSessionTest.java | 112 +- ...C036PutCreateNewChallengerSessionTest.java | 73 +- .../C037GetTodosDatabaseForRestoringTest.java | 63 +- .../C038PutRestoreChallengerTodosTest.java | 84 +- .../C039PostTodosXmlToJsonTest.java | 36 +- .../C040PostTodosJsonToXmlTest.java | 29 +- .../C041DeleteReceiveStatusCode405Test.java | 17 +- .../C042PatchReceiveStatusCode500Test.java | 18 +- .../C043TraceReceiveStatusCode501Test.java | 18 +- .../C044GetReceiveStatusCode204Test.java | 17 +- .../C045PostOverrideToDelete405Test.java | 18 +- .../C046PostOverrideToPatch500Test.java | 21 +- .../C047PostOverrideToTrace501Test.java | 21 +- .../CXXXextraDeleteExistingTodo200Test.java | 33 +- .../C048PostNotAuthenticated401Test.java | 28 +- .../C049PostAuthenticated201Test.java | 30 +- ...C050GetSecretNoteNotAuthorized403Test.java | 18 +- .../C051GetSecretNoteNoAuthToken401Test.java | 16 +- .../C052GetSecretNoteAuthorized200Test.java | 86 +- ...3PostAmendSecretNoteAuthorized200Test.java | 56 +- .../C054PostNoAuthToken401Test.java | 20 +- .../C055PostIncorrectAuthTokenTest.java | 31 +- ...57CanUseBearerAuthenticationTokenTest.java | 129 +- .../C058DeleteAllTodosTest.java | 8 +- .../C059AddMaximumNumberOfTodosTest.java | 40 +- .../restassured/api/ChallengesStatus.java | 41 +- .../restassured/api/RestAssuredBaseTest.java | 37 +- .../challenger/restassured/api/TodosApi.java | 104 +- .../BasicSimpleApiCrudCoverageTest.java | 150 +-- .../simpleapi/TimestampToIsbn13.java | 47 +- .../simpleapi/payloads/Item.java | 3 +- .../simulator/SimulatorHttpTest.java | 326 +++-- .../sparkstart/ChallengerAutoConfig.java | 107 +- .../sparkstart/ChallengerAutoConfigTest.java | 123 +- .../ChallengerAutoLocalArgsTest.java | 49 +- .../sparkstart/ChallengerAutoRunProfiles.java | 51 +- .../sparkstart/ChallengerAutoRuntime.java | 42 +- .../ChallengerAutoRuntimeHarnessCheck.java | 66 +- .../compendiumdev/sparkstart/Environment.java | 5 +- .../uk/co/compendiumdev/sparkstart/Port.java | 6 +- .../thingifier/core/EntityRelModel.java | 46 +- .../core/domain/definitions/Cardinality.java | 10 +- .../domain/definitions/DefinedFields.java | 11 +- .../definitions/DefinedRelationships.java | 11 +- .../core/domain/definitions/ERSchema.java | 51 +- .../domain/definitions/EntityDefinition.java | 33 +- .../definitions/field/definition/Field.java | 196 ++- .../field/definition/FieldType.java | 3 - .../field/instance/FieldValue.java | 46 +- .../field/instance/NamedValue.java | 2 +- .../definitions/relationship/Optionality.java | 15 +- .../relationship/RelationshipDefinition.java | 57 +- .../RelationshipVectorDefinition.java | 21 +- .../validation/BooleanValidationRule.java | 16 +- .../validation/EnumValidationRule.java | 21 +- .../FieldAutoIncrementValidationRule.java | 11 +- .../validation/FindsRegexValidationRule.java | 15 +- .../validation/FloatValidationRule.java | 33 +- .../validation/IntegerValidationRule.java | 31 +- .../validation/MandatoryValidationRule.java | 11 +- .../MatchesRegexValidationRule.java | 9 +- .../MaximumLengthValidationRule.java | 4 +- .../TypeValidationFailedMessageGenerator.java | 16 +- .../domain/definitions/validation/VRule.java | 3 +- .../core/domain/instances/AutoIncrement.java | 17 +- .../core/domain/instances/ERInstanceData.java | 73 +- .../core/domain/instances/EntityInstance.java | 77 +- .../instances/EntityInstanceCollection.java | 176 +-- .../domain/instances/EntityInstanceDraft.java | 10 +- .../EntityInstanceRelationships.java | 151 +-- .../EntityInstanceRepositoryAccess.java | 50 +- .../core/domain/instances/InstanceFields.java | 112 +- .../instances/RelationshipVectorInstance.java | 90 +- .../EntityInstanceDraftValidator.java | 44 +- .../EntityInstanceStateValidator.java | 9 +- .../core/domain/randomdata/RandomString.java | 24 +- .../core/query/ComparableFieldValue.java | 23 +- .../core/query/EntityInstanceListFilter.java | 16 +- .../core/query/EntityInstanceListSorter.java | 52 +- .../query/EntityListFilterParamParser.java | 59 +- .../core/query/EntityListSortParamParser.java | 42 +- .../thingifier/core/query/FilterBy.java | 20 +- .../core/query/QueryFilterParams.java | 13 +- .../core/query/RepositoryUrlQuery.java | 19 +- .../core/query/SortByFieldName.java | 3 +- .../thingifier/core/query/UrlQueryResult.java | 3 +- .../core/query/fromurl/UrlParamParser.java | 18 +- .../core/reporting/ERModelReport.java | 5 +- .../reporting/RepositoryJsonExporter.java | 20 +- .../core/reporting/ValidationReport.java | 19 +- .../core/repository/ThingRepository.java | 10 +- .../repository/ThingRepositoryProvider.java | 3 +- .../ThingRepositoryProviderConfig.java | 43 +- .../inmemory/InMemoryThingRepository.java | 39 +- .../InMemoryThingRepositoryProvider.java | 9 +- .../sqlite/SqliteRegexFilterPolicy.java | 3 +- .../sqlite/SqliteRegexToLikeConverter.java | 35 +- .../sqlite/SqliteThingRepository.java | 557 +++++---- .../sqlite/SqliteThingRepositoryProvider.java | 36 +- .../EntityInstanceWriteValidator.java | 16 +- .../thingifier/core/EntityRelModelTest.java | 82 +- .../MultipleEntityInstancesDatabasesTest.java | 26 +- .../thingifier/core/ThingTest.java | 102 +- .../domain/definitions/CardinalityTest.java | 21 +- .../domain/definitions/DefinedFieldsTest.java | 62 +- .../definitions/DefinedRelationshipsTest.java | 99 +- .../definitions/ThingDefinitionTest.java | 26 +- .../definition/AutoIncrementIdFieldTest.java | 36 +- .../field/definition/BooleanFieldTest.java | 58 +- .../field/definition/EnumFieldTest.java | 88 +- .../field/definition/FloatFieldTest.java | 127 +- .../field/definition/GenericFieldTest.java | 40 +- .../field/definition/GuidFieldTest.java | 7 +- .../field/definition/IntegerFieldTest.java | 105 +- .../field/definition/ObjectFieldTest.java | 89 +- .../field/definition/StringFieldTest.java | 97 +- .../field/instance/FieldObjectValueTest.java | 31 +- .../field/instance/FieldValueTest.java | 18 +- .../relationship/OptionalityTest.java | 23 +- .../RelationshipDefinitionTest.java | 39 +- .../relationship/RelationshipVectorTest.java | 13 +- .../FindsRegexValidationRuleTest.java | 46 +- .../MatchesRegexValidationRuleTest.java | 46 +- .../MaximumLengthValidationRuleTest.java | 59 +- .../NotEmptyValidationRuleTest.java | 39 +- .../domain/instances/AutoIncrementTest.java | 13 +- .../EntityInstanceCollectionTest.java | 81 +- .../instances/EntityInstanceDraftTest.java | 119 +- .../EntityInstanceFieldsAccessTest.java | 68 +- .../domain/instances/InstanceFieldsTest.java | 119 +- .../instances/RelationshipInstanceTest.java | 33 +- .../domain/instances/RelationshipsTest.java | 164 ++- .../ThingInstanceCardinalityCreationTest.java | 79 +- .../instances/ThingInstanceCreationTest.java | 16 +- .../domain/instances/fields/AutoGuidTest.java | 10 +- .../fields/AutoIncrementIdFieldTest.java | 22 +- .../instances/fields/BooleanFieldTest.java | 56 +- .../instances/fields/EnumFieldTest.java | 57 +- .../instances/fields/FloatFieldTest.java | 34 +- .../instances/fields/IntegerFieldTest.java | 54 +- .../instances/fields/ObjectFieldTest.java | 89 +- .../instances/fields/StringFieldTest.java | 108 +- .../instances/fields/UniqueFieldTest.java | 74 +- .../domain/randomdata/RandomStringTest.java | 6 +- .../core/query/QueryFiltersBooleanTest.java | 102 +- .../core/query/QueryFiltersFloatTest.java | 119 +- .../core/query/QueryFiltersIdTest.java | 55 +- .../core/query/QueryFiltersIntegerTest.java | 155 ++- .../core/query/QueryFiltersStringTest.java | 114 +- .../query/SortingViaQueryFiltersTest.java | 151 ++- .../query/TodoManagerQueryEngineTest.java | 234 ++-- .../urlparamsparser/FilterableParamsTest.java | 22 +- .../ThingRepositoryContractTest.java | 212 ++-- .../ThingRepositoryProviderConfigTest.java | 39 +- .../data/TodoAPIDataPopulator.java | 17 +- .../data/TodoManagerAPIDataPopulator.java | 20 +- .../examples/TodoListThingifier.java | 59 +- .../examples/TodoManagerThingifier.java | 95 +- pom.xml | 32 + readme.md | 34 +- .../todolist/sqlite/application/Main.java | 17 +- .../todolist/application/Main.java | 15 +- .../compendiumdev/sparkstart/Environment.java | 26 +- .../uk/co/compendiumdev/sparkstart/Port.java | 6 +- .../uk/co/compendiumdev/version4/api/Api.java | 32 +- .../compendiumdev/version4/api/Payloads.java | 12 +- .../version4/projects/ProjectEntityTest.java | 28 +- .../Tasks_ProjectToTodosTest.java | 57 +- .../version4/todos/TodoEntityTest.java | 49 +- .../todolist/application/Main.java | 16 +- .../swaggerizer/SwaggerizerTest.java | 18 +- .../swaggerizer/TodoAPITestDataPopulator.java | 21 +- .../TodoListThingifierTestModel.java | 55 +- .../compendiumdev/thingifier/Thingifier.java | 116 +- .../api/ThingifierRestAPIHandler.java | 20 +- .../api/docgen/ApiRoutingDefinition.java | 36 +- .../ApiRoutingDefinitionDocGenerator.java | 502 +++++--- .../api/docgen/HeaderDefinition.java | 2 +- .../api/docgen/RoutingDefinition.java | 46 +- .../thingifier/api/docgen/RoutingStatus.java | 18 +- .../thingifier/api/docgen/RoutingVerb.java | 9 +- .../ThingifierApiDocumentationDefn.java | 91 +- .../api/ermodelconversion/JsonPopulator.java | 92 +- .../api/ermodelconversion/JsonThing.java | 199 ++-- .../api/ermodelconversion/XmlThing.java | 23 +- .../thingifier/api/http/HttpApiRequest.java | 47 +- .../api/http/HttpApiRequestValidator.java | 35 +- .../thingifier/api/http/HttpApiResponse.java | 44 +- .../api/http/MethodOverrideParser.java | 10 +- .../api/http/ThingifierHttpApi.java | 158 +-- .../api/http/bodyparser/BodyParser.java | 150 +-- .../xml/GenericXMLPrettyPrinter.java | 27 +- .../api/http/bodyparser/xml/StringToXML.java | 9 +- .../bodyparser/xml/XMLParserAbstraction.java | 1 + .../http/bodyparser/xml/XMLParserFactory.java | 2 +- .../bodyparser/xml/XMLParserUsingOrgJson.java | 40 +- .../bodyparser/xml/XMLParserUsingXstream.java | 42 +- .../api/http/headers/HttpHeadersBlock.java | 21 +- .../headerparser/AcceptHeaderParser.java | 80 +- .../headerparser/BasicAuthHeaderParser.java | 39 +- .../headerparser/BearerAuthHeaderParser.java | 18 +- .../headerparser/ContentTypeHeaderParser.java | 6 +- .../AcceptHeaderValidator.java | 31 +- .../ContentTypeHeaderValidator.java | 18 +- .../thingifier/api/response/ApiResponse.java | 57 +- .../api/response/ApiResponseAsJson.java | 11 +- .../api/response/ApiResponseAsXml.java | 29 +- .../api/response/ApiResponseError.java | 6 +- .../restapihandlers/BodyArgsProcessor.java | 84 +- .../BodyCreationValidator.java | 27 +- .../BodyRelationshipValidator.java | 202 ++-- .../EntityInstanceBulkUpdater.java | 26 +- .../api/restapihandlers/EntityUrlMatcher.java | 12 +- .../api/restapihandlers/FieldValues.java | 7 +- .../RelationshipCollector.java | 9 +- .../restapihandlers/RelationshipCreation.java | 129 +- .../restapihandlers/RelationshipCreator.java | 50 +- .../restapihandlers/RelationshipDetails.java | 6 +- ...positoryBackedRelationshipUrlResolver.java | 27 +- .../restapihandlers/RestApiDeleteHandler.java | 30 +- .../restapihandlers/RestApiGetHandler.java | 74 +- .../restapihandlers/RestApiPostHandler.java | 91 +- .../restapihandlers/RestApiPutHandler.java | 71 +- .../restapihandlers/SessionHeaderParser.java | 8 +- .../api/restapihandlers/ThingAmendment.java | 50 +- .../api/restapihandlers/ThingCreation.java | 144 ++- .../thingifier/apiconfig/AdminConfig.java | 10 +- .../thingifier/apiconfig/ApiDocsConfig.java | 10 +- .../apiconfig/JsonOutputConfig.java | 20 +- .../thingifier/apiconfig/ParamConfig.java | 23 +- .../apiconfig/StatusCodeConfig.java | 24 +- .../apiconfig/ThingifierApiConfig.java | 78 +- .../apiconfig/ThingifierApiConfigProfile.java | 10 +- .../ThingifierApiConfigProfiles.java | 47 +- .../AdhocDocumentedSparkRouteConfigurer.java | 40 +- .../application/MainImplementation.java | 178 ++- .../HttpApiRequestHook.java | 7 +- .../HttpApiResponseHook.java | 11 +- .../SparkHttpGenericExceptionRoutings.java | 43 +- .../ThingifierAutoDocGenRouting.java | 100 +- .../ThingifierHttpApiRoutings.java | 341 +++--- .../HttpApiResponseToSpark.java | 9 +- .../InternalHttpResponse.java | 10 +- .../SparkResponseToInternalHttpResponse.java | 11 +- .../SparkToHttpApiRequest.java | 63 +- .../internalhttpconversion/StringPair.java | 2 +- .../ThingifierHttpApiBridge.java | 25 +- .../routehandlers/ShutdownRouteHandler.java | 47 +- .../SparkApiRequestResponseHandler.java | 26 +- .../ClearDataPreSparkRequestHook.java | 8 +- .../LogTheResponseHook.java | 6 +- .../LogTheSparkRequestHook.java | 8 +- .../SparkRequestResponseHook.java | 4 +- .../htmlgui/htmlgen/DefaultGUIHTML.java | 75 +- .../htmlgui/htmlgen/DefaultGuiHtmlPages.java | 322 +++-- .../htmlgen/DocumentationThingInstance.java | 15 +- .../thingifier/htmlgui/htmlgen/HtmlUtils.java | 10 +- .../RestApiDocumentationGenerator.java | 477 +++++--- .../htmlgui/routing/DefaultGuiRoutings.java | 131 ++- .../thingifier/reporting/ThingReporter.java | 1 - .../spark/SimpleSparkRouteCreator.java | 91 +- .../thingifier/spark/SparkAsText.java | 10 +- .../swaggerizer/SwaggerGenerationConfig.java | 4 +- .../thingifier/swaggerizer/Swaggerizer.java | 242 ++-- .../java/uk/co/compendiumdev/UsageTest.java | 61 +- ...leVerbPutEntityInstanceApiNonHttpTest.java | 338 ++++-- .../casestudy/todomanager/ModelsTest.java | 75 +- .../todomanager/TodoManagerModel.java | 149 ++- .../RelationshipApiNonHttpTest.java | 799 +++++++++---- .../RelationshipApiSqliteRepositoryTest.java | 228 ++-- ...erbDeleteEntityInstanceApiNonHttpTest.java | 84 +- .../VerbGetEntityInstanceApiNonHttpTest.java | 245 ++-- .../VerbPostEntityInstanceApiNonHttpTest.java | 217 ++-- .../VerbPutEntityInstanceApiNonHttpTest.java | 383 +++--- .../http_api/DeleteRequestTest.java | 88 +- .../todomanager/http_api/HeadersSupport.java | 15 +- .../http_api/JsonRequestResponseTest.java | 213 ++-- .../http_api/RelationshipHttpTest.java | 547 ++++++--- .../http_api/XmlRequestResponseTest.java | 169 +-- .../unit/BasicTodoManagerTest.java | 154 ++- .../unit/OptionalityRelationshipTest.java | 144 ++- .../api/AcceptHeaderParserTest.java | 57 +- .../api/http/bodyparser/BodyParserTest.java | 53 +- .../api/http/headers/HeadersTest.java | 2 +- .../BasicAuthHeaderParserTest.java | 90 +- .../BearerAuthHeaderParserTest.java | 27 +- .../ThingifierHttpApiRequestHooksTest.java | 40 +- .../ThingifierHttpApiResponseHooksTest.java | 31 +- .../ThingifierHttpApiRequestHandlingTest.java | 139 ++- ...hingifierHttpApiRequestStatusCodeTest.java | 30 +- .../BulkUpdateEntityInstanceTest.java | 75 +- .../non_http/IdBasedInstanceNonHttpTest.java | 102 +- .../api/non_http/NestedObjectsApiTest.java | 112 +- .../api/response/ApiResponseAsJsonTest.java | 105 +- .../api/response/ApiResponseAsXmlTest.java | 120 +- .../api/response/ApiResponseTest.java | 75 +- ...toryBackedRelationshipUrlResolverTest.java | 33 +- .../ThingifierApiConfigProfileTest.java | 6 +- .../ThingifierApiConfigProfilesTest.java | 41 +- .../DefaultGuiHtmlPagesRepositoryTest.java | 41 +- .../thingifier/reporting/JsonThingTest.java | 29 +- .../thingifier/reporting/XmlThingTest.java | 28 +- .../thingifier/application/Main.java | 23 +- .../uk/co/compendiumdev/sparkstart/Port.java | 6 +- .../CanClearEnvironmentTest.java | 88 +- .../CreateALotOfTodosTest.java | 46 +- .../postmanreplication/Environment.java | 33 +- .../ProjectTodoRelationshipCrudTest.java | 134 ++- .../postmanreplication/TodosCrudTest.java | 233 ++-- 443 files changed, 17819 insertions(+), 14922 deletions(-) create mode 100755 .githooks/pre-commit diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 00000000..7bb71670 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,20 @@ +#!/bin/sh + +set -e + +run_maven() { + shell_name="$(uname -s 2>/dev/null || echo unknown)" + + if [ -x "./mvnw" ]; then + "./mvnw" "$@" + elif [ -f "./mvnw.cmd" ] && case "$shell_name" in MINGW*|MSYS*|CYGWIN*) true;; *) false;; esac; then + ./mvnw.cmd "$@" + elif command -v mvn.cmd >/dev/null 2>&1 && case "$shell_name" in MINGW*|MSYS*|CYGWIN*) true;; *) false;; esac; then + mvn.cmd "$@" + else + mvn "$@" + fi +} + +echo "Running Spotless formatting check..." +run_maven -q spotless:check diff --git a/.github/workflows/run-juit-tests.yml b/.github/workflows/run-juit-tests.yml index f4a74c50..4311b186 100644 --- a/.github/workflows/run-juit-tests.yml +++ b/.github/workflows/run-juit-tests.yml @@ -17,10 +17,14 @@ jobs: java: [ 17, 21 ] name: Java ${{ matrix.java }} execution steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: temurin java-version: ${{ matrix.java }} + cache: maven + - name: Check Java formatting + run: mvn -B spotless:check - name: Build with Maven - run: mvn clean test + run: mvn -B clean test diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java index 1afa0065..1ec55f56 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeMain.java @@ -20,7 +20,8 @@ public static void main(String[] args) { logger.info("Starting Challenger"); MainImplementation app = new MainImplementation(); - ThingRepositoryProviderConfig repositoryConfig = ThingRepositoryProviderConfig.fromArgs(args); + ThingRepositoryProviderConfig repositoryConfig = + ThingRepositoryProviderConfig.fromArgs(args); logger.info("Using Thingifier repository {}", repositoryConfig.describe()); Thingifier thingifier = new ChallengeApiModel().get(repositoryConfig.createProvider()); app.registerModel("challengeapi", thingifier); @@ -33,15 +34,17 @@ public static void main(String[] args) { new IndexNowRouteHandler().configureRoutes(); - // create a default configuration class which we pass into the ChallengeRouteHandler and configure via args + // create a default configuration class which we pass into the ChallengeRouteHandler and + // configure via args ChallengerConfig config = new ChallengerConfig(); config.setSimulationRepositoryFromArgs(args); - logger.info("Using Simulation repository {}", + logger.info( + "Using Simulation repository {}", config.getSimulationRepositoryConfig().describe()); for (String arg : args) { - if (arg.toLowerCase().startsWith("-multiplayer") || - arg.toLowerCase().startsWith("-multiuser")) { + if (arg.toLowerCase().startsWith("-multiplayer") + || arg.toLowerCase().startsWith("-multiuser")) { logger.info("Running in multiplayer mode"); config.setToMultiPlayerMode(); } @@ -51,14 +54,13 @@ public static void main(String[] args) { config.setToCloudPersistenceMode(); } - if(arg.toLowerCase().startsWith("-guikeepalive")){ + if (arg.toLowerCase().startsWith("-guikeepalive")) { logger.info("Setting GUI to keep session alive through XHR"); config.setGuiToKeepSessionAlive(); } - if (arg.toLowerCase().startsWith("-memory") || - arg.toLowerCase().startsWith("-nostorage") - ) { + if (arg.toLowerCase().startsWith("-memory") + || arg.toLowerCase().startsWith("-nostorage")) { logger.info("Setting persistence mechanism to no persistence"); config.setToNoPersistenceMode(); } @@ -68,7 +70,7 @@ public static void main(String[] args) { config.enableAdminApi(); } - if(arg.toLowerCase().startsWith("-unlimitedtodos")){ + if (arg.toLowerCase().startsWith("-unlimitedtodos")) { // remove the limit on number of todos logger.info("Enabling Unlimited TODO Instances"); thingifier.getDefinitionNamed("todo").setNoMaxInstanceLimit(); @@ -81,7 +83,6 @@ public static void main(String[] args) { // setup routes required for challenges challenger = new ChallengeRouteHandler(thingifier, app.getApiDefn(), config); - app.chooseThingifier(); // can set profile by adding more configs, or just // app.setProfileToUse(aProfile) @@ -89,23 +90,25 @@ public static void main(String[] args) { app.setupDefaultGui(); app.getGuiManagement().setCanonicalHost("https://apichallenges.eviltester.com"); - app.getGuiManagement().appendToCustomHeadContent( -""" + app.getGuiManagement() + .appendToCustomHeadContent( + """ - """ - ); + """); challenger.setupGui(app.getGuiManagement()); challenger.configureRoutes(); - if(challenger.isSinglePlayerMode()){ + if (challenger.isSinglePlayerMode()) { logger.info("Running in Single User Mode"); - challenger.getThingifier().ensureCreatedAndPopulatedInstanceDatabaseNamed(Challengers.SINGLE_PLAYER_GUID); + challenger + .getThingifier() + .ensureCreatedAndPopulatedInstanceDatabaseNamed(Challengers.SINGLE_PLAYER_GUID); } final ThingifierHttpApiRoutings restServer = app.startRestServer(); @@ -113,14 +116,13 @@ public static void main(String[] args) { app.addBuiltInArgConfiguredHooks(); challenger.addHooks(restServer); - } - public static ChallengeRouteHandler getChallenger(){ + public static ChallengeRouteHandler getChallenger() { return challenger; } - public static void stop(){ + public static void stop() { if (challenger != null) { challenger.close(); } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java index 13fbe789..9a0155e9 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengeRouteHandler.java @@ -14,10 +14,9 @@ import uk.co.compendiumdev.thingifier.application.httprouting.ThingifierHttpApiRoutings; import uk.co.compendiumdev.thingifier.htmlgui.htmlgen.DefaultGUIHTML; - public class ChallengeRouteHandler { private final Thingifier thingifier; - //List routes; + // List routes; ThingifierApiDocumentationDefn apiChallengesDocumentationDefn; ThingifierApiDocumentationDefn mirrorModeDocumentationDefn; @@ -27,18 +26,24 @@ public class ChallengeRouteHandler { private boolean single_player_mode; private final ChallengerConfig config; PersistenceLayer persistenceLayer; - private boolean guiStayAlive=false; // when set gui makes a call every 5 mins to keep session alive, + private boolean guiStayAlive = + false; // when set gui makes a call every 5 mins to keep session alive, private DefaultGUIHTML guiTemplates; private SimulationRoutes simulationRoutes; + // not needed when storing data - public ChallengeRouteHandler(Thingifier thingifier, ThingifierApiDocumentationDefn apiDefn, ChallengerConfig config){ + public ChallengeRouteHandler( + Thingifier thingifier, + ThingifierApiDocumentationDefn apiDefn, + ChallengerConfig config) { this.config = config; this.apiChallengesDocumentationDefn = apiDefn; apiDefn.setThingifier(thingifier); apiDefn.setSeoTitle("API Challenges API Documentation | API Challenges"); - apiDefn.setSeoDescription("Explore API Challenges endpoint documentation with request formats, payload examples, and expected responses for practical API testing."); + apiDefn.setSeoDescription( + "Explore API Challenges endpoint documentation with request formats, payload examples, and expected responses for practical API testing."); apiDefn.setMetaRobots("index,follow"); apiDefn.setOgType("website"); apiDefn.setTwitterCard("summary_large_image"); @@ -47,11 +52,13 @@ public ChallengeRouteHandler(Thingifier thingifier, ThingifierApiDocumentationDe mirrorModeDocumentationDefn.setTitle("Mirror Mode"); mirrorModeDocumentationDefn.setDescription("Mirror HTTP Requests"); mirrorModeDocumentationDefn.setSeoTitle("Mirror Mode API Documentation | API Challenges"); - mirrorModeDocumentationDefn.setSeoDescription("Review Mirror Mode endpoint documentation to inspect reflected HTTP requests, headers, and payload behavior for debugging and learning."); + mirrorModeDocumentationDefn.setSeoDescription( + "Review Mirror Mode endpoint documentation to inspect reflected HTTP requests, headers, and payload behavior for debugging and learning."); mirrorModeDocumentationDefn.setMetaRobots("noindex,follow"); mirrorModeDocumentationDefn.setOgType("website"); mirrorModeDocumentationDefn.setTwitterCard("summary_large_image"); - mirrorModeDocumentationDefn.addServer("https://apichallenges.eviltester.com", "cloud hosted version"); + mirrorModeDocumentationDefn.addServer( + "https://apichallenges.eviltester.com", "cloud hosted version"); mirrorModeDocumentationDefn.addServer("http://localhost:4567", "local execution"); mirrorModeDocumentationDefn.setVersion("1.0.0"); @@ -61,40 +68,52 @@ public ChallengeRouteHandler(Thingifier thingifier, ThingifierApiDocumentationDe single_player_mode = config.single_player_mode; persistenceLayer = config.persistenceLayer; - guiStayAlive=config.guiStayAlive; + guiStayAlive = config.guiStayAlive; challengeDefinitions = new ChallengeDefinitions(config); this.thingifier = thingifier; - challengers = new Challengers(thingifier.getERmodel(), challengeDefinitions.getDefinedChallenges()); + challengers = + new Challengers( + thingifier.getERmodel(), challengeDefinitions.getDefinedChallenges()); challengers.setPersistenceLayer(persistenceLayer); - if(!single_player_mode){ + if (!single_player_mode) { challengers.setMultiPlayerMode(); } - if(single_player_mode) { + if (single_player_mode) { // auto load any single player challenger details in single player mode persistenceLayer.tryToLoadChallenger(challengers, challengers.SINGLE_PLAYER_GUID); } challengers.setApiConfig(thingifier.apiConfig()); - if(config.isAdminApiEnabled){ + if (config.isAdminApiEnabled) { enableAdminApi(); } this.guiTemplates = new DefaultGUIHTML(); - - } - public boolean isSinglePlayerMode(){ + public boolean isSinglePlayerMode() { return single_player_mode; } public ChallengeRouteHandler configureRoutes() { - new ChallengerTrackingRoutes().configure(challengers, single_player_mode, apiChallengesDocumentationDefn, persistenceLayer, thingifier, challengeDefinitions); - new ChallengesRoutes().configure(challengers, single_player_mode, apiChallengesDocumentationDefn, challengeDefinitions); + new ChallengerTrackingRoutes() + .configure( + challengers, + single_player_mode, + apiChallengesDocumentationDefn, + persistenceLayer, + thingifier, + challengeDefinitions); + new ChallengesRoutes() + .configure( + challengers, + single_player_mode, + apiChallengesDocumentationDefn, + challengeDefinitions); new HeartBeatRoutes().configure(apiChallengesDocumentationDefn); new AuthRoutes().configure(challengers, apiChallengesDocumentationDefn); @@ -102,9 +121,8 @@ public ChallengeRouteHandler configureRoutes() { new MirrorRoutes().configure(mirrorModeDocumentationDefn, guiTemplates); // Simulation routes should not show - simulationRoutes = new SimulationRoutes( - guiTemplates, - config.getSimulationRepositoryConfig()); + simulationRoutes = + new SimulationRoutes(guiTemplates, config.getSimulationRepositoryConfig()); simulationRoutes.configure(); new SimpleApiRoutes(guiTemplates).configure(); @@ -117,21 +135,25 @@ public void addHooks(final ThingifierHttpApiRoutings restServer) { // TODO: this is wrong - rethink this - we need SparkLevel, InternalHttp level (pre-post // these hooks are registered at a spark before and after level so run on every request, // regardless of thingifier used - this is wrong - restServer.registerInternalHttpResponseHook(new ChallengerInternalHTTPResponseHook(challengers)); - restServer.registerInternalHttpRequestHook(new ChallengerInternalHTTPRequestHook(challengers)); + restServer.registerInternalHttpResponseHook( + new ChallengerInternalHTTPResponseHook(challengers)); + restServer.registerInternalHttpRequestHook( + new ChallengerInternalHTTPRequestHook(challengers)); - // add hooks at the API bridge pre and post level so they only work in the specific thingifier + // add hooks at the API bridge pre and post level so they only work in the specific + // thingifier restServer.registerHttpApiRequestHook(new ChallengerApiRequestHook(challengers)); - restServer.registerHttpApiResponseHook(new ChallengerApiResponseHook(challengers, thingifier)); + restServer.registerHttpApiResponseHook( + new ChallengerApiResponseHook(challengers, thingifier)); } public void setupGui(DefaultGUIHTML guiManagement) { this.guiTemplates = guiManagement; - new ChallengerWebGUI(guiManagement, guiStayAlive).setup(challengers, challengeDefinitions, - persistenceLayer, single_player_mode); + new ChallengerWebGUI(guiManagement, guiStayAlive) + .setup(challengers, challengeDefinitions, persistenceLayer, single_player_mode); } - public Challengers getChallengers(){ + public Challengers getChallengers() { return challengers; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerAuthData.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerAuthData.java index 0afa4944..d729ea8c 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerAuthData.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/ChallengerAuthData.java @@ -2,12 +2,11 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; -import uk.co.compendiumdev.challenge.challengers.Challengers; - import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import uk.co.compendiumdev.challenge.challengers.Challengers; public class ChallengerAuthData { @@ -21,7 +20,7 @@ public class ChallengerAuthData { private ChallengerState state; - public ChallengerAuthData(Collection definedChallenges){ + public ChallengerAuthData(Collection definedChallenges) { this.xChallenger = UUID.randomUUID().toString(); this.xAuthToken = UUID.randomUUID().toString(); this.expiresin = 600000; // 10 * 60 * 1000; // 10 minutes @@ -35,7 +34,7 @@ public ChallengerAuthData(Collection definedChallenges){ private void resetChallengesStatus(Collection definedChallenges) { challengeStatus = new HashMap<>(); // this should only be challenges defined - for(CHALLENGE challenge : definedChallenges){ + for (CHALLENGE challenge : definedChallenges) { challengeStatus.put(challenge, false); } } @@ -53,12 +52,12 @@ public String getNote() { public void setNote(final String note) { touch(); int maxLen = 100; - if(note == null){ + if (note == null) { throw new RuntimeException("No Note Provided"); } - if(note.length()<=maxLen){ - secretNote=note; - }else { + if (note.length() <= maxLen) { + secretNote = note; + } else { secretNote = note.substring(0, maxLen); } } @@ -74,7 +73,7 @@ public long expiresAt() { public void touch() { lastAccessed = System.currentTimeMillis(); expiresin = expiresin + extratime; - if(expiresin > 600000){ + if (expiresin > 600000) { expiresin = 600000; } } @@ -85,7 +84,7 @@ public String getXChallenger() { public Boolean statusOfChallenge(final CHALLENGE challenge) { Boolean status = challengeStatus.get(challenge); - if(status==null){ + if (status == null) { return false; } return challengeStatus.get(challenge); @@ -93,7 +92,7 @@ public Boolean statusOfChallenge(final CHALLENGE challenge) { public void pass(final CHALLENGE id) { // only update challenge if configured for it - if(challengeStatus.containsKey(id)){ + if (challengeStatus.containsKey(id)) { challengeStatus.put(id, true); } } @@ -106,12 +105,13 @@ public void setState(ChallengerState challengerState) { this.state = challengerState; } - public ChallengerAuthData fromData(ChallengerAuthData data, Collection definedChallenges){ + public ChallengerAuthData fromData( + ChallengerAuthData data, Collection definedChallenges) { // set from data but do not fully trust data setNote(data.secretNote); // only allow setting the uuid if we are not in single player mode - if(!xChallenger.equals(Challengers.SINGLE_PLAYER_GUID)) { + if (!xChallenger.equals(Challengers.SINGLE_PLAYER_GUID)) { try { setXChallengerGUID(UUID.fromString(data.getXChallenger()).toString()); } catch (Exception e) { @@ -121,7 +121,7 @@ public ChallengerAuthData fromData(ChallengerAuthData data, Collection getRouteRegistrar; public IndexNowRouteHandler() { - this(System.getenv(INDEX_NOW_KEY_LOCATION), - System.getenv(INDEX_NOW_KEY), - Spark::get); + this(System.getenv(INDEX_NOW_KEY_LOCATION), System.getenv(INDEX_NOW_KEY), Spark::get); } - IndexNowRouteHandler(final String keyLocation, - final String key, - final BiConsumer getRouteRegistrar) { + IndexNowRouteHandler( + final String keyLocation, + final String key, + final BiConsumer getRouteRegistrar) { this.keyLocation = normalize(keyLocation); this.key = normalize(key); this.getRouteRegistrar = getRouteRegistrar; @@ -35,16 +33,19 @@ public IndexNowRouteHandler() { public boolean configureRoutes() { if (!isConfigurationValid()) { - logger.info("IndexNow endpoint not enabled because environment variables are missing or invalid"); + logger.info( + "IndexNow endpoint not enabled because environment variables are missing or invalid"); return false; } - getRouteRegistrar.accept(keyLocation, (request, response) -> { - if (response != null) { - response.type("text/plain; charset=utf-8"); - } - return key; - }); + getRouteRegistrar.accept( + keyLocation, + (request, response) -> { + if (response != null) { + response.type("text/plain; charset=utf-8"); + } + return key; + }); logger.info("IndexNow endpoint enabled at {}", keyLocation); return true; @@ -63,11 +64,11 @@ static boolean isValidLocation(final String location) { return false; } - return !location.contains("*") && - !location.contains("?") && - !location.contains("#") && - !location.contains(" ") && - !location.contains("\\"); + return !location.contains("*") + && !location.contains("?") + && !location.contains("#") + && !location.contains(" ") + && !location.contains("\\"); } private static String normalize(final String value) { diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModel.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModel.java index 8f2836bc..8ce3b494 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModel.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeApiModel.java @@ -1,15 +1,15 @@ package uk.co.compendiumdev.challenge.apimodel; +import static uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType.STRING; + +import uk.co.compendiumdev.thingifier.Thingifier; import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; -import uk.co.compendiumdev.thingifier.Thingifier; -import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.Field; +import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.core.domain.definitions.validation.VRule; import uk.co.compendiumdev.thingifier.core.repository.ThingRepositoryProvider; -import static uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType.STRING; - public class ChallengeApiModel { public Thingifier get() { return get(new EntityRelModel()); @@ -26,29 +26,24 @@ public Thingifier get(final EntityRelModel entityRelModel) { para.append("A Simple todo list"); - todoList.setDocumentation("Simple Todo List", para.toString()); // can create a maximum of 20 todos in the challenge todos list EntityDefinition todo = todoList.defineThing("todo", "todos", 20); todo.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); todo.addFields( - Field.is("title", STRING). - withExample("A title"). - makeMandatory(). - withValidation( - VRule.notEmpty(), - VRule.maximumLength(50)). - withDescription("The title of the TODO Item"), - Field.is("doneStatus", FieldType.BOOLEAN). - withDefaultValue("false"). - withDescription("Status to track the todo completion"), - Field.is("description", STRING). - withExample("my description"). - withValidation(VRule.maximumLength(200)). - withDescription("Longer description of the todo") - ); - + Field.is("title", STRING) + .withExample("A title") + .makeMandatory() + .withValidation(VRule.notEmpty(), VRule.maximumLength(50)) + .withDescription("The title of the TODO Item"), + Field.is("doneStatus", FieldType.BOOLEAN) + .withDefaultValue("false") + .withDescription("Status to track the todo completion"), + Field.is("description", STRING) + .withExample("my description") + .withValidation(VRule.maximumLength(200)) + .withDescription("Longer description of the todo")); todoList.setDataGenerator(new TodoAPIDataPopulator()); todoList.generateData(EntityRelModel.DEFAULT_DATABASE_NAME); diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java index a1a231dc..78696f76 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/ChallengeThingifier.java @@ -18,11 +18,11 @@ public class ChallengeThingifier { public EntityDefinition challengeDefn; private ChallengeDefinitions challengeDefinitions; - public ChallengeThingifier(){ + public ChallengeThingifier() { createThingifier(); } - private void createThingifier(){ + private void createThingifier() { // fake the data storage this.challengeThingifier = new Thingifier(); @@ -32,55 +32,59 @@ private void createThingifier(){ this.challengeThingifier.apiConfig().setApiToAllowRobotsIndexingResponses(false); this.challengeThingifier.apiConfig().setReturnSingleGetItemsAsCollection(true); - this.challengeDefn = this.challengeThingifier.defineThing( - "challenge", "challenges"); + this.challengeDefn = this.challengeThingifier.defineThing("challenge", "challenges"); this.challengeDefn.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT)); this.challengeDefn.addFields( - - Field.is("name", FieldType.STRING).withDefaultValue("") - .withDescription("title to describe the challenge"), - Field.is("description", FieldType.STRING).withDefaultValue("") + Field.is("name", FieldType.STRING) + .withDefaultValue("") + .withDescription("title to describe the challenge"), + Field.is("description", FieldType.STRING) + .withDefaultValue("") .withDescription("description of the challenge"), - Field.is("status", FieldType.BOOLEAN).withDefaultValue("false") - .withDescription("status to track if challenges is completed") - ); + Field.is("status", FieldType.BOOLEAN) + .withDefaultValue("false") + .withDescription("status to track if challenges is completed")); } public void populateThingifierFrom(ChallengeDefinitions challengeDefinitions) { this.challengeDefinitions = challengeDefinitions; // create all instances from the definitions, then when we want to // set all the status codes to the specific challenger status - ThingRepository repository = challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + ThingRepository repository = + challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); for (ChallengeDefinitionData challenge : challengeDefinitions.getChallenges()) { repository.createInstance( - EntityInstanceDraft.forEntity(challengeDefn). - withProtectedField("id", challenge.id). - withField("name", challenge.name). - withField("description", challenge.description)); + EntityInstanceDraft.forEntity(challengeDefn) + .withProtectedField("id", challenge.id) + .withField("name", challenge.name) + .withField("description", challenge.description)); } } - public void populateThingifierFromStatus(ChallengerAuthData challenger){ + public void populateThingifierFromStatus(ChallengerAuthData challenger) { ChallengerAuthData challengerToUse = challenger; - if(challenger == null){ + if (challenger == null) { // create one just to show no progress challengerToUse = new ChallengerAuthData(challengeDefinitions.getDefinedChallenges()); } - ThingRepository repository = challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); + ThingRepository repository = + challengeThingifier.getRepository(EntityRelModel.DEFAULT_DATABASE_NAME); for (ChallengeDefinitionData challenge : challengeDefinitions.getChallenges()) { final EntityInstance instance = repository.findInstanceByFieldNameAndValue(challengeDefn, "id", challenge.id); repository.patchInstance( instance, - EntityInstanceDraft.forEntity(challengeDefn). - withField( + EntityInstanceDraft.forEntity(challengeDefn) + .withField( "status", - challengerToUse.statusOfChallenge( - challengeDefinitions.getChallenge(challenge.name)). - toString())); + challengerToUse + .statusOfChallenge( + challengeDefinitions.getChallenge( + challenge.name)) + .toString())); } } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java index d7365179..c101b245 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/apimodel/TodoAPIDataPopulator.java @@ -10,21 +10,22 @@ public class TodoAPIDataPopulator implements RepositoryDataPopulator { @Override public void populate(final ERSchema schema, final ThingRepository repository) { - String [] todos={ - "scan paperwork", - "file paperwork", - "process payments", - "escalate late payments", - "pay invoices", - "process payroll", - "train staff", - "schedule meeting", - "tidy meeting room", - "install webcam"}; + String[] todos = { + "scan paperwork", + "file paperwork", + "process payments", + "escalate late payments", + "pay invoices", + "process payroll", + "train staff", + "schedule meeting", + "tidy meeting room", + "install webcam" + }; EntityDefinition todo = schema.getEntityDefinitionNamed("todo"); - for(String todoItem : todos){ + for (String todoItem : todos) { repository.createInstance( EntityInstanceDraft.forEntity(todo).withField("title", todoItem)); } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiRequestHook.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiRequestHook.java index c488a202..9f3392fd 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiRequestHook.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiRequestHook.java @@ -1,5 +1,8 @@ package uk.co.compendiumdev.challenge.challengehooks; +import static uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi.HTTP_SESSION_HEADER_NAME; + +import java.util.List; import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.challengers.Challengers; @@ -10,36 +13,35 @@ import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.application.httpapimessagehooks.HttpApiRequestHook; -import java.util.List; - -import static uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi.HTTP_SESSION_HEADER_NAME; - public class ChallengerApiRequestHook implements HttpApiRequestHook { private final Challengers challengers; - public ChallengerApiRequestHook(Challengers challengers){ + public ChallengerApiRequestHook(Challengers challengers) { this.challengers = challengers; } @Override public HttpApiResponse run(final HttpApiRequest request, final ThingifierApiConfig config) { - ChallengerAuthData challenger = challengers.getChallenger(request.getHeader("X-CHALLENGER")); - if(challenger==null){ + ChallengerAuthData challenger = + challengers.getChallenger(request.getHeader("X-CHALLENGER")); + if (challenger == null) { // if there is no x-challenger and we are in multi-player mode then do not allow any // POST, DELETE, PUT, PATCH through to the API as this would amend the default database - if(challengers.isMultiPlayerMode()){ - if( - request.getVerb().equals(HttpApiRequest.VERB.POST) || - request.getVerb().equals(HttpApiRequest.VERB.PUT) || - request.getVerb().equals(HttpApiRequest.VERB.PATCH) || - request.getVerb().equals(HttpApiRequest.VERB.DELETE) - ){ + if (challengers.isMultiPlayerMode()) { + if (request.getVerb().equals(HttpApiRequest.VERB.POST) + || request.getVerb().equals(HttpApiRequest.VERB.PUT) + || request.getVerb().equals(HttpApiRequest.VERB.PATCH) + || request.getVerb().equals(HttpApiRequest.VERB.DELETE)) { return new HttpApiResponse( request.getHeaders(), - new ApiResponse(401, true, List.of("Cannot amend details. Missing a valid X-CHALLENGER header.")), + new ApiResponse( + 401, + true, + List.of( + "Cannot amend details. Missing a valid X-CHALLENGER header.")), new JsonThing(challengers.getApiConfig().jsonOutput()), challengers.getApiConfig()); } @@ -58,16 +60,16 @@ public HttpApiResponse run(final HttpApiRequest request, final ThingifierApiConf // add challenger guid as session id to request request.addHeader(HTTP_SESSION_HEADER_NAME, challenger.getXChallenger()); - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().size()==0){ + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().size() == 0) { challengers.pass(challenger, CHALLENGE.GET_TODOS); } - if(request.getVerb() == HttpApiRequest.VERB.HEAD && - request.getPath().contentEquals("todos") && - request.getQueryParams().size()==0){ - challengers.pass(challenger,CHALLENGE.GET_HEAD_TODOS); + if (request.getVerb() == HttpApiRequest.VERB.HEAD + && request.getPath().contentEquals("todos") + && request.getQueryParams().size() == 0) { + challengers.pass(challenger, CHALLENGE.GET_HEAD_TODOS); } return null; diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java index 75ed6a33..05e4839e 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerApiResponseHook.java @@ -1,24 +1,22 @@ package uk.co.compendiumdev.challenge.challengehooks; +import java.util.Collection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.challengers.Challengers; import uk.co.compendiumdev.thingifier.Thingifier; -import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.ContentTypeHeaderParser; -import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.AcceptHeaderParser; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; +import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.AcceptHeaderParser; +import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.ContentTypeHeaderParser; import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.application.httpapimessagehooks.HttpApiResponseHook; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstance; import uk.co.compendiumdev.thingifier.core.repository.ThingRepository; -import java.util.Collection; - - public class ChallengerApiResponseHook implements HttpApiResponseHook { Logger logger = LoggerFactory.getLogger(ChallengerApiResponseHook.class); @@ -32,263 +30,260 @@ public ChallengerApiResponseHook(final Challengers challengers, Thingifier thing } @Override - public HttpApiResponse run(final HttpApiRequest request, - final HttpApiResponse response, - final ThingifierApiConfig config) { + public HttpApiResponse run( + final HttpApiRequest request, + final HttpApiResponse response, + final ThingifierApiConfig config) { - ChallengerAuthData challenger = challengers.getChallenger(request.getHeader("X-CHALLENGER")); + ChallengerAuthData challenger = + challengers.getChallenger(request.getHeader("X-CHALLENGER")); - if(challenger==null){ + if (challenger == null) { // cannot track challenges return null; } // READ - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().matches("todos/.*") && - response.getStatusCode()==200){ + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 200) { challengers.pass(challenger, CHALLENGE.GET_TODO); } - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().matches("todos/.*") && - response.getStatusCode()==404){ - challengers.pass(challenger,CHALLENGE.GET_TODO_404); + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 404) { + challengers.pass(challenger, CHALLENGE.GET_TODO_404); } final AcceptHeaderParser acceptParser = new AcceptHeaderParser(request.getHeader("accept")); - final ContentTypeHeaderParser contentTypeParser = new ContentTypeHeaderParser(request.getHeader("content-type")); - - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().isEmpty() && - acceptParser.hasAskedForXML() && - response.getType().contentEquals("application/xml") && - response.getStatusCode()==200 - ){ - challengers.pass(challenger,CHALLENGE.GET_ACCEPT_XML); + final ContentTypeHeaderParser contentTypeParser = + new ContentTypeHeaderParser(request.getHeader("content-type")); + + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().isEmpty() + && acceptParser.hasAskedForXML() + && response.getType().contentEquals("application/xml") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.GET_ACCEPT_XML); } - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().isEmpty() && - acceptParser.hasAskedForJSON() && - response.getType().contentEquals("application/json") && - response.getStatusCode()==200 - ){ - challengers.pass(challenger,CHALLENGE.GET_ACCEPT_JSON); + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().isEmpty() + && acceptParser.hasAskedForJSON() + && response.getType().contentEquals("application/json") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.GET_ACCEPT_JSON); } - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().isEmpty() && - acceptParser.missingAcceptHeader() && - response.getType().contentEquals("application/json") && - response.getStatusCode()==200 - ){ - challengers.pass(challenger,CHALLENGE.GET_JSON_BY_DEFAULT_NO_ACCEPT); + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().isEmpty() + && acceptParser.missingAcceptHeader() + && response.getType().contentEquals("application/json") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.GET_JSON_BY_DEFAULT_NO_ACCEPT); } - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().isEmpty() && - !acceptParser.isSupportedHeader() && - response.getStatusCode()==406 - ){ - challengers.pass(challenger,CHALLENGE.GET_UNSUPPORTED_ACCEPT_406); + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().isEmpty() + && !acceptParser.isSupportedHeader() + && response.getStatusCode() == 406) { + challengers.pass(challenger, CHALLENGE.GET_UNSUPPORTED_ACCEPT_406); } - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().isEmpty() && - acceptParser.hasAskedForANY() && - response.getType().contentEquals("application/json") && - response.getStatusCode()==200 - ){ - challengers.pass(challenger,CHALLENGE.GET_ACCEPT_ANY_DEFAULT_JSON); + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().isEmpty() + && acceptParser.hasAskedForANY() + && response.getType().contentEquals("application/json") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.GET_ACCEPT_ANY_DEFAULT_JSON); } - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().isEmpty() && - acceptParser.hasAskedForXML() && - acceptParser.hasAskedForJSON() && - acceptParser.hasAPreferenceForXml() && - response.getType().contentEquals("application/xml") && - response.getStatusCode()==200 - ){ - challengers.pass(challenger,CHALLENGE.GET_ACCEPT_XML_PREFERRED); + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().isEmpty() + && acceptParser.hasAskedForXML() + && acceptParser.hasAskedForJSON() + && acceptParser.hasAPreferenceForXml() + && response.getType().contentEquals("application/xml") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.GET_ACCEPT_XML_PREFERRED); } - - if(request.getVerb() == HttpApiRequest.VERB.GET && - request.getPath().contentEquals("todos") && - request.getQueryParams().containsKey("doneStatus") && - request.getQueryParams().get("doneStatus").contentEquals("true") && - response.getStatusCode()==200){ + if (request.getVerb() == HttpApiRequest.VERB.GET + && request.getPath().contentEquals("todos") + && request.getQueryParams().containsKey("doneStatus") + && request.getQueryParams().get("doneStatus").contentEquals("true") + && response.getStatusCode() == 200) { // only pass if there are done and not done todos final EntityInstance aDoneThing = findTodoByField(challenger, "doneStatus", "true"); final EntityInstance aNotDoneThing = findTodoByField(challenger, "doneStatus", "false"); - if(aDoneThing!=null && aNotDoneThing!=null) { - challengers.pass(challenger,CHALLENGE.GET_TODOS_FILTERED); + if (aDoneThing != null && aNotDoneThing != null) { + challengers.pass(challenger, CHALLENGE.GET_TODOS_FILTERED); } } // CREATE - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - response.getStatusCode()==201){ - challengers.pass(challenger,CHALLENGE.POST_TODOS); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && response.getStatusCode() == 201) { + challengers.pass(challenger, CHALLENGE.POST_TODOS); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos/.*") && - response.getStatusCode()==404){ - for(String errorMessage : response.apiResponse().getErrorMessages()){ - if(errorMessage.startsWith("No such todo entity instance with id ==")){ + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 404) { + for (String errorMessage : response.apiResponse().getErrorMessages()) { + if (errorMessage.startsWith("No such todo entity instance with id ==")) { challengers.pass(challenger, CHALLENGE.POST_TODOS_404); } } } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - response.getStatusCode()==201){ + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && response.getStatusCode() == 201) { try { String location = response.getHeaders().get("Location"); String[] locationParts = location.split("/"); - if(locationParts.length>2){ + if (locationParts.length > 2) { // to check it is an int Integer.parseInt(locationParts[2]); EntityInstance aTodo = findTodoByIdentifier(challenger, locationParts[2]); - if(aTodo != null && - aTodo.getFieldValue("title").asString().length() == 50 && - aTodo.getFieldValue("description").asString().length() == 200 - ){ - challengers.pass(challenger, CHALLENGE.POST_MAX_OUT_TITLE_DESCRIPTION_LENGTH); + if (aTodo != null + && aTodo.getFieldValue("title").asString().length() == 50 + && aTodo.getFieldValue("description").asString().length() == 200) { + challengers.pass( + challenger, CHALLENGE.POST_MAX_OUT_TITLE_DESCRIPTION_LENGTH); } } - }catch(Exception e){ - logger.warn("Error checking post todos 201 for max length ", e); + } catch (Exception e) { + logger.warn("Error checking post todos 201 for max length ", e); } - } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - contentTypeParser.isXML() && - response.getType().contentEquals("application/xml") && - response.getStatusCode()==201){ - challengers.pass(challenger,CHALLENGE.POST_CREATE_XML); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && contentTypeParser.isXML() + && response.getType().contentEquals("application/xml") + && response.getStatusCode() == 201) { + challengers.pass(challenger, CHALLENGE.POST_CREATE_XML); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - contentTypeParser.isJSON() && - acceptParser.hasAskedForJSON() && - response.getType().contentEquals("application/json") && - response.getStatusCode()==201){ - challengers.pass(challenger,CHALLENGE.POST_CREATE_JSON); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && contentTypeParser.isJSON() + && acceptParser.hasAskedForJSON() + && response.getType().contentEquals("application/json") + && response.getStatusCode() == 201) { + challengers.pass(challenger, CHALLENGE.POST_CREATE_JSON); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - contentTypeParser.isJSON() && - response.getType().contentEquals("application/xml") && - response.getStatusCode()==201){ - challengers.pass(challenger,CHALLENGE.POST_CREATE_JSON_ACCEPT_XML); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && contentTypeParser.isJSON() + && response.getType().contentEquals("application/xml") + && response.getStatusCode() == 201) { + challengers.pass(challenger, CHALLENGE.POST_CREATE_JSON_ACCEPT_XML); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - acceptParser.hasAskedForJSON() && - contentTypeParser.isXML() && - response.getType().contentEquals("application/json") && - response.getStatusCode()==201){ - challengers.pass(challenger,CHALLENGE.POST_CREATE_XML_ACCEPT_JSON); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && acceptParser.hasAskedForJSON() + && contentTypeParser.isXML() + && response.getType().contentEquals("application/json") + && response.getStatusCode() == 201) { + challengers.pass(challenger, CHALLENGE.POST_CREATE_XML_ACCEPT_JSON); } - - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - response.getStatusCode()==400 && - collate(response.apiResponse().getErrorMessages()).contains( - "Failed Validation: doneStatus should be BOOLEAN")){ - challengers.pass(challenger,CHALLENGE.POST_TODOS_BAD_DONE_STATUS); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && response.getStatusCode() == 400 + && collate(response.apiResponse().getErrorMessages()) + .contains("Failed Validation: doneStatus should be BOOLEAN")) { + challengers.pass(challenger, CHALLENGE.POST_TODOS_BAD_DONE_STATUS); } - if(request.getVerb() == HttpApiRequest.VERB.POST && + if (request.getVerb() == HttpApiRequest.VERB.POST + && // trap when creating or amending - request.getPath().startsWith("todo") && - response.getStatusCode()==400 && - collate(response.apiResponse().getErrorMessages()).contains( - "Failed Validation: Maximum allowable length exceeded for title - maximum allowed is 50")){ - challengers.pass(challenger,CHALLENGE.POST_TODOS_TOO_LONG_TITLE_LENGTH); + request.getPath().startsWith("todo") + && response.getStatusCode() == 400 + && collate(response.apiResponse().getErrorMessages()) + .contains( + "Failed Validation: Maximum allowable length exceeded for title - maximum allowed is 50")) { + challengers.pass(challenger, CHALLENGE.POST_TODOS_TOO_LONG_TITLE_LENGTH); } - if(request.getVerb() == HttpApiRequest.VERB.POST && + if (request.getVerb() == HttpApiRequest.VERB.POST + && // trap when creating or amending - request.getPath().startsWith("todo") && - response.getStatusCode()==400 && - collate(response.apiResponse().getErrorMessages()).contains( - "Failed Validation: Maximum allowable length exceeded for description - maximum allowed is 200")){ - challengers.pass(challenger,CHALLENGE.POST_TODOS_TOO_LONG_DESCRIPTION_LENGTH); + request.getPath().startsWith("todo") + && response.getStatusCode() == 400 + && collate(response.apiResponse().getErrorMessages()) + .contains( + "Failed Validation: Maximum allowable length exceeded for description - maximum allowed is 200")) { + challengers.pass(challenger, CHALLENGE.POST_TODOS_TOO_LONG_DESCRIPTION_LENGTH); } // POST to create too many todos - if (request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().contentEquals("todos") && - response.getStatusCode() == 400 && - collate(response.apiResponse().getErrorMessages()).contains("ERROR: Cannot add instance, maximum limit of 20 reached") - ) { + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().contentEquals("todos") + && response.getStatusCode() == 400 + && collate(response.apiResponse().getErrorMessages()) + .contains("ERROR: Cannot add instance, maximum limit of 20 reached")) { challengers.pass(challenger, CHALLENGE.POST_ALL_TODOS); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - response.getStatusCode()==415){ - challengers.pass(challenger,CHALLENGE.POST_TODOS_415); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && response.getStatusCode() == 415) { + challengers.pass(challenger, CHALLENGE.POST_TODOS_415); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - response.getStatusCode()==413 && - collate(response.apiResponse().getErrorMessages()).contains("Error: Request body too large, max allowed is 5000 bytes") - ){ - challengers.pass(challenger,CHALLENGE.POST_TODOS_TOO_LONG_PAYLOAD_SIZE); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && response.getStatusCode() == 413 + && collate(response.apiResponse().getErrorMessages()) + .contains("Error: Request body too large, max allowed is 5000 bytes")) { + challengers.pass(challenger, CHALLENGE.POST_TODOS_TOO_LONG_PAYLOAD_SIZE); } - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos") && - response.getStatusCode()==400 && - collate(response.apiResponse().getErrorMessages()).contains("Could not find field:") - ){ - challengers.pass(challenger,CHALLENGE.POST_TODOS_INVALID_EXTRA_FIELD); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos") + && response.getStatusCode() == 400 + && collate(response.apiResponse().getErrorMessages()) + .contains("Could not find field:")) { + challengers.pass(challenger, CHALLENGE.POST_TODOS_INVALID_EXTRA_FIELD); } // UPDATE - if(request.getVerb() == HttpApiRequest.VERB.POST && - request.getPath().matches("todos/.*") && - response.getStatusCode()==200){ - challengers.pass(challenger,CHALLENGE.POST_UPDATE_TODO); + if (request.getVerb() == HttpApiRequest.VERB.POST + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.POST_UPDATE_TODO); } - // DELETE - if(request.getVerb() == HttpApiRequest.VERB.DELETE && - request.getPath().matches("todos/.*") && - response.getStatusCode()==200){ - challengers.pass(challenger,CHALLENGE.DELETE_A_TODO); + if (request.getVerb() == HttpApiRequest.VERB.DELETE + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 200) { + challengers.pass(challenger, CHALLENGE.DELETE_A_TODO); } - if(request.getVerb() == HttpApiRequest.VERB.DELETE && - request.getPath().matches("todos/.*") && - response.getStatusCode()==200 && - countTodos(challenger)==0){ - challengers.pass(challenger,CHALLENGE.DELETE_ALL_TODOS); + if (request.getVerb() == HttpApiRequest.VERB.DELETE + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 200 + && countTodos(challenger) == 0) { + challengers.pass(challenger, CHALLENGE.DELETE_ALL_TODOS); } // do not interfere with api and return null @@ -296,9 +291,7 @@ public HttpApiResponse run(final HttpApiRequest request, } private EntityInstance findTodoByField( - final ChallengerAuthData challenger, - final String fieldName, - final String fieldValue) { + final ChallengerAuthData challenger, final String fieldName, final String fieldValue) { EntityDefinition todo = todoDefinition(); ThingRepository repository = repositoryFor(challenger); if (todo == null || repository == null) { @@ -308,8 +301,7 @@ private EntityInstance findTodoByField( } private EntityInstance findTodoByIdentifier( - final ChallengerAuthData challenger, - final String identifier) { + final ChallengerAuthData challenger, final String identifier) { EntityDefinition todo = todoDefinition(); ThingRepository repository = repositoryFor(challenger); if (todo == null || repository == null) { @@ -335,9 +327,9 @@ private EntityDefinition todoDefinition() { return thingifier.getDefinitionNamed("todo"); } - String collate(Collection strings){ + String collate(Collection strings) { StringBuilder collated = new StringBuilder(); - for(String string : strings){ + for (String string : strings) { collated.append(string); collated.append(" "); } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPRequestHook.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPRequestHook.java index f55376d7..050c9000 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPRequestHook.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPRequestHook.java @@ -1,5 +1,8 @@ package uk.co.compendiumdev.challenge.challengehooks; +import static uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi.HTTP_SESSION_HEADER_NAME; + +import java.util.List; import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.challengers.Challengers; @@ -7,15 +10,11 @@ import uk.co.compendiumdev.thingifier.api.http.HttpApiResponse; import uk.co.compendiumdev.thingifier.application.sparkhttpmessageHooks.InternalHttpRequestHook; -import java.util.List; - -import static uk.co.compendiumdev.thingifier.api.http.ThingifierHttpApi.HTTP_SESSION_HEADER_NAME; - /* - This is an Internal HTTP Request because it covers functionality for endpoints that do not - go through the normal API process i.e. heartbeat, challenges, challenger - */ -public class ChallengerInternalHTTPRequestHook implements InternalHttpRequestHook { + This is an Internal HTTP Request because it covers functionality for endpoints that do not + go through the normal API process i.e. heartbeat, challenges, challenger +*/ +public class ChallengerInternalHTTPRequestHook implements InternalHttpRequestHook { private final Challengers challengers; public ChallengerInternalHTTPRequestHook(final Challengers challengers) { @@ -26,15 +25,18 @@ public ChallengerInternalHTTPRequestHook(final Challengers challengers) { public HttpApiResponse run(final HttpApiRequest request) { // TODO: fix hooks so that they only run on a specific thingifier basis. - // Until fixed so hooks only run on specific thingifiers, restrict this to Challenges API end points - List validEndpointPrefixesToRunAgainst = List.of("challenger", "todo", "todos", "challenges", "heartbeat","secret"); + // Until fixed so hooks only run on specific thingifiers, restrict this to Challenges API + // end points + List validEndpointPrefixesToRunAgainst = + List.of("challenger", "todo", "todos", "challenges", "heartbeat", "secret"); String[] pathSegments = request.getPath().split("/"); - if(!validEndpointPrefixesToRunAgainst.contains(pathSegments[0])){ + if (!validEndpointPrefixesToRunAgainst.contains(pathSegments[0])) { return null; } - ChallengerAuthData challenger = challengers.getChallenger(request.getHeader("X-CHALLENGER")); - if(challenger==null){ + ChallengerAuthData challenger = + challengers.getChallenger(request.getHeader("X-CHALLENGER")); + if (challenger == null) { // cannot track challenges return null; } @@ -48,52 +50,49 @@ public HttpApiResponse run(final HttpApiRequest request) { HttpApiRequest.VERB method = request.getVerb(); String path = request.getPath(); - if(method == HttpApiRequest.VERB.GET && - path.equals("challenges")){ + if (method == HttpApiRequest.VERB.GET && path.equals("challenges")) { challengers.pass(challenger, CHALLENGE.GET_CHALLENGES); } - if(method == HttpApiRequest.VERB.GET && - path.equals("heartbeat")){ + if (method == HttpApiRequest.VERB.GET && path.equals("heartbeat")) { challengers.pass(challenger, CHALLENGE.GET_HEARTBEAT_204); } - if(method == HttpApiRequest.VERB.DELETE && - path.equals("heartbeat")){ - challengers.pass(challenger,CHALLENGE.DELETE_HEARTBEAT_405); + if (method == HttpApiRequest.VERB.DELETE && path.equals("heartbeat")) { + challengers.pass(challenger, CHALLENGE.DELETE_HEARTBEAT_405); } - if(method == HttpApiRequest.VERB.PATCH && - path.equals("heartbeat")){ - challengers.pass(challenger,CHALLENGE.PATCH_HEARTBEAT_500); + if (method == HttpApiRequest.VERB.PATCH && path.equals("heartbeat")) { + challengers.pass(challenger, CHALLENGE.PATCH_HEARTBEAT_500); } - if(method == HttpApiRequest.VERB.TRACE && - path.equals("heartbeat")){ - challengers.pass(challenger,CHALLENGE.TRACE_HEARTBEAT_501); + if (method == HttpApiRequest.VERB.TRACE && path.equals("heartbeat")) { + challengers.pass(challenger, CHALLENGE.TRACE_HEARTBEAT_501); } - if(method == HttpApiRequest.VERB.POST && - path.equals("heartbeat") && request.getHeader("x-http-method-override").equals("patch")){ - challengers.pass(challenger,CHALLENGE.OVERRIDE_PATCH_HEARTBEAT_500); + if (method == HttpApiRequest.VERB.POST + && path.equals("heartbeat") + && request.getHeader("x-http-method-override").equals("patch")) { + challengers.pass(challenger, CHALLENGE.OVERRIDE_PATCH_HEARTBEAT_500); } - if(method == HttpApiRequest.VERB.POST && - path.equals("heartbeat") && request.getHeader("x-http-method-override").equals("delete")){ - challengers.pass(challenger,CHALLENGE.OVERRIDE_DELETE_HEARTBEAT_405); + if (method == HttpApiRequest.VERB.POST + && path.equals("heartbeat") + && request.getHeader("x-http-method-override").equals("delete")) { + challengers.pass(challenger, CHALLENGE.OVERRIDE_DELETE_HEARTBEAT_405); } - if(method == HttpApiRequest.VERB.POST && - path.equals("heartbeat") && request.getHeader("x-http-method-override").equals("trace")){ - challengers.pass(challenger,CHALLENGE.OVERRIDE_TRACE_HEARTBEAT_501); + if (method == HttpApiRequest.VERB.POST + && path.equals("heartbeat") + && request.getHeader("x-http-method-override").equals("trace")) { + challengers.pass(challenger, CHALLENGE.OVERRIDE_TRACE_HEARTBEAT_501); } - if(method == HttpApiRequest.VERB.GET && - path.equals("challenger/" + challenger.getXChallenger())){ - challengers.pass(challenger,CHALLENGE.GET_RESTORABLE_CHALLENGER_PROGRESS_STATUS); + if (method == HttpApiRequest.VERB.GET + && path.equals("challenger/" + challenger.getXChallenger())) { + challengers.pass(challenger, CHALLENGE.GET_RESTORABLE_CHALLENGER_PROGRESS_STATUS); } return null; } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java index a363eb4d..4df7e132 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengehooks/ChallengerInternalHTTPResponseHook.java @@ -1,20 +1,18 @@ package uk.co.compendiumdev.challenge.challengehooks; -import uk.co.compendiumdev.challenge.ChallengerState; -import uk.co.compendiumdev.challenge.challengesrouting.XChallengerHeader; -import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.BearerAuthHeaderParser; +import static uk.co.compendiumdev.thingifier.api.http.HttpApiRequest.VERB.*; + +import java.util.List; import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerAuthData; +import uk.co.compendiumdev.challenge.ChallengerState; import uk.co.compendiumdev.challenge.challengers.Challengers; +import uk.co.compendiumdev.challenge.challengesrouting.XChallengerHeader; import uk.co.compendiumdev.thingifier.api.http.HttpApiRequest; +import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.BearerAuthHeaderParser; import uk.co.compendiumdev.thingifier.application.internalhttpconversion.InternalHttpResponse; import uk.co.compendiumdev.thingifier.application.sparkhttpmessageHooks.InternalHttpResponseHook; -import java.util.List; - -import static uk.co.compendiumdev.thingifier.api.http.HttpApiRequest.VERB.*; - - public class ChallengerInternalHTTPResponseHook implements InternalHttpResponseHook { private final Challengers challengers; @@ -23,85 +21,94 @@ public ChallengerInternalHTTPResponseHook(final Challengers challengers) { this.challengers = challengers; } - @Override public void run(final HttpApiRequest request, final InternalHttpResponse response) { // allow cross origin requests - // and swagger https://support.smartbear.com/swaggerhub/docs/en/edit-apis/cors-requirements-for--try-it-out-.html + // and swagger + // https://support.smartbear.com/swaggerhub/docs/en/edit-apis/cors-requirements-for--try-it-out-.html response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "*"); // this is necessary for swagger UI to show headers in the UI response.setHeader("Access-Control-Expose-Headers", "*"); - if (request.getVerb() == OPTIONS && request.getHeaders().headerExists("Access-Control-Allow-Methods")) { - response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Allow-Methods")); + if (request.getVerb() == OPTIONS + && request.getHeaders().headerExists("Access-Control-Allow-Methods")) { + response.setHeader( + "Access-Control-Allow-Methods", + request.getHeader("Access-Control-Allow-Methods")); } // TODO: fix hooks so that they only run on a specific thingifier basis. - // Until fixed so hooks only run on specific thingifiers, restrict this to Challenges API end points - List validEndpointPrefixesToRunAgainst = List.of("challenger", "todo", "todos", "challenges", "heartbeat","secret"); + // Until fixed so hooks only run on specific thingifiers, restrict this to Challenges API + // end points + List validEndpointPrefixesToRunAgainst = + List.of("challenger", "todo", "todos", "challenges", "heartbeat", "secret"); String[] pathSegments = request.getPath().split("/"); - if(!validEndpointPrefixesToRunAgainst.contains(pathSegments[0])){ + if (!validEndpointPrefixesToRunAgainst.contains(pathSegments[0])) { return; } -// boolean validEndpoint=false; -// for(String checkPrefix : validEndpointPrefixesToRunAgainst){ -// if(request.getPath().startsWith(checkPrefix)){ -// validEndpoint=true; -// break; -// } -// } -// if(!validEndpoint){ -// return; -// } - - ChallengerAuthData challenger = challengers.getChallenger(request.getHeader("X-CHALLENGER")); + // boolean validEndpoint=false; + // for(String checkPrefix : validEndpointPrefixesToRunAgainst){ + // if(request.getPath().startsWith(checkPrefix)){ + // validEndpoint=true; + // break; + // } + // } + // if(!validEndpoint){ + // return; + // } + + ChallengerAuthData challenger = + challengers.getChallenger(request.getHeader("X-CHALLENGER")); // we can complete a challenge while the user is null - creating the user - if (request.getVerb() == POST && - request.getPath().contentEquals("challenger") && - response.getStatusCode() == 201) { + if (request.getVerb() == POST + && request.getPath().contentEquals("challenger") + && response.getStatusCode() == 201) { // challenger did not exist so we need to find it to pass the challenge String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); - if (challenger != null && challenger.getState()== ChallengerState.NEW) { + if (challenger != null && challenger.getState() == ChallengerState.NEW) { challengers.pass(challenger, CHALLENGE.CREATE_NEW_CHALLENGER); } - if(challenger.getXChallenger().equals(Challengers.SINGLE_PLAYER_GUID)){ + if (challenger.getXChallenger().equals(Challengers.SINGLE_PLAYER_GUID)) { // single player pass is simulated challengers.pass(challenger, CHALLENGE.CREATE_NEW_CHALLENGER); } } - if (request.getVerb() == GET && - request.getPath().startsWith("challenger/") && - response.getStatusCode() == 200) { + if (request.getVerb() == GET + && request.getPath().startsWith("challenger/") + && response.getStatusCode() == 200) { String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); - if (challenger != null && challenger.getState()== ChallengerState.LOADED_FROM_PERSISTENCE) { + if (challenger != null + && challenger.getState() == ChallengerState.LOADED_FROM_PERSISTENCE) { challengers.pass(challenger, CHALLENGE.GET_RESTORE_EXISTING_CHALLENGER); } } - if (request.getVerb() == POST && - request.getPath().startsWith("challenger") && - response.getStatusCode() == 200) { + if (request.getVerb() == POST + && request.getPath().startsWith("challenger") + && response.getStatusCode() == 200) { String givenChallengerId = request.getHeader("X-Challenger"); String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); - if (challenger != null && givenChallengerId.equals(challengerId) && challenger.getState()== ChallengerState.LOADED_FROM_PERSISTENCE) { + if (challenger != null + && givenChallengerId.equals(challengerId) + && challenger.getState() == ChallengerState.LOADED_FROM_PERSISTENCE) { challengers.pass(challenger, CHALLENGE.POST_RESTORE_EXISTING_CHALLENGER); } } - if (request.getVerb() == PUT && - request.getPath().startsWith("challenger/") && - (response.getStatusCode() == 200)) { + if (request.getVerb() == PUT + && request.getPath().startsWith("challenger/") + && (response.getStatusCode() == 200)) { String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); @@ -110,9 +117,9 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons } } - if (request.getVerb() == PUT && - request.getPath().startsWith("challenger/") && - (response.getStatusCode() == 201)) { + if (request.getVerb() == PUT + && request.getPath().startsWith("challenger/") + && (response.getStatusCode() == 201)) { String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); @@ -121,9 +128,9 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons } } - if (request.getVerb() == GET && - request.getPath().startsWith("challenger/database/") && - (response.getStatusCode() == 200)) { + if (request.getVerb() == GET + && request.getPath().startsWith("challenger/database/") + && (response.getStatusCode() == 200)) { String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); @@ -132,9 +139,9 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons } } - if (request.getVerb() == PUT && - request.getPath().startsWith("challenger/database/") && - (response.getStatusCode() == 204)) { + if (request.getVerb() == PUT + && request.getPath().startsWith("challenger/database/") + && (response.getStatusCode() == 204)) { String challengerId = response.getHeader("X-Challenger"); challenger = challengers.getChallenger(challengerId); @@ -143,10 +150,11 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons } } - if (challenger == null) { - if (!request.getPath().contentEquals("challenger") && - !request.getPath().contains("mirror/r") // exclude mirror endpoints from adding a challenger + if (!request.getPath().contentEquals("challenger") + && !request.getPath() + .contains( + "mirror/r") // exclude mirror endpoints from adding a challenger ) { if (!response.getHeaders().headerExists("X-CHALLENGER")) { @@ -154,7 +162,8 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons } } // cannot track challenges - if (response.getStatusCode() == 404 && (response.getBody() == null || response.getBody().isEmpty())) { + if (response.getStatusCode() == 404 + && (response.getBody() == null || response.getBody().isEmpty())) { setResponseAs404(request, response); } return; @@ -162,141 +171,152 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons if (challenger != null) { if (!response.getHeaders().headerExists("X-CHALLENGER")) { - if(!request.getPath().contains("mirror/r")){ + if (!request.getPath().contains("mirror/r")) { // exclude mirror endpoints from adding a challenger XChallengerHeader.setResultHeaderBasedOnChallenger(response, challenger); } - } } // No endpoint defined so this 404 created by Spark routing - if (request.getVerb() == GET && - request.getPath().contentEquals("todo") && - response.getStatusCode() == 404) { + if (request.getVerb() == GET + && request.getPath().contentEquals("todo") + && response.getStatusCode() == 404) { challengers.pass(challenger, CHALLENGE.GET_TODOS_NOT_PLURAL_404); } - - if (request.getVerb() == OPTIONS && - request.getPath().contentEquals("todos") && - response.getStatusCode() == 204) { + if (request.getVerb() == OPTIONS + && request.getPath().contentEquals("todos") + && response.getStatusCode() == 204) { // hack for backwards compatibility with initial solutions response.setStatus(200); challengers.pass(challenger, CHALLENGE.OPTIONS_TODOS); } - if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 400){ - if(response.getBody().contains("Cannot create todo with PUT due to Auto fields id")){ - challengers.pass(challenger,CHALLENGE.PUT_TODOS_400); + if (request.getVerb() == PUT + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 400) { + if (response.getBody().contains("Cannot create todo with PUT due to Auto fields id")) { + challengers.pass(challenger, CHALLENGE.PUT_TODOS_400); } } - if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 200) { - if (request.getBody().toLowerCase().contains("donestatus") && request.getBody().toLowerCase().contains("description")){ + if (request.getVerb() == PUT + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 200) { + if (request.getBody().toLowerCase().contains("donestatus") + && request.getBody().toLowerCase().contains("description")) { challengers.pass(challenger, CHALLENGE.PUT_TODOS_FULL_200); } } - if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 400) { - if (response.getBody().contains("title : field is mandatory")){ + if (request.getVerb() == PUT + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 400) { + if (response.getBody().contains("title : field is mandatory")) { challengers.pass(challenger, CHALLENGE.PUT_TODOS_MISSING_TITLE_400); } } - if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 200){ - if(!request.getBody().toLowerCase().contains("donestatus") && !request.getBody().toLowerCase().contains("description")) { + if (request.getVerb() == PUT + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 200) { + if (!request.getBody().toLowerCase().contains("donestatus") + && !request.getBody().toLowerCase().contains("description")) { challengers.pass(challenger, CHALLENGE.PUT_TODOS_PARTIAL_200); } } - if(request.getVerb() == PUT && request.getPath().matches("todos/.*") && response.getStatusCode() == 400) { - if (response.getBody().contains("Can not amend id from")){ + if (request.getVerb() == PUT + && request.getPath().matches("todos/.*") + && response.getStatusCode() == 400) { + if (response.getBody().contains("Can not amend id from")) { challengers.pass(challenger, CHALLENGE.PUT_TODOS_400_NO_AMEND_ID); } } - if (request.getVerb() == POST && - request.getPath().contentEquals("secret/token") && - request.getHeaders().headerExists("Authorization") && - request.getHeader("Authorization").length() > 10 && - response.getStatusCode() == 401) { + if (request.getVerb() == POST + && request.getPath().contentEquals("secret/token") + && request.getHeaders().headerExists("Authorization") + && request.getHeader("Authorization").length() > 10 + && response.getStatusCode() == 401) { challengers.pass(challenger, CHALLENGE.CREATE_SECRET_TOKEN_401); } - if (request.getVerb() == POST && - request.getPath().contentEquals("secret/token") && - request.getHeaders().headerExists("Authorization") && - request.getHeader("Authorization").length() > 10 && - response.getStatusCode() == 201) { + if (request.getVerb() == POST + && request.getPath().contentEquals("secret/token") + && request.getHeaders().headerExists("Authorization") + && request.getHeader("Authorization").length() > 10 + && response.getStatusCode() == 201) { challengers.pass(challenger, CHALLENGE.CREATE_SECRET_TOKEN_201); } - if (request.getVerb() == GET && - request.getPath().contentEquals("secret/note") && - request.getHeaders().headerExists("X-AUTH-TOKEN") && - request.getHeader("X-AUTH-TOKEN").length() > 1 && - response.getStatusCode() == 403) { + if (request.getVerb() == GET + && request.getPath().contentEquals("secret/note") + && request.getHeaders().headerExists("X-AUTH-TOKEN") + && request.getHeader("X-AUTH-TOKEN").length() > 1 + && response.getStatusCode() == 403) { challengers.pass(challenger, CHALLENGE.GET_SECRET_NOTE_403); } - if (request.getVerb() == GET && - request.getPath().contentEquals("secret/note") && - !request.getHeaders().headerExists("X-AUTH-TOKEN") && - response.getStatusCode() == 401) { + if (request.getVerb() == GET + && request.getPath().contentEquals("secret/note") + && !request.getHeaders().headerExists("X-AUTH-TOKEN") + && response.getStatusCode() == 401) { challengers.pass(challenger, CHALLENGE.GET_SECRET_NOTE_401); } - if (request.getVerb() == POST && - request.getPath().contentEquals("secret/note") && - request.getHeaders().headerExists("X-AUTH-TOKEN") && - request.getHeader("X-AUTH-TOKEN").length() > 1 && - request.getBody().contains("\"note\"") && - response.getStatusCode() == 403) { + if (request.getVerb() == POST + && request.getPath().contentEquals("secret/note") + && request.getHeaders().headerExists("X-AUTH-TOKEN") + && request.getHeader("X-AUTH-TOKEN").length() > 1 + && request.getBody().contains("\"note\"") + && response.getStatusCode() == 403) { challengers.pass(challenger, CHALLENGE.POST_SECRET_NOTE_403); } - if (request.getVerb() == POST && - request.getPath().contentEquals("secret/note") && - !request.getHeaders().headerExists("X-AUTH-TOKEN") && - request.getBody().contains("\"note\"") && - response.getStatusCode() == 401) { + if (request.getVerb() == POST + && request.getPath().contentEquals("secret/note") + && !request.getHeaders().headerExists("X-AUTH-TOKEN") + && request.getBody().contains("\"note\"") + && response.getStatusCode() == 401) { challengers.pass(challenger, CHALLENGE.POST_SECRET_NOTE_401); } - if (request.getVerb() == POST && - request.getPath().contentEquals("secret/note") && - request.getHeaders().headerExists("X-AUTH-TOKEN") && - request.getBody().contains("\"note\"") && - response.getStatusCode() == 200) { + if (request.getVerb() == POST + && request.getPath().contentEquals("secret/note") + && request.getHeaders().headerExists("X-AUTH-TOKEN") + && request.getBody().contains("\"note\"") + && response.getStatusCode() == 200) { challengers.pass(challenger, CHALLENGE.POST_SECRET_NOTE_200); } - if (request.getVerb() == GET && - request.getPath().contentEquals("secret/note") && - request.getHeaders().headerExists("X-AUTH-TOKEN") && - response.getStatusCode() == 200) { + if (request.getVerb() == GET + && request.getPath().contentEquals("secret/note") + && request.getHeaders().headerExists("X-AUTH-TOKEN") + && response.getStatusCode() == 200) { challengers.pass(challenger, CHALLENGE.GET_SECRET_NOTE_200); } - if (request.getVerb() == GET && - request.getPath().contentEquals("secret/note") && - request.getHeaders().headerExists("Authorization") && - new BearerAuthHeaderParser(request.getHeader("Authorization")).isValid() && - response.getStatusCode() == 200) { + if (request.getVerb() == GET + && request.getPath().contentEquals("secret/note") + && request.getHeaders().headerExists("Authorization") + && new BearerAuthHeaderParser(request.getHeader("Authorization")).isValid() + && response.getStatusCode() == 200) { challengers.pass(challenger, CHALLENGE.GET_SECRET_NOTE_BEARER_200); } - if (request.getVerb() == POST && - request.getPath().contentEquals("secret/note") && - request.getHeaders().headerExists("Authorization") && - new BearerAuthHeaderParser(request.getHeader("Authorization")).isValid() && - request.getBody().contains("\"note\"") && - response.getStatusCode() == 200) { + if (request.getVerb() == POST + && request.getPath().contentEquals("secret/note") + && request.getHeaders().headerExists("Authorization") + && new BearerAuthHeaderParser(request.getHeader("Authorization")).isValid() + && request.getBody().contains("\"note\"") + && response.getStatusCode() == 200) { challengers.pass(challenger, CHALLENGE.POST_SECRET_NOTE_BEARER_200); } - if (response.getStatusCode() == 404 && (response.getBody() == null || response.getBody().isEmpty())) { + if (response.getStatusCode() == 404 + && (response.getBody() == null || response.getBody().isEmpty())) { setResponseAs404(request, response); } } @@ -304,7 +324,7 @@ public void run(final HttpApiRequest request, final InternalHttpResponse respons private void setResponseAs404(HttpApiRequest request, InternalHttpResponse response) { if (request.getAcceptHeader() != null && !request.getAcceptHeader().isEmpty()) { - if (request.getAcceptHeader().contains("html")){ + if (request.getAcceptHeader().contains("html")) { // treat as a GUI request and redirect response.setStatus(307); response.setHeader("Location", "/gui/404/" + request.getPath()); @@ -313,10 +333,11 @@ private void setResponseAs404(HttpApiRequest request, InternalHttpResponse respo if (request.getAcceptHeader().startsWith("application/")) { if (request.getAcceptHeader().endsWith("xml")) { response.setType("application/xml"); - response.setBody("404 resource Unknown"); + response.setBody( + "404 resource Unknown"); return; } - if(request.getAcceptHeader().endsWith("json")) { + if (request.getAcceptHeader().endsWith("json")) { response.setType("application/json"); response.setBody("{\"errorMessages\":[\"404 resource Unknown\"]}"); return; @@ -324,5 +345,4 @@ private void setResponseAs404(HttpApiRequest request, InternalHttpResponse respo } } } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java index 49d7ac20..3868fd87 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengers/Challengers.java @@ -1,5 +1,7 @@ package uk.co.compendiumdev.challenge.challengers; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.co.compendiumdev.challenge.CHALLENGE; @@ -10,9 +12,6 @@ import uk.co.compendiumdev.thingifier.apiconfig.ThingifierApiConfig; import uk.co.compendiumdev.thingifier.core.EntityRelModel; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - public class Challengers { Logger logger = LoggerFactory.getLogger(Challengers.class); @@ -21,19 +20,19 @@ public class Challengers { private boolean singlePlayerMode; Map authData; public ChallengerAuthData SINGLE_PLAYER; - public static final String SINGLE_PLAYER_GUID="rest-api-challenges-single-player"; + public static final String SINGLE_PLAYER_GUID = "rest-api-challenges-single-player"; public ChallengerAuthData DEFAULT_PLAYER_DATA; PersistenceLayer persistenceLayer; private ThingifierApiConfig apiConfig; private final Collection definedChallenges; - public Challengers(EntityRelModel erModel, Collection definedChallenges){ + public Challengers(EntityRelModel erModel, Collection definedChallenges) { authData = new ConcurrentHashMap<>(); this.definedChallenges = definedChallenges; SINGLE_PLAYER = new ChallengerAuthData(this.definedChallenges); SINGLE_PLAYER.setXChallengerGUID(SINGLE_PLAYER_GUID); DEFAULT_PLAYER_DATA = new ChallengerAuthData(this.definedChallenges); - this.singlePlayerMode=true; + this.singlePlayerMode = true; this.erModel = erModel; } @@ -41,54 +40,54 @@ public Collection getDefinedChallenges() { return definedChallenges; } - public void setMultiPlayerMode(){ - singlePlayerMode=false; + public void setMultiPlayerMode() { + singlePlayerMode = false; } - public boolean isMultiPlayerMode(){ + public boolean isMultiPlayerMode() { return !singlePlayerMode; } - public boolean isSinglePlayerMode(){ + public boolean isSinglePlayerMode() { return singlePlayerMode; } - public boolean inMemory(final String challengerGuid){ - if(challengerGuid == null || challengerGuid.trim().isEmpty()) { + public boolean inMemory(final String challengerGuid) { + if (challengerGuid == null || challengerGuid.trim().isEmpty()) { return false; } ChallengerAuthData challenger; - if(singlePlayerMode){ + if (singlePlayerMode) { challenger = SINGLE_PLAYER; - }else{ + } else { challenger = authData.get(challengerGuid); } return challenger != null; } public ChallengerAuthData getChallenger(final String challengerGuid) { - if(singlePlayerMode){ + if (singlePlayerMode) { return SINGLE_PLAYER; } - if(challengerGuid == null || challengerGuid.trim().isEmpty()) { + if (challengerGuid == null || challengerGuid.trim().isEmpty()) { return null; } ChallengerAuthData challenger = authData.get(challengerGuid); -// if(challengerGuid.equals(SINGLE_PLAYER_GUID)){ -// return SINGLE_PLAYER; -// } + // if(challengerGuid.equals(SINGLE_PLAYER_GUID)){ + // return SINGLE_PLAYER; + // } - if(challenger==null){ + if (challenger == null) { // we don't have challenger in memory, are they available in persistent store? if (persistenceLayer != null) { final PersistenceResponse response = persistenceLayer.tryToLoadChallenger(this, challengerGuid.trim()); - if(response.isSuccess()){ - if(authData.containsKey(challengerGuid)) { + if (response.isSuccess()) { + if (authData.containsKey(challengerGuid)) { challenger = authData.get(challengerGuid); challenger.setState(ChallengerState.LOADED_FROM_PERSISTENCE); } @@ -100,39 +99,42 @@ public ChallengerAuthData getChallenger(final String challengerGuid) { return challenger; } - public EntityRelModel getErModel(){ + public EntityRelModel getErModel() { return this.erModel; } public void purgeOldAuthData() { - if(singlePlayerMode){ + if (singlePlayerMode) { return; } List deleteMe = new ArrayList<>(); final long cutOffTime = System.currentTimeMillis(); - for(ChallengerAuthData data : authData.values()){ - if(data.expiresAt() < cutOffTime ){ + for (ChallengerAuthData data : authData.values()) { + if (data.expiresAt() < cutOffTime) { logger.warn("PURGING AUTH: {}", data.getXChallenger()); deleteMe.add(data.getXChallenger()); - }else{ - logger.info("PURGE: {} expires in {}", data.getXChallenger(), cutOffTime - data.expiresAt()); + } else { + logger.info( + "PURGE: {} expires in {}", + data.getXChallenger(), + cutOffTime - data.expiresAt()); } } - for(String deleteKey : deleteMe){ + for (String deleteKey : deleteMe) { delete(deleteKey); - if(erModel!=null){ - if(erModel.getDatabaseNames().contains(deleteKey)){ + if (erModel != null) { + if (erModel.getDatabaseNames().contains(deleteKey)) { logger.warn("DELETING DATABASE: {}", deleteKey); erModel.deleteInstanceDatabase(deleteKey); } } } - logger.info("CURRENT Challenger count: {}",authData.values().size()); - if(erModel!=null){ - logger.info("CURRENT database count: {}",erModel.getDatabaseNames().size()); + logger.info("CURRENT Challenger count: {}", authData.values().size()); + if (erModel != null) { + logger.info("CURRENT database count: {}", erModel.getDatabaseNames().size()); } } @@ -145,29 +147,28 @@ public ChallengerAuthData createNewChallenger() { public void put(final ChallengerAuthData challenger) { // todo: this should really check for single player mode and not just trust the GUID - if(challenger.getXChallenger().contentEquals(SINGLE_PLAYER_GUID)){ + if (challenger.getXChallenger().contentEquals(SINGLE_PLAYER_GUID)) { SINGLE_PLAYER = challenger; // we just loaded the single player session - }else { + } else { authData.put(challenger.getXChallenger(), challenger); } } - public void persistChallengerState(final ChallengerAuthData challenger){ + public void persistChallengerState(final ChallengerAuthData challenger) { if (persistenceLayer != null) { - String databaseName = challenger.getXChallenger(); + String databaseName = challenger.getXChallenger(); persistenceLayer.saveChallengerStatus( - challenger, - erModel.exportInstanceDataAsJson(databaseName)); + challenger, erModel.exportInstanceDataAsJson(databaseName)); } } public void pass(final ChallengerAuthData challenger, final CHALLENGE challengeId) { - if(challenger!=null) { + if (challenger != null) { // todo: possibly only update challenge status if not already set - //if(!challenger.statusOfChallenge(challengeId)) { - challenger.pass(challengeId); - persistChallengerState(challenger); - //} + // if(!challenger.statusOfChallenge(challengeId)) { + challenger.pass(challengeId); + persistChallengerState(challenger); + // } } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeData.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeData.java index 766557f8..54d8176f 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeData.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeData.java @@ -1,8 +1,5 @@ package uk.co.compendiumdev.challenge.challenges; -import java.util.ArrayList; -import java.util.List; - public class ChallengeData { public final String id; @@ -10,11 +7,10 @@ public class ChallengeData { public final String description; public Boolean status; - public ChallengeData(String id, String name, String description){ + public ChallengeData(String id, String name, String description) { this.id = id; - this.name=name; + this.name = name; this.description = description; this.status = false; } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitionData.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitionData.java index fb8e6ca3..1bd38e40 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitionData.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitionData.java @@ -12,19 +12,20 @@ public class ChallengeDefinitionData { public List solutions = new ArrayList<>(); public List hints = new ArrayList<>(); - public ChallengeDefinitionData(String id, String name, String description){ + public ChallengeDefinitionData(String id, String name, String description) { this.id = id; - this.name=name; + this.name = name; this.description = description; this.status = false; } - public void addSolutionLink(final String linkText, final String linkType, final String linkData) { + public void addSolutionLink( + final String linkText, final String linkType, final String linkData) { solutions.add(new ChallengeSolutionLink(linkText, linkType, linkData)); } public boolean hasSolutionLinks() { - return solutions.size()>0; + return solutions.size() > 0; } public void addSolutions(final List solutions) { @@ -36,7 +37,7 @@ public void addHint(final String hintText, final String hintLink) { } public boolean hasHints() { - return hints.size()>0; + return hints.size() > 0; } public void addHints(final List hints) { diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java index 5753f20f..5597131d 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeDefinitions.java @@ -1,13 +1,12 @@ package uk.co.compendiumdev.challenge.challenges; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.co.compendiumdev.challenge.CHALLENGE; import uk.co.compendiumdev.challenge.ChallengerConfig; import uk.co.compendiumdev.challenge.challenges.definitions.*; -import java.util.*; - public class ChallengeDefinitions { Logger logger = LoggerFactory.getLogger(ChallengeDefinitions.class); @@ -28,13 +27,11 @@ public Collection getChallengeSections() { return sections; } - - - private String renderChallengeNumber(int challengeOrder){ + private String renderChallengeNumber(int challengeOrder) { return String.format("%02d", challengeOrder); } - public ChallengeDefinitions(ChallengerConfig config){ + public ChallengeDefinitions(ChallengerConfig config) { challengeData = new HashMap<>(); orderedChallenges = new ArrayList<>(); @@ -43,259 +40,430 @@ public ChallengeDefinitions(ChallengerConfig config){ int challengeOrder = 1; ChallengeDefinitionData aChallenge; - - ChallengeSection getStarted = new ChallengeSection("Getting Started", - "If you want to track your challenge progress, in multi-user mode then you need to solve the challenges in this section to generate a unique ID that we can associate your progress with."); + ChallengeSection getStarted = + new ChallengeSection( + "Getting Started", + "If you want to track your challenge progress, in multi-user mode then you need to solve the challenges in this section to generate a unique ID that we can associate your progress with."); sections.add(getStarted); // create a challenger to persist challenge sessions - storeChallengeAs(CHALLENGE.CREATE_NEW_CHALLENGER, ChallengerChallenges.createChallenger201(challengeOrder++), getStarted); - - - - ChallengeSection firstChallenge = new ChallengeSection("First Real Challenge", - "For your first challenge, get the list of challenges. You'll be able to use this to see your progress in your API Client, as well as using the GUI."); + storeChallengeAs( + CHALLENGE.CREATE_NEW_CHALLENGER, + ChallengerChallenges.createChallenger201(challengeOrder++), + getStarted); + + ChallengeSection firstChallenge = + new ChallengeSection( + "First Real Challenge", + "For your first challenge, get the list of challenges. You'll be able to use this to see your progress in your API Client, as well as using the GUI."); sections.add(firstChallenge); // GET all challenges as a list - storeChallengeAs(CHALLENGE.GET_CHALLENGES, GetChallenges.getChallenges200(challengeOrder++), firstChallenge); - - - ChallengeSection getChallenges = new ChallengeSection("GET Challenges", - "To retrieve, or read information from an API we issue GET requests. This section has a bunch of GET request challenges to try out."); + storeChallengeAs( + CHALLENGE.GET_CHALLENGES, + GetChallenges.getChallenges200(challengeOrder++), + firstChallenge); + + ChallengeSection getChallenges = + new ChallengeSection( + "GET Challenges", + "To retrieve, or read information from an API we issue GET requests. This section has a bunch of GET request challenges to try out."); sections.add(getChallenges); - storeChallengeAs(CHALLENGE.GET_TODOS, GetChallenges.getTodos200(challengeOrder++), getChallenges); - storeChallengeAs(CHALLENGE.GET_TODOS_NOT_PLURAL_404, GetChallenges.getTodos404(challengeOrder++), getChallenges); - storeChallengeAs(CHALLENGE.GET_TODO, GetChallenges.getTodo200(challengeOrder++), getChallenges); - storeChallengeAs(CHALLENGE.GET_TODO_404, GetChallenges.getTodo404(challengeOrder++), getChallenges); - storeChallengeAs(CHALLENGE.GET_TODOS_FILTERED, GetChallenges.getTodosFiltered200(challengeOrder++), getChallenges); - + storeChallengeAs( + CHALLENGE.GET_TODOS, GetChallenges.getTodos200(challengeOrder++), getChallenges); + storeChallengeAs( + CHALLENGE.GET_TODOS_NOT_PLURAL_404, + GetChallenges.getTodos404(challengeOrder++), + getChallenges); + storeChallengeAs( + CHALLENGE.GET_TODO, GetChallenges.getTodo200(challengeOrder++), getChallenges); + storeChallengeAs( + CHALLENGE.GET_TODO_404, GetChallenges.getTodo404(challengeOrder++), getChallenges); + storeChallengeAs( + CHALLENGE.GET_TODOS_FILTERED, + GetChallenges.getTodosFiltered200(challengeOrder++), + getChallenges); // HEAD - ChallengeSection headChallenges = new ChallengeSection("HEAD Challenges", - "A HEAD request, is like a GET request, but only returns the headers and status code."); + ChallengeSection headChallenges = + new ChallengeSection( + "HEAD Challenges", + "A HEAD request, is like a GET request, but only returns the headers and status code."); sections.add(headChallenges); - storeChallengeAs(CHALLENGE.GET_HEAD_TODOS, HeadChallenges.headTodos200(challengeOrder++), headChallenges); - + storeChallengeAs( + CHALLENGE.GET_HEAD_TODOS, + HeadChallenges.headTodos200(challengeOrder++), + headChallenges); // CREATE with POST - ChallengeSection postCreateChallenges = new ChallengeSection("Creation Challenges with POST", - "A POST request can be used to create and update data, these challenges are to 'create' data."); + ChallengeSection postCreateChallenges = + new ChallengeSection( + "Creation Challenges with POST", + "A POST request can be used to create and update data, these challenges are to 'create' data."); sections.add(postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS, PostChallenges.postTodos201(challengeOrder++), postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_BAD_DONE_STATUS, PostChallenges.postTodosBadDoneStatus400(challengeOrder++), postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_TOO_LONG_TITLE_LENGTH, PostChallenges.postTodosTitleTooLong400(challengeOrder++), postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_TOO_LONG_DESCRIPTION_LENGTH, PostChallenges.postTodosDescriptionTooLong400(challengeOrder++), postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_MAX_OUT_TITLE_DESCRIPTION_LENGTH, PostChallenges.postTodosMaxTitleDescriptionTooLong400(challengeOrder++), postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_TOO_LONG_PAYLOAD_SIZE, PostChallenges.postTodosPayloadTooLong400(challengeOrder++), postCreateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_INVALID_EXTRA_FIELD, PostChallenges.postTodosInvalidExtraField400(challengeOrder++), postCreateChallenges); - - + storeChallengeAs( + CHALLENGE.POST_TODOS, + PostChallenges.postTodos201(challengeOrder++), + postCreateChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_BAD_DONE_STATUS, + PostChallenges.postTodosBadDoneStatus400(challengeOrder++), + postCreateChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_TOO_LONG_TITLE_LENGTH, + PostChallenges.postTodosTitleTooLong400(challengeOrder++), + postCreateChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_TOO_LONG_DESCRIPTION_LENGTH, + PostChallenges.postTodosDescriptionTooLong400(challengeOrder++), + postCreateChallenges); + storeChallengeAs( + CHALLENGE.POST_MAX_OUT_TITLE_DESCRIPTION_LENGTH, + PostChallenges.postTodosMaxTitleDescriptionTooLong400(challengeOrder++), + postCreateChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_TOO_LONG_PAYLOAD_SIZE, + PostChallenges.postTodosPayloadTooLong400(challengeOrder++), + postCreateChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_INVALID_EXTRA_FIELD, + PostChallenges.postTodosInvalidExtraField400(challengeOrder++), + postCreateChallenges); // CREATE with PUT - ChallengeSection putCreateChallenges = new ChallengeSection("Creation Challenges with PUT", - "A PUT request can often used to create and update data. The todo application we are using has automatically generated ids, so you cannot use PUT to create."); + ChallengeSection putCreateChallenges = + new ChallengeSection( + "Creation Challenges with PUT", + "A PUT request can often used to create and update data. The todo application we are using has automatically generated ids, so you cannot use PUT to create."); sections.add(putCreateChallenges); - storeChallengeAs(CHALLENGE.PUT_TODOS_400, PutChallenges.putTodosId400(challengeOrder++), putCreateChallenges); - - + storeChallengeAs( + CHALLENGE.PUT_TODOS_400, + PutChallenges.putTodosId400(challengeOrder++), + putCreateChallenges); // UPDATE with POST - ChallengeSection postUpdateChallenges = new ChallengeSection("Update Challenges with POST", - "Use a POST request to amend something that already exists. These are 'partial' content updates so you usually don't need to have all details of the entity in the request, e.g. you could just update a title, or a description, or a status"); + ChallengeSection postUpdateChallenges = + new ChallengeSection( + "Update Challenges with POST", + "Use a POST request to amend something that already exists. These are 'partial' content updates so you usually don't need to have all details of the entity in the request, e.g. you could just update a title, or a description, or a status"); sections.add(postUpdateChallenges); - storeChallengeAs(CHALLENGE.POST_UPDATE_TODO, PostChallenges.postTodosId200(challengeOrder++), postUpdateChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_404, PostChallenges.postTodosId404(challengeOrder++), postUpdateChallenges); - - + storeChallengeAs( + CHALLENGE.POST_UPDATE_TODO, + PostChallenges.postTodosId200(challengeOrder++), + postUpdateChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_404, + PostChallenges.postTodosId404(challengeOrder++), + postUpdateChallenges); // UPDATE with PUT - ChallengeSection putUpdateChallenges = new ChallengeSection("Update Challenges with PUT", - "A PUT request can be used to amend data. REST Put requests are idempotent, they provide the same result each time."); + ChallengeSection putUpdateChallenges = + new ChallengeSection( + "Update Challenges with PUT", + "A PUT request can be used to amend data. REST Put requests are idempotent, they provide the same result each time."); sections.add(putUpdateChallenges); - storeChallengeAs(CHALLENGE.PUT_TODOS_FULL_200, PutChallenges.putTodosIdFull200(challengeOrder++), putUpdateChallenges); - storeChallengeAs(CHALLENGE.PUT_TODOS_PARTIAL_200, PutChallenges.putTodosIdPartial200(challengeOrder++), putUpdateChallenges); - storeChallengeAs(CHALLENGE.PUT_TODOS_MISSING_TITLE_400, PutChallenges.putTodosIdNoTitle400(challengeOrder++), putUpdateChallenges); - storeChallengeAs(CHALLENGE.PUT_TODOS_400_NO_AMEND_ID, PutChallenges.putTodosIdNonMatchedIdsAmend400(challengeOrder++), putUpdateChallenges); - - + storeChallengeAs( + CHALLENGE.PUT_TODOS_FULL_200, + PutChallenges.putTodosIdFull200(challengeOrder++), + putUpdateChallenges); + storeChallengeAs( + CHALLENGE.PUT_TODOS_PARTIAL_200, + PutChallenges.putTodosIdPartial200(challengeOrder++), + putUpdateChallenges); + storeChallengeAs( + CHALLENGE.PUT_TODOS_MISSING_TITLE_400, + PutChallenges.putTodosIdNoTitle400(challengeOrder++), + putUpdateChallenges); + storeChallengeAs( + CHALLENGE.PUT_TODOS_400_NO_AMEND_ID, + PutChallenges.putTodosIdNonMatchedIdsAmend400(challengeOrder++), + putUpdateChallenges); // DELETE - ChallengeSection deleteChallenges = new ChallengeSection("DELETE Challenges", - "Use a DELETE request to delete an entity. Since this is an extreme request, normally you have to be logged in or authenticated, but we wanted to make life easier for you so we cover authentication later. Anyone can delete To Do items without authentication in this system."); + ChallengeSection deleteChallenges = + new ChallengeSection( + "DELETE Challenges", + "Use a DELETE request to delete an entity. Since this is an extreme request, normally you have to be logged in or authenticated, but we wanted to make life easier for you so we cover authentication later. Anyone can delete To Do items without authentication in this system."); sections.add(deleteChallenges); - storeChallengeAs(CHALLENGE.DELETE_A_TODO, DeleteChallenges.deleteTodosId200(challengeOrder++), deleteChallenges); - - + storeChallengeAs( + CHALLENGE.DELETE_A_TODO, + DeleteChallenges.deleteTodosId200(challengeOrder++), + deleteChallenges); // OPTIONS - ChallengeSection optionsChallenges = new ChallengeSection("OPTIONS Challenges", - "Use an OPTIONS verb and check the `Allow` header, this will show you what verbs are allowed to be used on an endpoint. When you test APIs it is worth checking to see if all the verbs listed are allowed or not."); + ChallengeSection optionsChallenges = + new ChallengeSection( + "OPTIONS Challenges", + "Use an OPTIONS verb and check the `Allow` header, this will show you what verbs are allowed to be used on an endpoint. When you test APIs it is worth checking to see if all the verbs listed are allowed or not."); sections.add(optionsChallenges); - storeChallengeAs(CHALLENGE.OPTIONS_TODOS, OptionsChallenges.optionsTodos200(challengeOrder++), optionsChallenges); - + storeChallengeAs( + CHALLENGE.OPTIONS_TODOS, + OptionsChallenges.optionsTodos200(challengeOrder++), + optionsChallenges); - - ChallengeSection acceptChallenges = new ChallengeSection("Accept Challenges", - "The `Accept` header, tells the server what format you want the response to be in. By changing the `Accept` header you can specify JSON or XML."); + ChallengeSection acceptChallenges = + new ChallengeSection( + "Accept Challenges", + "The `Accept` header, tells the server what format you want the response to be in. By changing the `Accept` header you can specify JSON or XML."); sections.add(acceptChallenges); - storeChallengeAs(CHALLENGE.GET_ACCEPT_XML, GetChallenges.getTodosAcceptXML200(challengeOrder++), acceptChallenges); - storeChallengeAs(CHALLENGE.GET_ACCEPT_JSON, GetChallenges.getTodosAcceptJson200(challengeOrder++), acceptChallenges); - storeChallengeAs(CHALLENGE.GET_ACCEPT_ANY_DEFAULT_JSON, GetChallenges.getTodosAcceptAny200(challengeOrder++), acceptChallenges); - storeChallengeAs(CHALLENGE.GET_ACCEPT_XML_PREFERRED, GetChallenges.getTodosPreferAcceptXML200(challengeOrder++), acceptChallenges); - storeChallengeAs(CHALLENGE.GET_JSON_BY_DEFAULT_NO_ACCEPT, GetChallenges.getTodosNoAccept200(challengeOrder++), acceptChallenges); - storeChallengeAs(CHALLENGE.GET_UNSUPPORTED_ACCEPT_406, GetChallenges.getTodosUnavailableAccept406(challengeOrder++), acceptChallenges); - - - - - ChallengeSection contentTypeChallenges = new ChallengeSection("Content-Type Challenges", - "The `Content-Type` header, tells the server what format type your 'body' content is, e.g. are you sending XML or JSON."); + storeChallengeAs( + CHALLENGE.GET_ACCEPT_XML, + GetChallenges.getTodosAcceptXML200(challengeOrder++), + acceptChallenges); + storeChallengeAs( + CHALLENGE.GET_ACCEPT_JSON, + GetChallenges.getTodosAcceptJson200(challengeOrder++), + acceptChallenges); + storeChallengeAs( + CHALLENGE.GET_ACCEPT_ANY_DEFAULT_JSON, + GetChallenges.getTodosAcceptAny200(challengeOrder++), + acceptChallenges); + storeChallengeAs( + CHALLENGE.GET_ACCEPT_XML_PREFERRED, + GetChallenges.getTodosPreferAcceptXML200(challengeOrder++), + acceptChallenges); + storeChallengeAs( + CHALLENGE.GET_JSON_BY_DEFAULT_NO_ACCEPT, + GetChallenges.getTodosNoAccept200(challengeOrder++), + acceptChallenges); + storeChallengeAs( + CHALLENGE.GET_UNSUPPORTED_ACCEPT_406, + GetChallenges.getTodosUnavailableAccept406(challengeOrder++), + acceptChallenges); + + ChallengeSection contentTypeChallenges = + new ChallengeSection( + "Content-Type Challenges", + "The `Content-Type` header, tells the server what format type your 'body' content is, e.g. are you sending XML or JSON."); sections.add(contentTypeChallenges); - storeChallengeAs(CHALLENGE.POST_CREATE_XML, PostChallenges.postCreateTodoWithXMLAcceptXML(challengeOrder++), contentTypeChallenges); - storeChallengeAs(CHALLENGE.POST_CREATE_JSON, PostChallenges.postCreateTodoWithJsonAcceptJson(challengeOrder++), contentTypeChallenges); - storeChallengeAs(CHALLENGE.POST_TODOS_415, PostChallenges.postCreateUnsupportedContentType415(challengeOrder++), contentTypeChallenges); - - - + storeChallengeAs( + CHALLENGE.POST_CREATE_XML, + PostChallenges.postCreateTodoWithXMLAcceptXML(challengeOrder++), + contentTypeChallenges); + storeChallengeAs( + CHALLENGE.POST_CREATE_JSON, + PostChallenges.postCreateTodoWithJsonAcceptJson(challengeOrder++), + contentTypeChallenges); + storeChallengeAs( + CHALLENGE.POST_TODOS_415, + PostChallenges.postCreateUnsupportedContentType415(challengeOrder++), + contentTypeChallenges); // adjust the take a break challenges based on the app configuration - ChallengeSection restoreChallenger = new ChallengeSection("Fancy a Break? Restore your session", - "Your challenge progress can be saved, and as long as you remember you challenger ID you can restore it. Leaving a challenger idle in the system for more than 10 minutes will remove the challenger from memory. Challenger status and the todos database can be saved to, and restored from, the browser localStorage."); + ChallengeSection restoreChallenger = + new ChallengeSection( + "Fancy a Break? Restore your session", + "Your challenge progress can be saved, and as long as you remember you challenger ID you can restore it. Leaving a challenger idle in the system for more than 10 minutes will remove the challenger from memory. Challenger status and the todos database can be saved to, and restored from, the browser localStorage."); sections.add(restoreChallenger); // if persistence layer is set to cloud or file then the following apply - if(config.persistenceLayer.willAutoSaveChallengerStatusToPersistenceLayer() && !config.single_player_mode) { - storeChallengeAs(CHALLENGE.GET_RESTORE_EXISTING_CHALLENGER, ChallengerChallenges.getRestoreExistingChallenger200(challengeOrder++), restoreChallenger); - storeChallengeAs(CHALLENGE.POST_RESTORE_EXISTING_CHALLENGER, ChallengerChallenges.postRestoreExistingChallenger200(challengeOrder++), restoreChallenger); + if (config.persistenceLayer.willAutoSaveChallengerStatusToPersistenceLayer() + && !config.single_player_mode) { + storeChallengeAs( + CHALLENGE.GET_RESTORE_EXISTING_CHALLENGER, + ChallengerChallenges.getRestoreExistingChallenger200(challengeOrder++), + restoreChallenger); + storeChallengeAs( + CHALLENGE.POST_RESTORE_EXISTING_CHALLENGER, + ChallengerChallenges.postRestoreExistingChallenger200(challengeOrder++), + restoreChallenger); } // GET the restorable version of challenger progress via api - storeChallengeAs(CHALLENGE.GET_RESTORABLE_CHALLENGER_PROGRESS_STATUS, ChallengerChallenges.getRestorableExistingChallengerProgress200(challengeOrder++), restoreChallenger); + storeChallengeAs( + CHALLENGE.GET_RESTORABLE_CHALLENGER_PROGRESS_STATUS, + ChallengerChallenges.getRestorableExistingChallengerProgress200(challengeOrder++), + restoreChallenger); // PUT to restore challenger progress via api - storeChallengeAs(CHALLENGE.PUT_RESTORABLE_CHALLENGER_PROGRESS_STATUS, ChallengerChallenges.putRestoreChallengerProgress200(challengeOrder++), restoreChallenger); - - // the create with PUT is only valid in multi-user mode, pass in the mode and exclude this challenge - if(!config.single_player_mode) { - storeChallengeAs(CHALLENGE.PUT_NEW_RESTORED_CHALLENGER_PROGRESS_STATUS, ChallengerChallenges.putRestoreChallengerProgress201(challengeOrder++), restoreChallenger); + storeChallengeAs( + CHALLENGE.PUT_RESTORABLE_CHALLENGER_PROGRESS_STATUS, + ChallengerChallenges.putRestoreChallengerProgress200(challengeOrder++), + restoreChallenger); + + // the create with PUT is only valid in multi-user mode, pass in the mode and exclude this + // challenge + if (!config.single_player_mode) { + storeChallengeAs( + CHALLENGE.PUT_NEW_RESTORED_CHALLENGER_PROGRESS_STATUS, + ChallengerChallenges.putRestoreChallengerProgress201(challengeOrder++), + restoreChallenger); } // GET the restorable version of todos database via api - storeChallengeAs(CHALLENGE.GET_RESTORABLE_TODOS, ChallengerChallenges.getRestorableTodos200(challengeOrder++), restoreChallenger); + storeChallengeAs( + CHALLENGE.GET_RESTORABLE_TODOS, + ChallengerChallenges.getRestorableTodos200(challengeOrder++), + restoreChallenger); // PUT to restore version of todos via api - storeChallengeAs(CHALLENGE.PUT_RESTORABLE_TODOS, ChallengerChallenges.putRestorableTodos204(challengeOrder++), restoreChallenger); - - - - + storeChallengeAs( + CHALLENGE.PUT_RESTORABLE_TODOS, + ChallengerChallenges.putRestorableTodos204(challengeOrder++), + restoreChallenger); // POST mixed content and accept - ChallengeSection mixAcceptContentTypeChallenges = new ChallengeSection("Mix Accept and Content-Type Challenges", - "We can mix the `Accept` and `Content-Type` headers so that we can send JSON but receive XML. These challenges encourage you to explore some combinations."); + ChallengeSection mixAcceptContentTypeChallenges = + new ChallengeSection( + "Mix Accept and Content-Type Challenges", + "We can mix the `Accept` and `Content-Type` headers so that we can send JSON but receive XML. These challenges encourage you to explore some combinations."); sections.add(mixAcceptContentTypeChallenges); - storeChallengeAs(CHALLENGE.POST_CREATE_XML_ACCEPT_JSON, PostChallenges.postTodosXmlToJson201(challengeOrder++), mixAcceptContentTypeChallenges); - storeChallengeAs(CHALLENGE.POST_CREATE_JSON_ACCEPT_XML, PostChallenges.postTodosJsonToXml201(challengeOrder++), mixAcceptContentTypeChallenges); - - - - - ChallengeSection miscStatusCodes = new ChallengeSection("Status Code Challenges", - "Status-codes are essential to understand, so we created some challenges that help you trigger more status codes. Remember to review httpstatuses.com to learn what the status codes mean."); + storeChallengeAs( + CHALLENGE.POST_CREATE_XML_ACCEPT_JSON, + PostChallenges.postTodosXmlToJson201(challengeOrder++), + mixAcceptContentTypeChallenges); + storeChallengeAs( + CHALLENGE.POST_CREATE_JSON_ACCEPT_XML, + PostChallenges.postTodosJsonToXml201(challengeOrder++), + mixAcceptContentTypeChallenges); + + ChallengeSection miscStatusCodes = + new ChallengeSection( + "Status Code Challenges", + "Status-codes are essential to understand, so we created some challenges that help you trigger more status codes. Remember to review httpstatuses.com to learn what the status codes mean."); sections.add(miscStatusCodes); - storeChallengeAs(CHALLENGE.DELETE_HEARTBEAT_405, StatusCodeChallenges.methodNotAllowed405UsingDelete(challengeOrder++), miscStatusCodes); - storeChallengeAs(CHALLENGE.PATCH_HEARTBEAT_500, StatusCodeChallenges.serverError500UsingPatch(challengeOrder++), miscStatusCodes); - storeChallengeAs(CHALLENGE.TRACE_HEARTBEAT_501, StatusCodeChallenges.notImplemented501UsingTrace(challengeOrder++), miscStatusCodes); - storeChallengeAs(CHALLENGE.GET_HEARTBEAT_204, StatusCodeChallenges.noContent204UsingGet(challengeOrder++), miscStatusCodes); - - ChallengeSection methodOverrideChallenges = new ChallengeSection("HTTP Method Override Challenges", - "Some HTTP Clients can not send all verbs e.g. PATCH, DELETE, PUT. Use an X-HTTP-Method-Override header to simulate these with a POST request"); + storeChallengeAs( + CHALLENGE.DELETE_HEARTBEAT_405, + StatusCodeChallenges.methodNotAllowed405UsingDelete(challengeOrder++), + miscStatusCodes); + storeChallengeAs( + CHALLENGE.PATCH_HEARTBEAT_500, + StatusCodeChallenges.serverError500UsingPatch(challengeOrder++), + miscStatusCodes); + storeChallengeAs( + CHALLENGE.TRACE_HEARTBEAT_501, + StatusCodeChallenges.notImplemented501UsingTrace(challengeOrder++), + miscStatusCodes); + storeChallengeAs( + CHALLENGE.GET_HEARTBEAT_204, + StatusCodeChallenges.noContent204UsingGet(challengeOrder++), + miscStatusCodes); + + ChallengeSection methodOverrideChallenges = + new ChallengeSection( + "HTTP Method Override Challenges", + "Some HTTP Clients can not send all verbs e.g. PATCH, DELETE, PUT. Use an X-HTTP-Method-Override header to simulate these with a POST request"); sections.add(methodOverrideChallenges); - storeChallengeAs(CHALLENGE.OVERRIDE_DELETE_HEARTBEAT_405, StatusCodeChallenges.overridePostToDeleteFor405(challengeOrder++), methodOverrideChallenges); - storeChallengeAs(CHALLENGE.OVERRIDE_PATCH_HEARTBEAT_500, StatusCodeChallenges.overridePostToPatchFor500(challengeOrder++), methodOverrideChallenges); - storeChallengeAs(CHALLENGE.OVERRIDE_TRACE_HEARTBEAT_501, StatusCodeChallenges.overridePostToTraceFor501(challengeOrder++), methodOverrideChallenges); - - + storeChallengeAs( + CHALLENGE.OVERRIDE_DELETE_HEARTBEAT_405, + StatusCodeChallenges.overridePostToDeleteFor405(challengeOrder++), + methodOverrideChallenges); + storeChallengeAs( + CHALLENGE.OVERRIDE_PATCH_HEARTBEAT_500, + StatusCodeChallenges.overridePostToPatchFor500(challengeOrder++), + methodOverrideChallenges); + storeChallengeAs( + CHALLENGE.OVERRIDE_TRACE_HEARTBEAT_501, + StatusCodeChallenges.overridePostToTraceFor501(challengeOrder++), + methodOverrideChallenges); // authorization and authentication - ChallengeSection authenticationChallenges = new ChallengeSection("Authentication Challenges", - "Authentication is telling the system who you are. In multi-user mode you are already doing that with the X-CHALLENGER header, but we have added an extra level of security on the /secret section. So first Authenticate with Basic Authentication to find out the token to use for authorisation for later challenges."); + ChallengeSection authenticationChallenges = + new ChallengeSection( + "Authentication Challenges", + "Authentication is telling the system who you are. In multi-user mode you are already doing that with the X-CHALLENGER header, but we have added an extra level of security on the /secret section. So first Authenticate with Basic Authentication to find out the token to use for authorisation for later challenges."); sections.add(authenticationChallenges); - storeChallengeAs(CHALLENGE.CREATE_SECRET_TOKEN_401, SecretTokenChallenges.createSecretTokenNotAuthenticated401(challengeOrder++), authenticationChallenges); - storeChallengeAs(CHALLENGE.CREATE_SECRET_TOKEN_201, SecretTokenChallenges.createSecretTokenAuthenticated201(challengeOrder++), authenticationChallenges); - - - - - ChallengeSection authorizationChallenges = new ChallengeSection("Authorization Challenges", - "Once the system knows who you are, authorization is if you have the correct level of access. In these challenges the authorization is granted using a custom API header X-AUTH-TOKEN or using a Bearer Authorization header."); + storeChallengeAs( + CHALLENGE.CREATE_SECRET_TOKEN_401, + SecretTokenChallenges.createSecretTokenNotAuthenticated401(challengeOrder++), + authenticationChallenges); + storeChallengeAs( + CHALLENGE.CREATE_SECRET_TOKEN_201, + SecretTokenChallenges.createSecretTokenAuthenticated201(challengeOrder++), + authenticationChallenges); + + ChallengeSection authorizationChallenges = + new ChallengeSection( + "Authorization Challenges", + "Once the system knows who you are, authorization is if you have the correct level of access. In these challenges the authorization is granted using a custom API header X-AUTH-TOKEN or using a Bearer Authorization header."); sections.add(authorizationChallenges); - storeChallengeAs(CHALLENGE.GET_SECRET_NOTE_403, SecretTokenChallenges.forbiddenNotAuthorized403(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.GET_SECRET_NOTE_401, SecretTokenChallenges.invalidRequestNoAuthHeader401(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.GET_SECRET_NOTE_200, SecretTokenChallenges.authorizedGet200(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.POST_SECRET_NOTE_200, SecretTokenChallenges.authorizedUpdate200(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.POST_SECRET_NOTE_401, SecretTokenChallenges.postMissingTokenAuth401(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.POST_SECRET_NOTE_403, SecretTokenChallenges.postInvalidTokenAuth401(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.GET_SECRET_NOTE_BEARER_200, SecretTokenChallenges.getWithValidBearerToken200(challengeOrder++), authorizationChallenges); - storeChallengeAs(CHALLENGE.POST_SECRET_NOTE_BEARER_200, SecretTokenChallenges.postUpdateWithValidBearerToken200(challengeOrder++), authorizationChallenges); - - + storeChallengeAs( + CHALLENGE.GET_SECRET_NOTE_403, + SecretTokenChallenges.forbiddenNotAuthorized403(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.GET_SECRET_NOTE_401, + SecretTokenChallenges.invalidRequestNoAuthHeader401(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.GET_SECRET_NOTE_200, + SecretTokenChallenges.authorizedGet200(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.POST_SECRET_NOTE_200, + SecretTokenChallenges.authorizedUpdate200(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.POST_SECRET_NOTE_401, + SecretTokenChallenges.postMissingTokenAuth401(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.POST_SECRET_NOTE_403, + SecretTokenChallenges.postInvalidTokenAuth401(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.GET_SECRET_NOTE_BEARER_200, + SecretTokenChallenges.getWithValidBearerToken200(challengeOrder++), + authorizationChallenges); + storeChallengeAs( + CHALLENGE.POST_SECRET_NOTE_BEARER_200, + SecretTokenChallenges.postUpdateWithValidBearerToken200(challengeOrder++), + authorizationChallenges); // misc - ChallengeSection miscChallenges = new ChallengeSection("Miscellaneous Challenges", - "We left these challenges to the end because they seemed fun, but... different."); + ChallengeSection miscChallenges = + new ChallengeSection( + "Miscellaneous Challenges", + "We left these challenges to the end because they seemed fun, but... different."); sections.add(miscChallenges); - - storeChallengeAs(CHALLENGE.DELETE_ALL_TODOS, MiscChallenges.deleteAllTodos200(challengeOrder++), miscChallenges); - storeChallengeAs(CHALLENGE.POST_ALL_TODOS, MiscChallenges.postAllTodos201(challengeOrder++), miscChallenges); - - - - - + storeChallengeAs( + CHALLENGE.DELETE_ALL_TODOS, + MiscChallenges.deleteAllTodos200(challengeOrder++), + miscChallenges); + storeChallengeAs( + CHALLENGE.POST_ALL_TODOS, + MiscChallenges.postAllTodos201(challengeOrder++), + miscChallenges); Set challengeNames = new HashSet(); - for(ChallengeDefinitionData challenge : orderedChallenges){ + for (ChallengeDefinitionData challenge : orderedChallenges) { logger.info("Setup Challenge: " + challenge.name); challengeNames.add(challenge.name); } int sectionChallengesCount = 0; - for(ChallengeSection section : sections){ + for (ChallengeSection section : sections) { sectionChallengesCount += section.getChallenges().size(); } - if(sectionChallengesCount!= orderedChallenges.size()) { + if (sectionChallengesCount != orderedChallenges.size()) { throw new RuntimeException( - "Number challenges in sections, does not match number of challenges" + - ", possibly forgot to add section or challenge to section"); + "Number challenges in sections, does not match number of challenges" + + ", possibly forgot to add section or challenge to section"); } - if(challengeNames.size()!= orderedChallenges.size()) { + if (challengeNames.size() != orderedChallenges.size()) { throw new RuntimeException( - "Number of names, does not match number of challenges" + - ", possible duplicate name"); + "Number of names, does not match number of challenges" + + ", possible duplicate name"); } } private ChallengeDefinitionData storeChallengeAs( - final CHALLENGE id, - final ChallengeDefinitionData challenge, - ChallengeSection section) { + final CHALLENGE id, final ChallengeDefinitionData challenge, ChallengeSection section) { challengeData.put(id, challenge); orderedChallenges.add(challenge); @@ -304,10 +472,9 @@ private ChallengeDefinitionData storeChallengeAs( return challenge; } - public CHALLENGE getChallenge(final String name) { - for(Map.Entrychallenge : challengeData.entrySet()){ - if(challenge.getValue().name.contentEquals(name)){ + for (Map.Entry challenge : challengeData.entrySet()) { + if (challenge.getValue().name.contentEquals(name)) { return challenge.getKey(); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java index 2b23b1a6..fff5f709 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/ChallengeSolutionLink.java @@ -6,34 +6,36 @@ public class ChallengeSolutionLink { public final String linkType; public final String linkData; - public ChallengeSolutionLink(final String linkText, final String linkType, final String linkUrl) { + public ChallengeSolutionLink( + final String linkText, final String linkType, final String linkUrl) { this.linkText = linkText.trim(); - if(linkType==null){ + if (linkType == null) { this.linkType = ""; - }else{ + } else { this.linkType = linkType.trim().toUpperCase(); } - if(linkUrl==null) { + if (linkUrl == null) { this.linkData = ""; - }else{ + } else { this.linkData = linkUrl.trim(); } } public String asHtmlAHref() { - if(linkType.equals("YOUTUBE")){ - return String.format("%s",linkData, linkText); + if (linkType.equals("YOUTUBE")) { + return String.format( + "%s", linkData, linkText); } - if(linkData.isEmpty()){ + if (linkData.isEmpty()) { return linkText; } - String target="target='_blank'"; - if(!linkData.startsWith("http")){ - target=""; + String target = "target='_blank'"; + if (!linkData.startsWith("http")) { + target = ""; } - return String.format("%s",linkData, target, linkText); + return String.format("%s", linkData, target, linkText); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengeRenderer.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengeRenderer.java index 21594969..68b1ebc3 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengeRenderer.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengeRenderer.java @@ -2,7 +2,7 @@ public class ChallengeRenderer { - public static String renderChallengeNumber(int challengeOrder){ + public static String renderChallengeNumber(int challengeOrder) { return String.format("%02d", challengeOrder); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengerChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengerChallenges.java index 144520ea..e24dc0e4 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengerChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/ChallengerChallenges.java @@ -5,132 +5,187 @@ public class ChallengerChallenges { public static ChallengeDefinitionData createChallenger201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /challenger (201)", - "Issue a POST request on the `/challenger` end point, with no body, to create a new challenger session. Use the generated X-CHALLENGER header in future requests to track challenge completion." - ); - aChallenge.addHint("In multi-user mode, you need to create an X-CHALLENGER Session in order to complete any challenges or make PUT, POST, DELETE requests", "/gui/multiuser"); - aChallenge.addSolutionLink("Send request using POST to /challenger endpoint. The response has an X-CHALLENGER header, add this header X-CHALLENGER and the GUID value to all future requests.","",""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/create-session/post-challenger-201"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /challenger (201)", + "Issue a POST request on the `/challenger` end point, with no body, to create a new challenger session. Use the generated X-CHALLENGER header in future requests to track challenge completion."); + aChallenge.addHint( + "In multi-user mode, you need to create an X-CHALLENGER Session in order to complete any challenges or make PUT, POST, DELETE requests", + "/gui/multiuser"); + aChallenge.addSolutionLink( + "Send request using POST to /challenger endpoint. The response has an X-CHALLENGER header, add this header X-CHALLENGER and the GUID value to all future requests.", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/create-session/post-challenger-201"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } // challenge to GET /challenger/{guid} and restore a challenger session public static ChallengeDefinitionData getRestoreExistingChallenger200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /challenger/guid (200)", - "Issue a GET request on the `/challenger` end point with an existing challenger GUID to restore that challenger's progress into memory." - ); - aChallenge.addHint("In multi-user mode, you need to create an X-CHALLENGER Session first and let it go idle so it is removed in the 10 minute purge", "/gui/multiuser"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /challenger/guid (200)", + "Issue a GET request on the `/challenger` end point with an existing challenger GUID to restore that challenger's progress into memory."); + aChallenge.addHint( + "In multi-user mode, you need to create an X-CHALLENGER Session first and let it go idle so it is removed in the 10 minute purge", + "/gui/multiuser"); aChallenge.addHint("Remember to add the X-CHALLENGER header to track your progress", ""); aChallenge.addHint("Add the guid in the URL as the last part of the path", ""); - aChallenge.addSolutionLink("GET /challenger/{guid} for a challenger previously saved in the persistence store", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); + aChallenge.addSolutionLink( + "GET /challenger/{guid} for a challenger previously saved in the persistence store", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } - // challenge to POST /challenger with X-CHALLENGER header of existing challenger to restore a challenger + // challenge to POST /challenger with X-CHALLENGER header of existing challenger to restore a + // challenger public static ChallengeDefinitionData postRestoreExistingChallenger200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /challenger (existing X-CHALLENGER)", - "Issue a POST request on the `/challenger` end point, with an existing challenger GUID as the X-CHALLENGER header to restore that challenger's progress into memory." - ); - aChallenge.addHint("In multi-user mode, you need to create an X-CHALLENGER Session first and let it go idle so it is removed in the 10 minute purge", "/gui/multiuser"); - - aChallenge.addSolutionLink("POST /challenger with the challenger GUID in the X-CHALLENGER header for a challenger previously saved in the persistence store", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); - - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /challenger (existing X-CHALLENGER)", + "Issue a POST request on the `/challenger` end point, with an existing challenger GUID as the X-CHALLENGER header to restore that challenger's progress into memory."); + aChallenge.addHint( + "In multi-user mode, you need to create an X-CHALLENGER Session first and let it go idle so it is removed in the 10 minute purge", + "/gui/multiuser"); + + aChallenge.addSolutionLink( + "POST /challenger with the challenger GUID in the X-CHALLENGER header for a challenger previously saved in the persistence store", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); + + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } - public static ChallengeDefinitionData getRestorableExistingChallengerProgress200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /challenger/guid (existing X-CHALLENGER)", - "Issue a GET request on the `/challenger/{guid}` end point, with an existing challenger GUID. This will return the progress data payload that can be used to later restore your progress to this status." - ); + public static ChallengeDefinitionData getRestorableExistingChallengerProgress200( + int challengeOrder) { + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /challenger/guid (existing X-CHALLENGER)", + "Issue a GET request on the `/challenger/{guid}` end point, with an existing challenger GUID. This will return the progress data payload that can be used to later restore your progress to this status."); aChallenge.addHint("A challenger must have been created already for this to work", ""); aChallenge.addHint("Remember to add the X-CHALLENGER header to track your progress", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } public static ChallengeDefinitionData putRestoreChallengerProgress200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /challenger/guid RESTORE", - "Issue a PUT request on the `/challenger/{guid}` end point, with an existing challenger GUID to restore that challenger's progress into memory." - ); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /challenger/guid RESTORE", + "Issue a PUT request on the `/challenger/{guid}` end point, with an existing challenger GUID to restore that challenger's progress into memory."); aChallenge.addHint("Use the challenger payload returned from the earlier GET request", ""); aChallenge.addHint("Remember to add the X-CHALLENGER header to track your progress", ""); - aChallenge.addHint("The challenger should already exist in memory and this will restore status to an earlier point", ""); + aChallenge.addHint( + "The challenger should already exist in memory and this will restore status to an earlier point", + ""); - aChallenge.addSolutionLink("Using the payload from the earlier 'GET /challenger/guid' request, use PUT to reset the challenger progress", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); + aChallenge.addSolutionLink( + "Using the payload from the earlier 'GET /challenger/guid' request, use PUT to reset the challenger progress", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } public static ChallengeDefinitionData putRestoreChallengerProgress201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /challenger/guid CREATE", - "Issue a PUT request on the `/challenger/{guid}` end point, with a challenger GUID not currently in memory to restore that challenger's progress into memory." - ); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /challenger/guid CREATE", + "Issue a PUT request on the `/challenger/{guid}` end point, with a challenger GUID not currently in memory to restore that challenger's progress into memory."); aChallenge.addHint("Use the challenger payload returned from the earlier GET request", ""); aChallenge.addHint("Remember to add the X-CHALLENGER header to track your progress", ""); - aChallenge.addHint("This will create the Challenger in memory because it should not already exist", ""); + aChallenge.addHint( + "This will create the Challenger in memory because it should not already exist", + ""); - aChallenge.addSolutionLink("Using the payload from the earlier 'GET /challenger/guid' request, use PUT to reset the challenger progress", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); + aChallenge.addSolutionLink( + "Using the payload from the earlier 'GET /challenger/guid' request, use PUT to reset the challenger progress", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } public static ChallengeDefinitionData getRestorableTodos200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /challenger/database/guid (200)", - "Issue a GET request on the `/challenger/database/{guid}` end point, to retrieve the current todos database for the user. You can use this to restore state later." - ); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /challenger/database/guid (200)", + "Issue a GET request on the `/challenger/database/{guid}` end point, to retrieve the current todos database for the user. You can use this to restore state later."); aChallenge.addHint("Remember to add the X-CHALLENGER header to track your progress", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } public static ChallengeDefinitionData putRestorableTodos204(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /challenger/database/guid (Update)", - "Issue a PUT request on the `/challenger/database/{guid}` end point, with a payload to restore the Todos database in memory." - ); - aChallenge.addHint("Use the Todos database payload returned from the earlier GET request", ""); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /challenger/database/guid (Update)", + "Issue a PUT request on the `/challenger/database/{guid}` end point, with a payload to restore the Todos database in memory."); + aChallenge.addHint( + "Use the Todos database payload returned from the earlier GET request", ""); aChallenge.addHint("Remember to add the X-CHALLENGER header to track your progress", ""); - aChallenge.addSolutionLink("Using the payload from the earlier 'GET /challenger/database/guid' request, use PUT to reset the challenger todos data", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/manage-session/save-restore-session"); + aChallenge.addSolutionLink( + "Using the payload from the earlier 'GET /challenger/database/guid' request, use PUT to reset the challenger todos data", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/manage-session/save-restore-session"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tNGuZMQgHxw"); return aChallenge; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/DeleteChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/DeleteChallenges.java index d062d784..e0de1024 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/DeleteChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/DeleteChallenges.java @@ -5,15 +5,18 @@ public class DeleteChallenges { public static ChallengeDefinitionData deleteTodosId200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "DELETE /todos/{id} (200)", - "Issue a DELETE request to successfully delete a todo"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "DELETE /todos/{id} (200)", + "Issue a DELETE request to successfully delete a todo"); - aChallenge.addHint("Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); + aChallenge.addHint( + "Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); aChallenge.addHint("Make sure a todo with the id exists prior to issuing the request"); aChallenge.addHint("Check it was deleted by issuing a GET or HEAD on the /todos/{id}"); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/delete/delete-todos-id-200"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/delete/delete-todos-id-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "6MXTkaXn9qU"); return aChallenge; diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/GetChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/GetChallenges.java index 4c002db7..3379aca4 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/GetChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/GetChallenges.java @@ -5,101 +5,124 @@ public class GetChallenges { public static ChallengeDefinitionData getChallenges200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /challenges (200)", - "Issue a GET request on the `/challenges` end point"); - - aChallenge.addHint("Remember to add the X-CHALLENGER header so you see the progress of the challenges for your session.", ""); - aChallenge.addHint("If you issue a GET request without an X-CHALLENGER header you will see the default challenge values.", ""); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /challenges (200)", + "Issue a GET request on the `/challenges` end point"); + + aChallenge.addHint( + "Remember to add the X-CHALLENGER header so you see the progress of the challenges for your session.", + ""); + aChallenge.addHint( + "If you issue a GET request without an X-CHALLENGER header you will see the default challenge values.", + ""); aChallenge.addHint("By default the response body will be JSON format.", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/first-challenge/get-challenges-200"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/first-challenge/get-challenges-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "DrAjk2NaPRo"); return aChallenge; } - public static ChallengeDefinitionData getTodos200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200)", - "Issue a GET request on the `/todos` end point"); - - aChallenge.addHint("Remember to add the X-CHALLENGER header so you see the data for your session.", ""); - aChallenge.addHint("If you issue a GET request without an X-CHALLENGER header you will see the default todo values.", ""); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200)", + "Issue a GET request on the `/todos` end point"); + + aChallenge.addHint( + "Remember to add the X-CHALLENGER header so you see the data for your session.", + ""); + aChallenge.addHint( + "If you issue a GET request without an X-CHALLENGER header you will see the default todo values.", + ""); aChallenge.addHint("By default the response body will be JSON format.", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-200"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "OpisB0UZq0c"); return aChallenge; } public static ChallengeDefinitionData getTodos404(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todo (404) not plural", - "Issue a GET request on the `/todo` end point should 404 because nouns should be plural"); - - - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/get/get-todo-404"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todo (404) not plural", + "Issue a GET request on the `/todo` end point should 404 because nouns should be plural"); + + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/get/get-todo-404"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "gAJzqgcN9dc"); return aChallenge; } public static ChallengeDefinitionData getTodo200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos/{id} (200)", - "Issue a GET request on the `/todos/{id}` end point to return a specific todo"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos/{id} (200)", + "Issue a GET request on the `/todos/{id}` end point to return a specific todo"); - aChallenge.addHint("Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); + aChallenge.addHint( + "Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-id-200"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-id-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "JDbbSY3U_rY"); return aChallenge; } public static ChallengeDefinitionData getTodo404(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos/{id} (404)", - "Issue a GET request on the `/todos/{id}` end point for a todo that does not exist"); - - aChallenge.addHint("Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos/{id} (404)", + "Issue a GET request on the `/todos/{id}` end point for a todo that does not exist"); + + aChallenge.addHint( + "Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); aChallenge.addHint("Make sure the id is an integer e.g. /todos/1"); aChallenge.addHint("Make sure you are using the /todos end point e.g. /todos/1"); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-id-404"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-id-404"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "1S5kpd8-xfM"); return aChallenge; } public static ChallengeDefinitionData getTodosFiltered200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200) ?filter", - "Issue a GET request on the `/todos` end point with a query filter to get only todos which are 'done'. There must exist both 'done' and 'not done' todos, to pass this challenge."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200) ?filter", + "Issue a GET request on the `/todos` end point with a query filter to get only todos which are 'done'. There must exist both 'done' and 'not done' todos, to pass this challenge."); aChallenge.addHint("A query filter is a URL parameter using the field name and a value"); - aChallenge.addHint("A URL parameter is added to the end of a url with a ? e.g. /todos?id=1"); - aChallenge.addHint("To filter on 'done' we use the 'doneStatus' field ? e.g. ?doneStatus=true"); + aChallenge.addHint( + "A URL parameter is added to the end of a url with a ? e.g. /todos?id=1"); + aChallenge.addHint( + "To filter on 'done' we use the 'doneStatus' field ? e.g. ?doneStatus=true"); aChallenge.addHint("Make sure there are todos which are done, and not yet done"); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-200-filter"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/get/get-todos-200-filter"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "G-sLuhyPMuw"); return aChallenge; } - /* - ACCEPT HEADERS - */ + ACCEPT HEADERS + */ // GET accept type // specify accept type - XML // specify accept type - JSON @@ -108,72 +131,91 @@ public static ChallengeDefinitionData getTodosFiltered200(int challengeOrder) { // none specified - get default // cannot supply accepted type 406 - public static ChallengeDefinitionData getTodosAcceptXML200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200) XML", - "Issue a GET request on the `/todos` end point with an `Accept` header of `application/xml` to receive results in XML format"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/accept-header/get-todos-200-xml"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200) XML", + "Issue a GET request on the `/todos` end point with an `Accept` header of `application/xml` to receive results in XML format"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/accept-header/get-todos-200-xml"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "cLeEuZm2VG8"); return aChallenge; } public static ChallengeDefinitionData getTodosAcceptJson200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200) JSON", - "Issue a GET request on the `/todos` end point with an `Accept` header of `application/json` to receive results in JSON format"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/accept-header/get-todos-200-json"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200) JSON", + "Issue a GET request on the `/todos` end point with an `Accept` header of `application/json` to receive results in JSON format"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/accept-header/get-todos-200-json"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "79JTHiby2Qw"); return aChallenge; } public static ChallengeDefinitionData getTodosAcceptAny200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200) ANY", - "Issue a GET request on the `/todos` end point with an `Accept` header of `*/*` to receive results in default JSON format"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/accept-header/get-todos-200-any"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200) ANY", + "Issue a GET request on the `/todos` end point with an `Accept` header of `*/*` to receive results in default JSON format"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/accept-header/get-todos-200-any"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "O4DhJ8Ohkk8"); return aChallenge; } public static ChallengeDefinitionData getTodosPreferAcceptXML200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200) XML pref", - "Issue a GET request on the `/todos` end point with an `Accept` header of `application/xml, application/json` to receive results in the preferred XML format"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/accept-header/get-todos-200-xml-pref"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200) XML pref", + "Issue a GET request on the `/todos` end point with an `Accept` header of `application/xml, application/json` to receive results in the preferred XML format"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/accept-header/get-todos-200-xml-pref"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "sLChuy9pc9U"); return aChallenge; } public static ChallengeDefinitionData getTodosNoAccept200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (200) no accept", - "Issue a GET request on the `/todos` end point with no `Accept` header present in the message to receive results in default JSON format"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/accept-header/get-todos-200-no-accept"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (200) no accept", + "Issue a GET request on the `/todos` end point with no `Accept` header present in the message to receive results in default JSON format"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/accept-header/get-todos-200-no-accept"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "CSVP2PcvOdg"); return aChallenge; } public static ChallengeDefinitionData getTodosUnavailableAccept406(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /todos (406)", - "Issue a GET request on the `/todos` end point with an `Accept` header `application/gzip` to receive 406 'NOT ACCEPTABLE' status code"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/accept-header/get-todos-406"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /todos (406)", + "Issue a GET request on the `/todos` end point with an `Accept` header `application/gzip` to receive 406 'NOT ACCEPTABLE' status code"); + + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/accept-header/get-todos-406"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "QzfbegkY1ok"); return aChallenge; } - - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/HeadChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/HeadChallenges.java index 5269289a..13f645fa 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/HeadChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/HeadChallenges.java @@ -5,13 +5,15 @@ public class HeadChallenges { public static ChallengeDefinitionData headTodos200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "HEAD /todos (200)", - "Issue a HEAD request on the `/todos` end point"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "HEAD /todos (200)", + "Issue a HEAD request on the `/todos` end point"); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/head/head-todos-200"); - aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "zKbytTelP84"); return aChallenge; + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/head/head-todos-200"); + aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "zKbytTelP84"); + return aChallenge; } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/MiscChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/MiscChallenges.java index 941a7025..86db3ae7 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/MiscChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/MiscChallenges.java @@ -6,28 +6,36 @@ public class MiscChallenges { // TODO: create solution and video for Post all public static ChallengeDefinitionData postAllTodos201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (201) all", - "Issue as many POST requests as it takes to add the maximum number of TODOS allowed for a user. The maximum number should be listed in the documentation." - ); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (201) all", + "Issue as many POST requests as it takes to add the maximum number of TODOS allowed for a user. The maximum number should be listed in the documentation."); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/miscellaneous/create-maximum-number-todos"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/miscellaneous/create-maximum-number-todos"); return aChallenge; } public static ChallengeDefinitionData deleteAllTodos200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "DELETE /todos/{id} (200) all", - "Issue a DELETE request to successfully delete the last todo in system so that there are no more todos in the system"); - - aChallenge.addHint("After deleting the last todo, there will be no todos left in the application"); - aChallenge.addHint("Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); - aChallenge.addHint("You have to delete all the todo items in the system to complete this challenge"); - - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/miscellaneous/delete-all-todos"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "DELETE /todos/{id} (200) all", + "Issue a DELETE request to successfully delete the last todo in system so that there are no more todos in the system"); + + aChallenge.addHint( + "After deleting the last todo, there will be no todos left in the application"); + aChallenge.addHint( + "Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); + aChallenge.addHint( + "You have to delete all the todo items in the system to complete this challenge"); + + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/miscellaneous/delete-all-todos"); return aChallenge; } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/OptionsChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/OptionsChallenges.java index 166b192a..7ec5e87f 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/OptionsChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/OptionsChallenges.java @@ -5,12 +5,14 @@ public class OptionsChallenges { public static ChallengeDefinitionData optionsTodos200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "OPTIONS /todos (200)", - "Issue an OPTIONS request on the `/todos` end point. You might want to manually check the 'Allow' header in the response is as expected."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "OPTIONS /todos (200)", + "Issue an OPTIONS request on the `/todos` end point. You might want to manually check the 'Allow' header in the response is as expected."); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/options/options-todos-200"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/options/options-todos-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "Ld5h1TSnXWA"); return aChallenge; diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PostChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PostChallenges.java index 371905ed..834e6638 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PostChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PostChallenges.java @@ -4,225 +4,300 @@ public class PostChallenges { - - /* - CREATE TODOS - */ + CREATE TODOS + */ public static ChallengeDefinitionData postTodos201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (201)", - "Issue a POST request to successfully create a todo"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (201)", + "Issue a POST request to successfully create a todo"); aChallenge.addHint("Add a JSON payload in the request", ""); - aChallenge.addHint("If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", ""); + aChallenge.addHint( + "If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", + ""); aChallenge.addHint("You must add an X-CHALLENGER header for a valid session", ""); - - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-201"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-201"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); return aChallenge; } public static ChallengeDefinitionData postTodosTitleTooLong400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (400) title too long", - "Issue a POST request to create a todo but fail length validation on the `title` field because your title exceeds maximum allowable characters."); - - aChallenge.addHint("The API Documentation shows the maximum allowed length of the title field", ""); - - aChallenge.addSolutionLink("Send a POST request to /todos with a title longer than 50 characters", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-400-title-too-long"); -// aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (400) title too long", + "Issue a POST request to create a todo but fail length validation on the `title` field because your title exceeds maximum allowable characters."); + + aChallenge.addHint( + "The API Documentation shows the maximum allowed length of the title field", ""); + + aChallenge.addSolutionLink( + "Send a POST request to /todos with a title longer than 50 characters", "", ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/post-create/post-todos-400-title-too-long"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); // TODO: create solution for failing title too long return aChallenge; } public static ChallengeDefinitionData postTodosBadDoneStatus400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (400) doneStatus", - "Issue a POST request to create a todo but fail validation on the `doneStatus` field"); - - aChallenge.addHint("doneStatus should be boolean, an invalid status would be a String or a number e.g. \"invalid\""); - - aChallenge.addSolutionLink("Send a POST request to /todos with a non-boolean `doneStatus` e.g. {\"title\":\"a title\",\"doneStatus\":\"invalid\"}", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-400"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (400) doneStatus", + "Issue a POST request to create a todo but fail validation on the `doneStatus` field"); + + aChallenge.addHint( + "doneStatus should be boolean, an invalid status would be a String or a number e.g. \"invalid\""); + + aChallenge.addSolutionLink( + "Send a POST request to /todos with a non-boolean `doneStatus` e.g. {\"title\":\"a title\",\"doneStatus\":\"invalid\"}", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-400"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); return aChallenge; } public static ChallengeDefinitionData postTodosDescriptionTooLong400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (400) description too long", - "Issue a POST request to create a todo but fail length validation on the `description` because your description exceeds maximum allowable characters."); - - aChallenge.addHint("The API Documentation shows the maximum allowed length of the description field", ""); - - aChallenge.addSolutionLink("Send a POST request to /todos with a description longer than 200 characters", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-400-description-too-long"); - -// aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (400) description too long", + "Issue a POST request to create a todo but fail length validation on the `description` because your description exceeds maximum allowable characters."); + + aChallenge.addHint( + "The API Documentation shows the maximum allowed length of the description field", + ""); + + aChallenge.addSolutionLink( + "Send a POST request to /todos with a description longer than 200 characters", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/post-create/post-todos-400-description-too-long"); + + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); // TODO: create video solution for failing description too long return aChallenge; } - public static ChallengeDefinitionData postTodosMaxTitleDescriptionTooLong400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (201) max out content", - "Issue a POST request to create a todo with maximum length title and description fields."); + public static ChallengeDefinitionData postTodosMaxTitleDescriptionTooLong400( + int challengeOrder) { + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (201) max out content", + "Issue a POST request to create a todo with maximum length title and description fields."); aChallenge.addHint("Max lengths are listed in the API Documentation"); - aChallenge.addHint("CounterStrings are very useful for testing with maximum field lengths","https://eviltester.github.io/TestingApp/apps/counterstrings/counterstrings.html"); + aChallenge.addHint( + "CounterStrings are very useful for testing with maximum field lengths", + "https://eviltester.github.io/TestingApp/apps/counterstrings/counterstrings.html"); aChallenge.addHint("Both title and description should be the correct maximum lengths"); - aChallenge.addSolutionLink("Send a POST request to /todos with a description of 200 characters and a title with 50 characters", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-201-max-content"); + aChallenge.addSolutionLink( + "Send a POST request to /todos with a description of 200 characters and a title with 50 characters", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/post-create/post-todos-201-max-content"); -// aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); // TODO: create video solution for max out title and description return aChallenge; } public static ChallengeDefinitionData postTodosPayloadTooLong400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (413) content too long", - "Issue a POST request to create a todo but fail payload length validation on the `description` because your whole payload exceeds maximum allowable 5000 characters."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (413) content too long", + "Issue a POST request to create a todo but fail payload length validation on the `description` because your whole payload exceeds maximum allowable 5000 characters."); aChallenge.addHint("Try using a long 5000 char string as the description or title text"); - aChallenge.addHint("CounterStrings are very useful for testing with maximum field lengths","https://eviltester.github.io/TestingApp/apps/counterstrings/counterstrings.html"); - - aChallenge.addSolutionLink("Send a POST request to /todos with a description of 5000 characters in length", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-413-content-too-long"); - -// aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); + aChallenge.addHint( + "CounterStrings are very useful for testing with maximum field lengths", + "https://eviltester.github.io/TestingApp/apps/counterstrings/counterstrings.html"); + + aChallenge.addSolutionLink( + "Send a POST request to /todos with a description of 5000 characters in length", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/post-create/post-todos-413-content-too-long"); + + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); // TODO: create video solution for failing content too long return aChallenge; } public static ChallengeDefinitionData postTodosInvalidExtraField400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (400) extra", - "Issue a POST request to create a todo but fail validation because your payload contains an unrecognised field."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (400) extra", + "Issue a POST request to create a todo but fail validation because your payload contains an unrecognised field."); aChallenge.addHint("Try to create a todo with a title, description and a priority"); - aChallenge.addSolutionLink("Send a POST request to /todos with a priority field e.g. {\"title\":\"a title\",\"priority\":\"extra\"}", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-create/post-todos-400-extra-field"); + aChallenge.addSolutionLink( + "Send a POST request to /todos with a priority field e.g. {\"title\":\"a title\",\"priority\":\"extra\"}", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/post-create/post-todos-400-extra-field"); -// aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "tlye5bQ72g0"); // TODO: create video solution for unrecognised field names return aChallenge; } /* - UPDATE TODOs - */ - + UPDATE TODOs + */ public static ChallengeDefinitionData postTodosId200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos/{id} (200)", - "Issue a POST request to successfully update a todo"); - - aChallenge.addHint("Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-update/post-todos-id-200"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos/{id} (200)", + "Issue a POST request to successfully update a todo"); + + aChallenge.addHint( + "Make sure you don't use {id} in the url, replace that with the id of a todo e.g. /todos/1"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/post-update/post-todos-id-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "feXdRpZ_tgs"); return aChallenge; } public static ChallengeDefinitionData postTodosId404(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos/{id} (404)", - "Issue a POST request for a todo which does not exist. Expect to receive a 404 response."); - - aChallenge.addHint("Make sure you don't use {id} in the url, replace that with the id of a todo that does not exist e.g. /todos/100"); - - aChallenge.addSolutionLink("Send a POST request to /todos/{id} with a valid update payload where {id} does not exist", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/post-update/post-todos-id-404"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos/{id} (404)", + "Issue a POST request for a todo which does not exist. Expect to receive a 404 response."); + + aChallenge.addHint( + "Make sure you don't use {id} in the url, replace that with the id of a todo that does not exist e.g. /todos/100"); + + aChallenge.addSolutionLink( + "Send a POST request to /todos/{id} with a valid update payload where {id} does not exist", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/post-update/post-todos-id-404"); // todo add video solution and hints for POST 404 - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "feXdRpZ_tgs"); return aChallenge; + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "feXdRpZ_tgs"); + // return aChallenge; return aChallenge; } /* - CONTENT TYPE - */ + CONTENT TYPE + */ // POST control content type, and Accepting only XML ie. Accept header of `application/xml` // control content type to create with - XML public static ChallengeDefinitionData postCreateTodoWithXMLAcceptXML(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos XML", - "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/xml`, and Accepting only XML ie. Accept header of `application/xml`"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/content-type-header/post-todos-xml"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos XML", + "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/xml`, and Accepting only XML ie. Accept header of `application/xml`"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/content-type-header/post-todos-xml"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "2-KBYHwb7MM"); return aChallenge; } // control content type to create with - JSON public static ChallengeDefinitionData postCreateTodoWithJsonAcceptJson(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos JSON", - "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/json`, and Accepting only JSON ie. Accept header of `application/json`"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/content-type-header/post-todos-json"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos JSON", + "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/json`, and Accepting only JSON ie. Accept header of `application/json`"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/content-type-header/post-todos-json"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "VS9qIhgp51Q"); return aChallenge; } // content type not supported 415 e.g. form encoded public static ChallengeDefinitionData postCreateUnsupportedContentType415(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos (415)", - "Issue a POST request on the `/todos` end point with an unsupported content type to generate a 415 status code"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/content-type-header/post-todos-415"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos (415)", + "Issue a POST request on the `/todos` end point with an unsupported content type to generate a 415 status code"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/content-type-header/post-todos-415"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "L8H-vkbXyr0"); return aChallenge; } - /* - MIXED CONTENT AND ACCEPT TYPES - */ + MIXED CONTENT AND ACCEPT TYPES + */ // content type XML - accept type JSON public static ChallengeDefinitionData postTodosXmlToJson201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos XML to JSON", - "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/xml` but Accept `application/json`"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/mix-accept-content/post-xml-accept-json"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos XML to JSON", + "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/xml` but Accept `application/json`"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/mix-accept-content/post-xml-accept-json"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "kfe7VtaV7u0"); return aChallenge; } // content type JSON - accept type XML public static ChallengeDefinitionData postTodosJsonToXml201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /todos JSON to XML", - "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/json` but Accept `application/xml`"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/mix-accept-content/post-json-accept-xml"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /todos JSON to XML", + "Issue a POST request on the `/todos` end point to create a todo using Content-Type `application/json` but Accept `application/xml`"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/mix-accept-content/post-json-accept-xml"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "uw1Jq8t1em4"); return aChallenge; } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PutChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PutChallenges.java index f1a5c96c..ed38339a 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PutChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/PutChallenges.java @@ -5,77 +5,106 @@ public class PutChallenges { public static ChallengeDefinitionData putTodosId400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /todos/{id} (400)", - "Issue a PUT request to unsuccessfully create a todo"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /todos/{id} (400)", + "Issue a PUT request to unsuccessfully create a todo"); aChallenge.addHint("Add a JSON payload in the request", ""); - aChallenge.addHint("If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", ""); + aChallenge.addHint( + "If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", + ""); aChallenge.addHint("Do not include an 'id' in the payload", ""); aChallenge.addHint("The id in the URL should not exist", ""); aChallenge.addHint("You must add an X-CHALLENGER header for a valid session", ""); - aChallenge.addSolutionLink("Send a PUT request to /todos/{id} with a valid creation payload", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/put-create/put-todos-400-create"); + aChallenge.addSolutionLink( + "Send a PUT request to /todos/{id} with a valid creation payload", "", ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/put-create/put-todos-400-create"); // todo: create video solution for PUT todos 400 challenge - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); return aChallenge; } /* - UPDATE TODO CHALLENGES - */ + UPDATE TODO CHALLENGES + */ public static ChallengeDefinitionData putTodosIdFull200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /todos/{id} full (200)", - "Issue a PUT request to update an existing todo with a complete payload i.e. title, description and donestatus."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /todos/{id} full (200)", + "Issue a PUT request to update an existing todo with a complete payload i.e. title, description and donestatus."); aChallenge.addHint("Add a JSON payload in the request", ""); - aChallenge.addHint("If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", ""); + aChallenge.addHint( + "If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", + ""); aChallenge.addHint("Do not include an 'id' in the payload", ""); - - aChallenge.addSolutionLink("Send a PUT request to /todos/{id} with a full payload. Do not attempt to change the id.", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/put-update/put-todos-id-200-update-full"); + aChallenge.addSolutionLink( + "Send a PUT request to /todos/{id} with a full payload. Do not attempt to change the id.", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/put-update/put-todos-id-200-update-full"); // todo: create solution video for PUT todos full 200 challenge - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); return aChallenge; } public static ChallengeDefinitionData putTodosIdPartial200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /todos/{id} partial (200)", - "Issue a PUT request to update an existing todo with just mandatory items in payload i.e. title."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /todos/{id} partial (200)", + "Issue a PUT request to update an existing todo with just mandatory items in payload i.e. title."); aChallenge.addHint("Add a JSON payload in the request", ""); - aChallenge.addHint("If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", ""); + aChallenge.addHint( + "If you don't know the format of the payload, use the response from a GET /todos/{id} request and amend it", + ""); aChallenge.addHint("Do not include an 'id' in the payload", ""); - aChallenge.addSolutionLink("Send a PUT request to /todos/{id} with a partial payload. Mandatory field title must be included.", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/put-update/put-todos-id-200-update-partial"); + aChallenge.addSolutionLink( + "Send a PUT request to /todos/{id} with a partial payload. Mandatory field title must be included.", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/put-update/put-todos-id-200-update-partial"); // todo: create solution video for PUT todos partial 200 challenge - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); return aChallenge; } public static ChallengeDefinitionData putTodosIdNoTitle400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /todos/{id} no title (400)", - "Issue a PUT request to fail to update an existing todo because title is missing in payload."); - - - aChallenge.addHint("Title is required for Put requests because they are idempotent. You can amend using POST without a title, but not using a PUT."); - - aChallenge.addSolutionLink("Send a PUT request to /todos/{id} without a title field in the payload.", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/put-update/put-todos-id-400-no-title"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /todos/{id} no title (400)", + "Issue a PUT request to fail to update an existing todo because title is missing in payload."); + + aChallenge.addHint( + "Title is required for Put requests because they are idempotent. You can amend using POST without a title, but not using a PUT."); + + aChallenge.addSolutionLink( + "Send a PUT request to /todos/{id} without a title field in the payload.", "", ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/put-update/put-todos-id-400-no-title"); // todo: create solution video for PUT todos partial 200 challenge @@ -83,22 +112,28 @@ public static ChallengeDefinitionData putTodosIdNoTitle400(int challengeOrder) { } public static ChallengeDefinitionData putTodosIdNonMatchedIdsAmend400(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PUT /todos/{id} no amend id (400)", - "Issue a PUT request to fail to update an existing todo because id different in payload."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PUT /todos/{id} no amend id (400)", + "Issue a PUT request to fail to update an existing todo because id different in payload."); // todo: create solution for PUT todos partial 200 challenge aChallenge.addHint("ID is auto generated you can not amend it in the payload."); - aChallenge.addHint("If you have a different id in the payload from the url then this is viewed as an amendment and you can not amend an auto generated field."); - - - aChallenge.addSolutionLink("Send a PUT request to /todos/{id} with a different id in the url than in the payload.", "", ""); - aChallenge.addSolutionLink("Read Solution", "HREF", "/apichallenges/solutions/put-update/put-todos-id-400-no-amend-id"); - - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); + aChallenge.addHint( + "If you have a different id in the payload from the url then this is viewed as an amendment and you can not amend an auto generated field."); + + aChallenge.addSolutionLink( + "Send a PUT request to /todos/{id} with a different id in the url than in the payload.", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/put-update/put-todos-id-400-no-amend-id"); + + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "T0LFHwavsNA"); // TODO: add solution video for put totos id no amend id return aChallenge; } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/SecretTokenChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/SecretTokenChallenges.java index bde94fbf..ab01d6e0 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/SecretTokenChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/SecretTokenChallenges.java @@ -6,133 +6,167 @@ public class SecretTokenChallenges { // POST /secret/token with incorrect username and password credentials get 401 public static ChallengeDefinitionData createSecretTokenNotAuthenticated401(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /secret/token (401)", - "Issue a POST request on the `/secret/token` end point and receive 401 when Basic auth username/password is not admin/password"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /secret/token (401)", + "Issue a POST request on the `/secret/token` end point and receive 401 when Basic auth username/password is not admin/password"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authentication/post-secret-401"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/authentication/post-secret-401"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "RSQGADU3SLA"); return aChallenge; } // POST /secret/token with correct username and password credentials get secret token 201 public static ChallengeDefinitionData createSecretTokenAuthenticated201(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /secret/token (201)", - "Issue a POST request on the `/secret/token` end point and receive 201 when Basic auth username/password is admin/password"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /secret/token (201)", + "Issue a POST request on the `/secret/token` end point and receive 201 when Basic auth username/password is admin/password"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authentication/post-secret-201"); + aChallenge.addSolutionLink( + "Read Solution", "HREF", "/apichallenges/solutions/authentication/post-secret-201"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "J2GQiuEfHkI"); return aChallenge; } - // GET /secret/note with no token and 403 public static ChallengeDefinitionData forbiddenNotAuthorized403(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /secret/note (403)", - "Issue a GET request on the `/secret/note` end point and receive 403 when X-AUTH-TOKEN does not match a valid token"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /secret/note (403)", + "Issue a GET request on the `/secret/note` end point and receive 403 when X-AUTH-TOKEN does not match a valid token"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/get-secret-note-403"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/get-secret-note-403"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "77mnUQezdas"); return aChallenge; } // GET /secret/note with invalid token and 401 public static ChallengeDefinitionData invalidRequestNoAuthHeader401(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /secret/note (401)", - "Issue a GET request on the `/secret/note` end point and receive 401 when no X-AUTH-TOKEN header present"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /secret/note (401)", + "Issue a GET request on the `/secret/note` end point and receive 401 when no X-AUTH-TOKEN header present"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/get-secret-note-401"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/get-secret-note-401"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "__uZlQZ48io"); return aChallenge; } // GET /secret/note with token and see token public static ChallengeDefinitionData authorizedGet200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /secret/note (200)", - "Issue a GET request on the `/secret/note` end point receive 200 when valid X-AUTH-TOKEN used - response body should contain the note"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /secret/note (200)", + "Issue a GET request on the `/secret/note` end point receive 200 when valid X-AUTH-TOKEN used - response body should contain the note"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/get-secret-note-200"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/get-secret-note-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "2uRpzr2OmEY"); return aChallenge; } // POST /secret/note with token and update secret note public static ChallengeDefinitionData authorizedUpdate200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /secret/note (200)", - "Issue a POST request on the `/secret/note` end point with a note payload e.g. {\"note\":\"my note\"} and receive 200 when valid X-AUTH-TOKEN used. Note is maximum length 100 chars and will be truncated when stored."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /secret/note (200)", + "Issue a POST request on the `/secret/note` end point with a note payload e.g. {\"note\":\"my note\"} and receive 200 when valid X-AUTH-TOKEN used. Note is maximum length 100 chars and will be truncated when stored."); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/post-secret-note-200"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/post-secret-note-200"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "A9T9yjzEOEE"); return aChallenge; } // POST /secret/note with invalid token and 401 public static ChallengeDefinitionData postMissingTokenAuth401(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /secret/note (401)", - "Issue a POST request on the `/secret/note` end point with a note payload {\"note\":\"my note\"} and receive 401 when no X-AUTH-TOKEN present"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /secret/note (401)", + "Issue a POST request on the `/secret/note` end point with a note payload {\"note\":\"my note\"} and receive 401 when no X-AUTH-TOKEN present"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/post-secret-note-401-403"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/post-secret-note-401-403"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "76U5TEvLzLI"); return aChallenge; } // POST /secret/note with no token and 403 public static ChallengeDefinitionData postInvalidTokenAuth401(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /secret/note (403)", - "Issue a POST request on the `/secret/note` end point with a note payload {\"note\":\"my note\"} and receive 403 when X-AUTH-TOKEN does not match a valid token"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /secret/note (403)", + "Issue a POST request on the `/secret/note` end point with a note payload {\"note\":\"my note\"} and receive 403 when X-AUTH-TOKEN does not match a valid token"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/post-secret-note-401-403"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/post-secret-note-401-403"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "76U5TEvLzLI"); return aChallenge; } // GET /secret/note with bearer token authorization public static ChallengeDefinitionData getWithValidBearerToken200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /secret/note (Bearer)", - "Issue a GET request on the `/secret/note` end point receive 200 when using the X-AUTH-TOKEN value as an Authorization Bearer token - response body should contain the note"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /secret/note (Bearer)", + "Issue a GET request on the `/secret/note` end point receive 200 when using the X-AUTH-TOKEN value as an Authorization Bearer token - response body should contain the note"); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/get-post-secret-note-bearer"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/get-post-secret-note-bearer"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "8GsMTZxEItw"); return aChallenge; } // POST /secret/note with token and update secret note public static ChallengeDefinitionData postUpdateWithValidBearerToken200(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /secret/note (Bearer)", - "Issue a POST request on the `/secret/note` end point with a note payload e.g. {\"note\":\"my note\"} and receive 200 when valid X-AUTH-TOKEN value used as an Authorization Bearer token. Status code 200 received. Note is maximum length 100 chars and will be truncated when stored."); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /secret/note (Bearer)", + "Issue a POST request on the `/secret/note` end point with a note payload e.g. {\"note\":\"my note\"} and receive 200 when valid X-AUTH-TOKEN value used as an Authorization Bearer token. Status code 200 received. Note is maximum length 100 chars and will be truncated when stored."); aChallenge.addHint("Remember to add your X-CHALLENGER guid header"); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/authorization/get-post-secret-note-bearer"); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/authorization/get-post-secret-note-bearer"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "8GsMTZxEItw"); return aChallenge; } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java index 2b1c9f0b..97ac60a0 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challenges/definitions/StatusCodeChallenges.java @@ -7,96 +7,132 @@ public class StatusCodeChallenges { // extra status code challenges // method not allowed - 405 public static ChallengeDefinitionData methodNotAllowed405UsingDelete(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "DELETE /heartbeat (405)", - "Issue a DELETE request on the `/heartbeat` end point and receive 405 (Method Not Allowed)"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "DELETE /heartbeat (405)", + "Issue a DELETE request on the `/heartbeat` end point and receive 405 (Method Not Allowed)"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } // cannot process request server error 500 public static ChallengeDefinitionData serverError500UsingPatch(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "PATCH /heartbeat (500)", - "Issue a PATCH request on the `/heartbeat` end point and receive 500 (internal server error)"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "PATCH /heartbeat (500)", + "Issue a PATCH request on the `/heartbeat` end point and receive 500 (internal server error)"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } // 501 public static ChallengeDefinitionData notImplemented501UsingTrace(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "TRACE /heartbeat (501)", - "Issue a TRACE request on the `/heartbeat` end point and receive 501 (Not Implemented)"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "TRACE /heartbeat (501)", + "Issue a TRACE request on the `/heartbeat` end point and receive 501 (Not Implemented)"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } // No Content 204 - ping public static ChallengeDefinitionData noContent204UsingGet(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "GET /heartbeat (204)", - "Issue a GET request on the `/heartbeat` end point and receive 204 when server is running"); - - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "GET /heartbeat (204)", + "Issue a GET request on the `/heartbeat` end point and receive 204 when server is running"); + + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/status-codes/status-codes-405-500-501-204"); aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } - /* - Status codes using method overrides - */ + Status codes using method overrides + */ public static ChallengeDefinitionData overridePostToPatchFor500(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /heartbeat as PATCH (500)", - "Issue a POST request on the `/heartbeat` end point and receive 500 when you override the Method Verb to a PATCH"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /heartbeat as PATCH (500)", + "Issue a POST request on the `/heartbeat` end point and receive 500 when you override the Method Verb to a PATCH"); aChallenge.addHint("Use a normal POST Request, but add an X-HTTP-Method-Override header"); - aChallenge.addSolutionLink("Add a header 'X-HTTP-Method-Override: PATCH' to a POST /heartbeat request", "",""); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/method-override/all-method-overrides"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); + aChallenge.addSolutionLink( + "Add a header 'X-HTTP-Method-Override: PATCH' to a POST /heartbeat request", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/method-override/all-method-overrides"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } public static ChallengeDefinitionData overridePostToDeleteFor405(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /heartbeat as DELETE (405)", - "Issue a POST request on the `/heartbeat` end point and receive 405 when you override the Method Verb to a DELETE"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /heartbeat as DELETE (405)", + "Issue a POST request on the `/heartbeat` end point and receive 405 when you override the Method Verb to a DELETE"); aChallenge.addHint("Use a normal POST Request, but add an X-HTTP-Method-Override header"); - aChallenge.addSolutionLink("Add a header 'X-HTTP-Method-Override: DELETE' to a POST /heartbeat request", "",""); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/method-override/all-method-overrides"); - //aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); + aChallenge.addSolutionLink( + "Add a header 'X-HTTP-Method-Override: DELETE' to a POST /heartbeat request", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/method-override/all-method-overrides"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } // 501 public static ChallengeDefinitionData overridePostToTraceFor501(int challengeOrder) { - ChallengeDefinitionData aChallenge = new ChallengeDefinitionData( - ChallengeRenderer.renderChallengeNumber(challengeOrder), - "POST /heartbeat as Trace (501)", - "Issue a POST request on the `/heartbeat` end point and receive 501 (Not Implemented) when you override the Method Verb to a TRACE"); + ChallengeDefinitionData aChallenge = + new ChallengeDefinitionData( + ChallengeRenderer.renderChallengeNumber(challengeOrder), + "POST /heartbeat as Trace (501)", + "Issue a POST request on the `/heartbeat` end point and receive 501 (Not Implemented) when you override the Method Verb to a TRACE"); aChallenge.addHint("Use a normal POST Request, but add an X-HTTP-Method-Override header"); - aChallenge.addSolutionLink("Add a header 'X-HTTP-Method-Override: TRACE' to a POST /heartbeat request", "",""); - aChallenge.addSolutionLink("Read Solution", "HREF","/apichallenges/solutions/method-override/all-method-overrides"); + aChallenge.addSolutionLink( + "Add a header 'X-HTTP-Method-Override: TRACE' to a POST /heartbeat request", + "", + ""); + aChallenge.addSolutionLink( + "Read Solution", + "HREF", + "/apichallenges/solutions/method-override/all-method-overrides"); -// aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); + // aChallenge.addSolutionLink("Watch Insomnia Solution", "YOUTUBE", "SGfKVFdylVI"); return aChallenge; } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java index 0923c220..6f7a8b9e 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/AuthRoutes.java @@ -1,21 +1,25 @@ package uk.co.compendiumdev.challenge.challengesrouting; +import static spark.Spark.*; + +import java.util.List; import spark.Route; -import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.AcceptHeaderParser; -import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.BasicAuthHeaderParser; -import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.BearerAuthHeaderParser; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.challengers.Challengers; import uk.co.compendiumdev.thingifier.Thingifier; +import uk.co.compendiumdev.thingifier.api.docgen.RoutingDefinition; +import uk.co.compendiumdev.thingifier.api.docgen.RoutingStatus; +import uk.co.compendiumdev.thingifier.api.docgen.RoutingVerb; import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; +import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.api.http.*; import uk.co.compendiumdev.thingifier.api.http.bodyparser.BodyParser; +import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.AcceptHeaderParser; +import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.BasicAuthHeaderParser; +import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.BearerAuthHeaderParser; import uk.co.compendiumdev.thingifier.api.http.headers.headerparser.ContentTypeHeaderParser; import uk.co.compendiumdev.thingifier.api.response.ApiResponse; import uk.co.compendiumdev.thingifier.api.restapihandlers.ThingCreation; -import uk.co.compendiumdev.thingifier.api.docgen.RoutingDefinition; -import uk.co.compendiumdev.thingifier.api.docgen.RoutingStatus; -import uk.co.compendiumdev.thingifier.api.docgen.RoutingVerb; import uk.co.compendiumdev.thingifier.application.internalhttpconversion.HttpApiResponseToSpark; import uk.co.compendiumdev.thingifier.application.internalhttpconversion.SparkToHttpApiRequest; import uk.co.compendiumdev.thingifier.core.EntityRelModel; @@ -27,13 +31,8 @@ import uk.co.compendiumdev.thingifier.core.domain.instances.EntityInstanceDraft; import uk.co.compendiumdev.thingifier.core.domain.instances.validation.EntityInstanceStateValidator; import uk.co.compendiumdev.thingifier.core.reporting.ValidationReport; -import uk.co.compendiumdev.thingifier.api.ermodelconversion.JsonThing; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; -import java.util.List; - -import static spark.Spark.*; - // TODO: This should be using a Thingifier to do the work of XML JSON etc... like the simulation public class AuthRoutes { private Thingifier secretNoteStore; @@ -42,8 +41,8 @@ public class AuthRoutes { private JsonThing jsonThing; private final EntityInstanceStateValidator stateValidator = new EntityInstanceStateValidator(); - public void configure(final Challengers challengers, - final ThingifierApiDocumentationDefn apiDefn) { + public void configure( + final Challengers challengers, final ThingifierApiDocumentationDefn apiDefn) { // authentication and authorisation // - create a 'secret' note which can be stored against session using an auth token @@ -52,273 +51,313 @@ public void configure(final Challengers challengers, this.secretNote = this.secretNoteStore.defineThing("secretnote", "secretnotes"); this.secretNote.addFields( - Field.is("note", FieldType.STRING). - makeMandatory(). - withValidation(new MaximumLengthValidationRule(100)). - withDefaultValue("") - ); + Field.is("note", FieldType.STRING) + .makeMandatory() + .withValidation(new MaximumLengthValidationRule(100)) + .withDefaultValue("")); this.httpApi = new ThingifierHttpApi(this.secretNoteStore); this.jsonThing = new JsonThing(this.secretNoteStore.apiConfig().jsonOutput()); + SimpleSparkRouteCreator.addHandler( + "/secret/token", + "options", + (request, result) -> { + result.status(204); + // disallow POST, DELETE, PATCH, TRACE + result.header("Allow", "POST, OPTIONS"); + return ""; + }); - SimpleSparkRouteCreator.addHandler("/secret/token", "options", (request, result) ->{ - result.status(204); - // disallow POST, DELETE, PATCH, TRACE - result.header("Allow", "POST, OPTIONS"); - return ""; - }); - - - // TODO: this all feels too tightly coupled to SparkJava we should have our own routing internally that spark delegates too + // TODO: this all feels too tightly coupled to SparkJava we should have our own routing + // internally that spark delegates too // POST /secret/token with basic auth to get a secret/token to use as X-AUTH-TOKEN header // todo: or {username, password} payload - post("/secret/token", (request, result) -> { - - BasicAuthHeaderParser basicAuth = new BasicAuthHeaderParser(request.headers("Authorization")); - - // admin/password as default username:password - if(!basicAuth.matches("admin","password")){ - result.raw().setHeader("WWW-Authenticate","Basic realm=\"User Visible Realm\""); - result.status(401); - return ""; - } + post( + "/secret/token", + (request, result) -> { + BasicAuthHeaderParser basicAuth = + new BasicAuthHeaderParser(request.headers("Authorization")); + + // admin/password as default username:password + if (!basicAuth.matches("admin", "password")) { + result.raw() + .setHeader( + "WWW-Authenticate", "Basic realm=\"User Visible Realm\""); + result.status(401); + return ""; + } - ChallengerAuthData challenger = challengers.getChallenger(request.headers("X-CHALLENGER")); + ChallengerAuthData challenger = + challengers.getChallenger(request.headers("X-CHALLENGER")); - if(challenger==null){ - result.status(401); - XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); - return ""; - } + if (challenger == null) { + result.status(401); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + return ""; + } - // if no header X-AUTH-TOKEN then grant one - result.raw().setHeader("X-AUTH-TOKEN", challenger.getXAuthToken()); - result.status(201); - return ""; - }); + // if no header X-AUTH-TOKEN then grant one + result.raw().setHeader("X-AUTH-TOKEN", challenger.getXAuthToken()); + result.status(201); + return ""; + }); - SimpleSparkRouteCreator.routeStatusWhenNot(405, "/secret/token", List.of("post", "options")); + SimpleSparkRouteCreator.routeStatusWhenNot( + 405, "/secret/token", List.of("post", "options")); apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.POST, - "/secret/token", - RoutingStatus.returnedFromCall(), - null).addDocumentation("POST /secret/token with basic auth to get a secret/token to use as X-AUTH-TOKEN header, to allow access to the /secret/note end points."). - addPossibleStatuses(201,401). - secureWithBasicAuth() - ); - - - - - - - - - + RoutingVerb.POST, + "/secret/token", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "POST /secret/token with basic auth to get a secret/token to use as X-AUTH-TOKEN header, to allow access to the /secret/note end points.") + .addPossibleStatuses(201, 401) + .secureWithBasicAuth()); // todo: GET /secret/token returns the secret token or 401 if not authenticated - // POST /secret/note GET /secret/note - limit note to 100 chars // no auth token will receive a 403 // auth token which does not match the session will receive a 401 // header X-AUTH-TOKEN: token given - if token not found (then) 401 - SimpleSparkRouteCreator.addHandler("/secret/note", "options", (request, result) ->{ - result.status(204); - // disallow POST, DELETE, PATCH, TRACE - result.header("Allow", "GET, HEAD, POST, OPTIONS"); - return ""; - }); + SimpleSparkRouteCreator.addHandler( + "/secret/note", + "options", + (request, result) -> { + result.status(204); + // disallow POST, DELETE, PATCH, TRACE + result.header("Allow", "GET, HEAD, POST, OPTIONS"); + return ""; + }); + + Route getSecretNote = + (request, result) -> { + String authToken = request.headers("X-AUTH-TOKEN"); + final String authorization = request.headers("Authorization"); + + result.header("Content-Type", "application/json"); + + ChallengerAuthData challenger = + challengers.getChallenger(request.headers("X-CHALLENGER")); + + if (challenger == null) { + result.status(401); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + return ""; + } + // authorization bearer token will take precedence over X-AUTH-HEADER + if (authorization != null && !authorization.isEmpty()) { + final BearerAuthHeaderParser bearerToken = + new BearerAuthHeaderParser(authorization); + if (bearerToken.isBearerToken() && bearerToken.isValid()) { + authToken = bearerToken.getToken(); + } + } - Route getSecretNote = (request, result) -> { - String authToken = request.headers("X-AUTH-TOKEN"); - final String authorization = request.headers("Authorization"); + if (authToken == null || authToken.isEmpty()) { + result.status(401); + return ""; + } - result.header("Content-Type", "application/json"); + if (!authToken.contentEquals(challenger.getXAuthToken())) { + result.status(403); // given token is not allowed to access anything + return ""; + } - ChallengerAuthData challenger = challengers.getChallenger(request.headers("X-CHALLENGER")); + AcceptHeaderParser acceptHeaderParser = + new AcceptHeaderParser(request.headers("ACCEPT")); + if (!acceptHeaderParser.missingAcceptHeader() + && !acceptHeaderParser.isSupportedHeader()) { + result.status(406); + return ""; + } - if(challenger==null){ - result.status(401); - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - return ""; - } + final HttpApiRequest myRequest = SparkToHttpApiRequest.convert(request); - // authorization bearer token will take precedence over X-AUTH-HEADER - if(authorization!=null && !authorization.isEmpty()){ - final BearerAuthHeaderParser bearerToken = new BearerAuthHeaderParser(authorization); - if(bearerToken.isBearerToken() && bearerToken.isValid()){ - authToken = bearerToken.getToken(); - } - } + EntityInstance note = + EntityInstance.fromDraft( + EntityInstanceDraft.forEntity(secretNote) + .withField("note", challenger.getNote())); + final ApiResponse response = ApiResponse.success().returnSingleInstance(note); - if(authToken==null || authToken.isEmpty()){ - result.status(401); - return ""; - } + final HttpApiResponse httpApiResponse = + new HttpApiResponse( + myRequest.getHeaders(), + response, + jsonThing, + this.secretNoteStore.apiConfig()); - if(!authToken.contentEquals(challenger.getXAuthToken())){ - result.status(403); // given token is not allowed to access anything - return ""; - } + return HttpApiResponseToSpark.convert(httpApiResponse, result); - AcceptHeaderParser acceptHeaderParser = new AcceptHeaderParser(request.headers("ACCEPT")); - if(!acceptHeaderParser.missingAcceptHeader() && !acceptHeaderParser.isSupportedHeader()){ - result.status(406); - return ""; - } + // return resultBasedOnAcceptHeader(result, request.headers("ACCEPT"), + // challenger.getNote()); + }; - final HttpApiRequest myRequest = SparkToHttpApiRequest.convert(request); + get( + "/secret/note", + (request, result) -> { + return getSecretNote.handle(request, result); + }); - EntityInstance note = EntityInstance.fromDraft(EntityInstanceDraft.forEntity(secretNote). - withField("note", challenger.getNote())); - final ApiResponse response = ApiResponse.success().returnSingleInstance(note); + head( + "/secret/note", + (request, result) -> { + getSecretNote.handle(request, result); + return ""; + }); - final HttpApiResponse httpApiResponse = new HttpApiResponse(myRequest.getHeaders(), response, - jsonThing, this.secretNoteStore.apiConfig()); + apiDefn.addRouteToDocumentation( + new RoutingDefinition( + RoutingVerb.GET, + "/secret/note", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "GET /secret/note with X-AUTH-TOKEN to return the secret note for the user.") + .addPossibleStatuses(200, 401, 403) + .addCustomHeader("X-AUTH-TOKEN", "string")); + + post( + "/secret/note", + (request, result) -> { + final String authorization = request.headers("Authorization"); + String authToken = request.headers("X-AUTH-TOKEN"); + + AcceptHeaderParser acceptHeaderParser = + new AcceptHeaderParser(request.headers("ACCEPT")); + if (!acceptHeaderParser.missingAcceptHeader() + && !acceptHeaderParser.isSupportedHeader()) { + result.status(406); + return ""; + } - return HttpApiResponseToSpark.convert(httpApiResponse, result); + ContentTypeHeaderParser contentTypeParser = + new ContentTypeHeaderParser(request.headers("CONTENT-TYPE")); + if (!contentTypeParser.isJSON() && !contentTypeParser.isXML()) { + result.status(415); + return ""; + } - //return resultBasedOnAcceptHeader(result, request.headers("ACCEPT"), challenger.getNote()); - }; + // todo: if no X-CHALLENGER provided then, search memory for authToken and use + // associated + // challenger + ChallengerAuthData challenger = + challengers.getChallenger(request.headers("X-CHALLENGER")); - get("/secret/note", (request, result) -> { - return getSecretNote.handle(request,result); - }); + if (challenger == null) { + result.status(401); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + return ""; + } - head("/secret/note", (request, result) -> { - getSecretNote.handle(request,result); - return ""; - }); + result.raw().setHeader("X-CHALLENGER", challenger.getXChallenger()); + // set content-type header for error responses + if (acceptHeaderParser.hasAPreferenceForXml()) { + result.header("Content-Type", "application/xml"); + } else { + result.header("Content-Type", "application/json"); + } - apiDefn.addRouteToDocumentation( - new RoutingDefinition( - RoutingVerb.GET, - "/secret/note", - RoutingStatus.returnedFromCall(), - null).addDocumentation( - "GET /secret/note with X-AUTH-TOKEN to return the secret note for the user."). - addPossibleStatuses(200,401,403). - addCustomHeader("X-AUTH-TOKEN","string") - ); - - - post("/secret/note", (request, result) -> { - - final String authorization = request.headers("Authorization"); - String authToken = request.headers("X-AUTH-TOKEN"); - - AcceptHeaderParser acceptHeaderParser = new AcceptHeaderParser(request.headers("ACCEPT")); - if(!acceptHeaderParser.missingAcceptHeader() && !acceptHeaderParser.isSupportedHeader()){ - result.status(406); - return ""; - } - - ContentTypeHeaderParser contentTypeParser = new ContentTypeHeaderParser(request.headers("CONTENT-TYPE")); - if(!contentTypeParser.isJSON() && !contentTypeParser.isXML()){ - result.status(415); - return ""; - } - - // todo: if no X-CHALLENGER provided then, search memory for authToken and use associated - // challenger - ChallengerAuthData challenger = challengers.getChallenger(request.headers("X-CHALLENGER")); - - if(challenger==null){ - result.status(401); - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - return ""; - } - - result.raw().setHeader("X-CHALLENGER", challenger.getXChallenger()); - // set content-type header for error responses - if(acceptHeaderParser.hasAPreferenceForXml()){ - result.header("Content-Type", "application/xml"); - }else{ - result.header("Content-Type", "application/json"); - } - - // authorization bearer token will take precedence over X-AUTH-HEADER - if(authorization!=null && !authorization.isEmpty()){ - final BearerAuthHeaderParser bearerToken = new BearerAuthHeaderParser(authorization); - if(bearerToken.isBearerToken() && bearerToken.isValid()){ - authToken = bearerToken.getToken(); - } - } - - if(authToken==null || authToken.isEmpty()){ - result.status(401); - return ""; - } - - if(!authToken.contentEquals(challenger.getXAuthToken())){ - result.status(403); // given token is not allowed to access anything - return ""; - } - - if(!acceptHeaderParser.missingAcceptHeader() && !acceptHeaderParser.isSupportedHeader()){ - result.status(406); - return ""; - } - - final HttpApiRequest myRequest = SparkToHttpApiRequest.convert(request); - HttpApiResponse httpApiResponse = this.httpApi.validateRequestSyntax(myRequest, - ThingifierHttpApi.HttpVerb.POST); - - // TODO: this should be simpler to use by apps building on thingifier - if(httpApiResponse==null) { - - ApiResponse response=null; - response = new ThingCreation(this.secretNoteStore).with( - new BodyParser(myRequest, List.of("secretnote")), - this.secretNote, EntityRelModel.DEFAULT_DATABASE_NAME); - if (!response.isErrorResponse()) { - - EntityInstance returnedInstance = response.getReturnedInstance(); - final List protectedFieldNames = returnedInstance.getEntity().getFieldNamesOfType(FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); - ValidationReport validity = stateValidator.validateFields( - returnedInstance, protectedFieldNames, false); - validity.combine(stateValidator.validateRelationships(returnedInstance)); - - this.secretNoteStore.deleteThing(response.getReturnedInstance(), EntityRelModel.DEFAULT_DATABASE_NAME); - - if (!validity.isValid()) { - response = ApiResponse.error(400, validity.getCombinedErrorMessages()); - }else{ - final EntityInstance postedThing = response.getReturnedInstance(); - response = ApiResponse.success().returnSingleInstance(postedThing); - challenger.setNote(response.getReturnedInstance(). - getFieldValue("note").asString()); + // authorization bearer token will take precedence over X-AUTH-HEADER + if (authorization != null && !authorization.isEmpty()) { + final BearerAuthHeaderParser bearerToken = + new BearerAuthHeaderParser(authorization); + if (bearerToken.isBearerToken() && bearerToken.isValid()) { + authToken = bearerToken.getToken(); + } } - } - httpApiResponse = new HttpApiResponse(myRequest.getHeaders(), response, - jsonThing, this.secretNoteStore.apiConfig()); - } + if (authToken == null || authToken.isEmpty()) { + result.status(401); + return ""; + } + + if (!authToken.contentEquals(challenger.getXAuthToken())) { + result.status(403); // given token is not allowed to access anything + return ""; + } + if (!acceptHeaderParser.missingAcceptHeader() + && !acceptHeaderParser.isSupportedHeader()) { + result.status(406); + return ""; + } - return HttpApiResponseToSpark.convert(httpApiResponse, result); + final HttpApiRequest myRequest = SparkToHttpApiRequest.convert(request); + HttpApiResponse httpApiResponse = + this.httpApi.validateRequestSyntax( + myRequest, ThingifierHttpApi.HttpVerb.POST); + + // TODO: this should be simpler to use by apps building on thingifier + if (httpApiResponse == null) { + + ApiResponse response = null; + response = + new ThingCreation(this.secretNoteStore) + .with( + new BodyParser(myRequest, List.of("secretnote")), + this.secretNote, + EntityRelModel.DEFAULT_DATABASE_NAME); + if (!response.isErrorResponse()) { + + EntityInstance returnedInstance = response.getReturnedInstance(); + final List protectedFieldNames = + returnedInstance + .getEntity() + .getFieldNamesOfType( + FieldType.AUTO_INCREMENT, FieldType.AUTO_GUID); + ValidationReport validity = + stateValidator.validateFields( + returnedInstance, protectedFieldNames, false); + validity.combine( + stateValidator.validateRelationships(returnedInstance)); + + this.secretNoteStore.deleteThing( + response.getReturnedInstance(), + EntityRelModel.DEFAULT_DATABASE_NAME); + + if (!validity.isValid()) { + response = + ApiResponse.error(400, validity.getCombinedErrorMessages()); + } else { + final EntityInstance postedThing = response.getReturnedInstance(); + response = ApiResponse.success().returnSingleInstance(postedThing); + challenger.setNote( + response.getReturnedInstance() + .getFieldValue("note") + .asString()); + } + } + + httpApiResponse = + new HttpApiResponse( + myRequest.getHeaders(), + response, + jsonThing, + this.secretNoteStore.apiConfig()); + } - }); + return HttpApiResponseToSpark.convert(httpApiResponse, result); + }); - SimpleSparkRouteCreator.routeStatusWhenNot(405, "/secret/note", List.of("get", "post", "head", "options")); + SimpleSparkRouteCreator.routeStatusWhenNot( + 405, "/secret/note", List.of("get", "post", "head", "options")); apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.POST, - "/secret/note", - RoutingStatus.returnedFromCall(), - null).addDocumentation("POST /secret/note with X-AUTH-TOKEN, and a payload of `{'note':'contents of note'}` to amend the contents of the secret note."). - addPossibleStatuses(200,400,401,403). - addCustomHeader("X-AUTH-TOKEN","string") - ); + RoutingVerb.POST, + "/secret/note", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "POST /secret/note with X-AUTH-TOKEN, and a payload of `{'note':'contents of note'}` to amend the contents of the secret note.") + .addPossibleStatuses(200, 400, 401, 403) + .addCustomHeader("X-AUTH-TOKEN", "string")); } - - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerIpAddressTracker.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerIpAddressTracker.java index 029cdf15..8884d5bc 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerIpAddressTracker.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerIpAddressTracker.java @@ -17,12 +17,12 @@ public class ChallengerIpAddressTracker { public ChallengerIpAddressTracker(int maxChallengersPerIp, boolean addressLimitingOn) { maxChallengers = maxChallengersPerIp; ipaddresses = new ConcurrentHashMap<>(); - this.addressLimitingOn=addressLimitingOn; + this.addressLimitingOn = addressLimitingOn; } public void purgeEmptyIpAddresses(Set existingChallengerGuids) { - for(String anIp : ipaddresses.keySet()) { + for (String anIp : ipaddresses.keySet()) { ConcurrentSkipListSet challengerGuids = ipaddresses.get(anIp); List challengersToRemove = new ArrayList<>(); @@ -37,8 +37,8 @@ public void purgeEmptyIpAddresses(Set existingChallengerGuids) { } // purge any empty ip addresses - for(String anIp : ipaddresses.keySet()){ - if(ipaddresses.get(anIp).isEmpty()){ + for (String anIp : ipaddresses.keySet()) { + if (ipaddresses.get(anIp).isEmpty()) { ipaddresses.remove(anIp); } } @@ -46,9 +46,9 @@ public void purgeEmptyIpAddresses(Set existingChallengerGuids) { public boolean hasLimitBeenReachedFor(String ip) { - if(!addressLimitingOn) return false; + if (!addressLimitingOn) return false; - if(countFor(ip) guids = new ConcurrentSkipListSet<>(); guids.add(xChallengerGuid); ipaddresses.put(ip, guids); @@ -80,7 +80,7 @@ public boolean isTrackingIp(String ip) { } public int countFor(String ip) { - if(isTrackingIp(ip)){ + if (isTrackingIp(ip)) { return ipaddresses.get(ip).size(); } return 0; diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java index 7cda1a77..1e5c1275 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengerTrackingRoutes.java @@ -1,6 +1,9 @@ package uk.co.compendiumdev.challenge.challengesrouting; +import static spark.Spark.*; + import com.google.gson.Gson; +import java.util.*; import spark.Route; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.challengers.Challengers; @@ -14,78 +17,81 @@ import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; -import java.util.*; - -import static spark.Spark.*; - public class ChallengerTrackingRoutes { - final int MAX_CHALLENGERS_PER_IP=100; - - - public void configure(final Challengers challengers, - final boolean single_player_mode, - final ThingifierApiDocumentationDefn apiDefn, - final PersistenceLayer persistenceLayer, - final Thingifier thingifier, - ChallengeDefinitions challengeDefinitions){ + final int MAX_CHALLENGERS_PER_IP = 100; + public void configure( + final Challengers challengers, + final boolean single_player_mode, + final ThingifierApiDocumentationDefn apiDefn, + final PersistenceLayer persistenceLayer, + final Thingifier thingifier, + ChallengeDefinitions challengeDefinitions) { - // todo: control max challengers and ip address limiting dynamically through admin interface and via environment variables + // todo: control max challengers and ip address limiting dynamically through admin interface + // and via environment variables // until then, leave this off by passing in false - ChallengerIpAddressTracker ipAddressTracker = new ChallengerIpAddressTracker(MAX_CHALLENGERS_PER_IP, false); - - - - Route getChallengerId = (request, result) -> { - - ChallengerAuthData challenger=null; - String xChallengerGuid = request.params("id"); - - if(xChallengerGuid != null && !xChallengerGuid.trim().isEmpty()){ - challenger = challengers.getChallenger(xChallengerGuid); - if(challenger!=null){ - challenger.touch(); - thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed(challenger.getXChallenger()); - result.status(200); - result.header("content-type", "application/json"); - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - return challenger.asJson(); - }else{ - result.status(404); - } - }else{ - result.status(404); - } - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - return ""; - }; - - SimpleSparkRouteCreator.addHandler("/challenger/:id", "options", (request, result) ->{ - result.status(204); - // disallow POST, DELETE, PATCH, TRACE - result.header("Allow", "GET, PUT, HEAD, OPTIONS"); - return ""; - }); - - SimpleSparkRouteCreator.routeStatusWhenNot(405, "/challenger/:id", List.of("get", "put", "head", "options")); + ChallengerIpAddressTracker ipAddressTracker = + new ChallengerIpAddressTracker(MAX_CHALLENGERS_PER_IP, false); + + Route getChallengerId = + (request, result) -> { + ChallengerAuthData challenger = null; + String xChallengerGuid = request.params("id"); + + if (xChallengerGuid != null && !xChallengerGuid.trim().isEmpty()) { + challenger = challengers.getChallenger(xChallengerGuid); + if (challenger != null) { + challenger.touch(); + thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed( + challenger.getXChallenger()); + result.status(200); + result.header("content-type", "application/json"); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + return challenger.asJson(); + } else { + result.status(404); + } + } else { + result.status(404); + } + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + return ""; + }; + + SimpleSparkRouteCreator.addHandler( + "/challenger/:id", + "options", + (request, result) -> { + result.status(204); + // disallow POST, DELETE, PATCH, TRACE + result.header("Allow", "GET, PUT, HEAD, OPTIONS"); + return ""; + }); + + SimpleSparkRouteCreator.routeStatusWhenNot( + 405, "/challenger/:id", List.of("get", "put", "head", "options")); // refresh challenger to avoid purging - get("/challenger/:id", (request, result) -> { - return getChallengerId.handle(request, result); - }); - - head("/challenger/:id", (request, result) -> { - getChallengerId.handle(request, result); - return ""; - }); - - if(!single_player_mode) { + get( + "/challenger/:id", + (request, result) -> { + return getChallengerId.handle(request, result); + }); + + head( + "/challenger/:id", + (request, result) -> { + getChallengerId.handle(request, result); + return ""; + }); + + if (!single_player_mode) { // make sure the X-CHALLENGER header shows up in the swagger when in multi-user mode apiDefn.addCustomHeaderWhenRouteNotMatches( - new RoutingDefinition(RoutingVerb.POST,"/challenger", null, null), - new HeaderDefinition("X-CHALLENGER", "guid") - ); + new RoutingDefinition(RoutingVerb.POST, "/challenger", null, null), + new HeaderDefinition("X-CHALLENGER", "guid")); } Field guidField = new Field("guid", FieldType.AUTO_GUID); @@ -93,299 +99,342 @@ public void configure(final Challengers challengers, // Document the endpoint apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.GET, - "/challenger/:guid", - RoutingStatus.returnedFromCall(), - null).addDocumentation("Get a challenger in Json format to allow continued tracking of challenges."). - addPossibleStatuses(200,404). - addRequestUrlParam(guidField)); + RoutingVerb.GET, + "/challenger/:guid", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "Get a challenger in Json format to allow continued tracking of challenges.") + .addPossibleStatuses(200, 404) + .addRequestUrlParam(guidField)); // endpoint to restore a saved challenger status from UI - put("/challenger/:id", (request, result) -> { - ChallengerAuthData challenger; - - // for which challenger? - String xChallengerGuid = request.params("id"); - - if(xChallengerGuid==null){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID"); - } - - if(!single_player_mode) { - try { - UUID.fromString(xChallengerGuid); - } catch (Exception e) { - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID " + e.getMessage()); - } - } - - // try and parse payload, fail if nonsense - try { - challenger = new Gson().fromJson(request.body(), ChallengerAuthData.class); - }catch (Exception e){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson(e.getMessage()); - } - - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - - if(challenger==null){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Payload"); - } - - // check payload against id - if(!challenger.getXChallenger().equals(xChallengerGuid)){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("URL GUID does not match payload X-CHALLENGER"); - } - - // does id exist in memory, if so just replace state data - if(challengers.inMemory(xChallengerGuid)){ - ChallengerAuthData existingChallenger = challengers.getChallenger(xChallengerGuid); - existingChallenger.fromData(challenger, challengeDefinitions.getDefinedChallenges()); - result.status(200); - result.header("content-type", "application/json"); - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - return existingChallenger.asJson(); - } - - // need to create a new challenger in memory with this state - // but first... have a limit on the number of challengers an IP address can create - challengers.purgeOldAuthData(); - ipAddressTracker.purgeEmptyIpAddresses(challengers.getChallengerGuids()); - if(ipAddressTracker.hasLimitBeenReachedFor(request.ip())){ - result.status(429); - result.header("content-type", "application/json"); - result.header("X-CHALLENGER", xChallengerGuid); - return ApiResponseError.asAppropriate(request.headers("accept"), "Attempted to create too many challengers, wait and try again later."); - } - - - // create a challenger from the JSON and instantiate the database - ChallengerAuthData newChallenger = new ChallengerAuthData(challengeDefinitions.getDefinedChallenges()).fromData(challenger, challengeDefinitions.getDefinedChallenges()); - challengers.put(newChallenger); - thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed(challenger.getXChallenger()); - - // track challenger against IP - ipAddressTracker.trackAgainstThisIp(request.ip(), xChallengerGuid); - - - result.status(201); - result.header("content-type", "application/json"); - result.header("X-CHALLENGER", xChallengerGuid); - return ""; - }); + put( + "/challenger/:id", + (request, result) -> { + ChallengerAuthData challenger; + + // for which challenger? + String xChallengerGuid = request.params("id"); + + if (xChallengerGuid == null) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID"); + } + + if (!single_player_mode) { + try { + UUID.fromString(xChallengerGuid); + } catch (Exception e) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson( + "Invalid Challenger GUID " + e.getMessage()); + } + } + + // try and parse payload, fail if nonsense + try { + challenger = new Gson().fromJson(request.body(), ChallengerAuthData.class); + } catch (Exception e) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson(e.getMessage()); + } + + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + + if (challenger == null) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson("Invalid Payload"); + } + + // check payload against id + if (!challenger.getXChallenger().equals(xChallengerGuid)) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson( + "URL GUID does not match payload X-CHALLENGER"); + } + + // does id exist in memory, if so just replace state data + if (challengers.inMemory(xChallengerGuid)) { + ChallengerAuthData existingChallenger = + challengers.getChallenger(xChallengerGuid); + existingChallenger.fromData( + challenger, challengeDefinitions.getDefinedChallenges()); + result.status(200); + result.header("content-type", "application/json"); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + return existingChallenger.asJson(); + } + + // need to create a new challenger in memory with this state + // but first... have a limit on the number of challengers an IP address can + // create + challengers.purgeOldAuthData(); + ipAddressTracker.purgeEmptyIpAddresses(challengers.getChallengerGuids()); + if (ipAddressTracker.hasLimitBeenReachedFor(request.ip())) { + result.status(429); + result.header("content-type", "application/json"); + result.header("X-CHALLENGER", xChallengerGuid); + return ApiResponseError.asAppropriate( + request.headers("accept"), + "Attempted to create too many challengers, wait and try again later."); + } + + // create a challenger from the JSON and instantiate the database + ChallengerAuthData newChallenger = + new ChallengerAuthData(challengeDefinitions.getDefinedChallenges()) + .fromData( + challenger, + challengeDefinitions.getDefinedChallenges()); + challengers.put(newChallenger); + thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed( + challenger.getXChallenger()); + + // track challenger against IP + ipAddressTracker.trackAgainstThisIp(request.ip(), xChallengerGuid); + + result.status(201); + result.header("content-type", "application/json"); + result.header("X-CHALLENGER", xChallengerGuid); + return ""; + }); // Document the endpoint apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.PUT, - "/challenger/:guid", - RoutingStatus.returnedFromCall(), - null).addDocumentation("Restore a saved challenger matching the supplied X-CHALLENGER guid to allow continued tracking of challenges.") - .addPossibleStatuses(200,201,400). - addRequestUrlParam(guidField)); - + RoutingVerb.PUT, + "/challenger/:guid", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "Restore a saved challenger matching the supplied X-CHALLENGER guid to allow continued tracking of challenges.") + .addPossibleStatuses(200, 201, 400) + .addRequestUrlParam(guidField)); /* - / challenger + / challenger - */ + */ - SimpleSparkRouteCreator.addHandler("/challenger", "options", (request, result) ->{ - result.status(204); - // disallow POST, DELETE, PATCH, TRACE - result.header("Allow", "POST, OPTIONS"); - return ""; - }); + SimpleSparkRouteCreator.addHandler( + "/challenger", + "options", + (request, result) -> { + result.status(204); + // disallow POST, DELETE, PATCH, TRACE + result.header("Allow", "POST, OPTIONS"); + return ""; + }); // create a challenger - post("/challenger", (request, result) -> { - - if(single_player_mode){ - XChallengerHeader.setResultHeaderBasedOnChallenger(result, challengers.SINGLE_PLAYER.getXChallenger()); - result.raw().setHeader("Location", "/gui/challenges"); - result.status(201); - return ""; - } - - String xChallengerGuid = request.headers("X-CHALLENGER"); - if(xChallengerGuid == null || xChallengerGuid.trim().isEmpty()){ - // create a new challenger with a database - final ChallengerAuthData challenger = challengers.createNewChallenger(); - // create the database for the user - thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed(challenger.getXChallenger()); - XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); - result.raw().setHeader("Location", "/gui/challenges/" + challenger.getXChallenger()); - result.status(201); - }else { - ChallengerAuthData challenger = challengers.getChallenger(xChallengerGuid); - if(challenger==null){ - // if X-CHALLENGER header exists, and is not a known UUID, - // return 404, challenger ID not valid - result.status(404); - }else{ - // challenger already exists, ensure the database does - // create the database for the user - thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed(challenger.getXChallenger()); - // if X-CHALLENGER header exists, and has a valid UUID, and UUID exists, then return 200 - result.raw().setHeader("Location", "/gui/challenges/" + challenger.getXChallenger()); - result.status(200); - } - XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); - } - return ""; - }); + post( + "/challenger", + (request, result) -> { + if (single_player_mode) { + XChallengerHeader.setResultHeaderBasedOnChallenger( + result, challengers.SINGLE_PLAYER.getXChallenger()); + result.raw().setHeader("Location", "/gui/challenges"); + result.status(201); + return ""; + } + + String xChallengerGuid = request.headers("X-CHALLENGER"); + if (xChallengerGuid == null || xChallengerGuid.trim().isEmpty()) { + // create a new challenger with a database + final ChallengerAuthData challenger = challengers.createNewChallenger(); + // create the database for the user + thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed( + challenger.getXChallenger()); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + result.raw() + .setHeader( + "Location", + "/gui/challenges/" + challenger.getXChallenger()); + result.status(201); + } else { + ChallengerAuthData challenger = challengers.getChallenger(xChallengerGuid); + if (challenger == null) { + // if X-CHALLENGER header exists, and is not a known UUID, + // return 404, challenger ID not valid + result.status(404); + } else { + // challenger already exists, ensure the database does + // create the database for the user + thingifier.ensureCreatedAndPopulatedInstanceDatabaseNamed( + challenger.getXChallenger()); + // if X-CHALLENGER header exists, and has a valid UUID, and UUID exists, + // then return 200 + result.raw() + .setHeader( + "Location", + "/gui/challenges/" + challenger.getXChallenger()); + result.status(200); + } + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + } + return ""; + }); apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.POST, - "/challenger", - RoutingStatus.returnedFromCall(), - null). - addDocumentation("Create a challenger using the X-CHALLENGER guid header."). - addPossibleStatuses(200,400,405) - ); + RoutingVerb.POST, + "/challenger", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation("Create a challenger using the X-CHALLENGER guid header.") + .addPossibleStatuses(200, 400, 405)); SimpleSparkRouteCreator.routeStatusWhenNot(405, "/challenger", List.of("post", "options")); /* - The todos restore endpoint - */ + The todos restore endpoint + */ // Document the endpoint apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.GET, - "/challenger/database/:guid", - RoutingStatus.returnedFromCall(), - null).addDocumentation("Get the todo data for the supplied X-CHALLENGER guid to allow later restoration of the todos.") - .addPossibleStatuses(200,400,404). - addRequestUrlParam(guidField)); - - Route getChallengerDatabaseId = (request, result) -> { - ChallengerAuthData challenger; - - // for which challenger? - String xChallengerGuid = request.params("id"); - - if(xChallengerGuid==null){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID"); - } - - if(!single_player_mode) { - try { - UUID.fromString(xChallengerGuid); - } catch (Exception e) { - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID " + e.getMessage()); - } - } - - challenger = challengers.getChallenger(xChallengerGuid); - XChallengerHeader.setResultHeaderBasedOnChallenger(result,challenger); - - if(challenger!=null){ - challenger.touch(); - result.header("content-type", "application/json"); - - if(challengers.getErModel().getRepository(xChallengerGuid)==null){ - result.status(404); - return ApiResponseAsJson.getErrorMessageJson("Challenger database not instantiated " + xChallengerGuid); - } - - result.status(200); - return challengers.getErModel().exportInstanceDataAsJson(xChallengerGuid); - }else{ - result.status(404); - return ApiResponseAsJson.getErrorMessageJson("Challenger not found " + xChallengerGuid); - } - }; - - SimpleSparkRouteCreator.addHandler("/challenger/database/:id", "options", (request, result) ->{ - result.status(204); - // disallow POST, DELETE, PATCH, TRACE - result.header("Allow", "GET, PUT, HEAD, OPTIONS"); - return ""; - }); - - SimpleSparkRouteCreator.routeStatusWhenNot(405, "/challenger/database/:id", List.of("get", "put", "head", "options")); + RoutingVerb.GET, + "/challenger/database/:guid", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "Get the todo data for the supplied X-CHALLENGER guid to allow later restoration of the todos.") + .addPossibleStatuses(200, 400, 404) + .addRequestUrlParam(guidField)); + + Route getChallengerDatabaseId = + (request, result) -> { + ChallengerAuthData challenger; + + // for which challenger? + String xChallengerGuid = request.params("id"); + + if (xChallengerGuid == null) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID"); + } + + if (!single_player_mode) { + try { + UUID.fromString(xChallengerGuid); + } catch (Exception e) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson( + "Invalid Challenger GUID " + e.getMessage()); + } + } + + challenger = challengers.getChallenger(xChallengerGuid); + XChallengerHeader.setResultHeaderBasedOnChallenger(result, challenger); + + if (challenger != null) { + challenger.touch(); + result.header("content-type", "application/json"); + + if (challengers.getErModel().getRepository(xChallengerGuid) == null) { + result.status(404); + return ApiResponseAsJson.getErrorMessageJson( + "Challenger database not instantiated " + xChallengerGuid); + } + + result.status(200); + return challengers.getErModel().exportInstanceDataAsJson(xChallengerGuid); + } else { + result.status(404); + return ApiResponseAsJson.getErrorMessageJson( + "Challenger not found " + xChallengerGuid); + } + }; + + SimpleSparkRouteCreator.addHandler( + "/challenger/database/:id", + "options", + (request, result) -> { + result.status(204); + // disallow POST, DELETE, PATCH, TRACE + result.header("Allow", "GET, PUT, HEAD, OPTIONS"); + return ""; + }); + + SimpleSparkRouteCreator.routeStatusWhenNot( + 405, "/challenger/database/:id", List.of("get", "put", "head", "options")); // add a GET challenger/database/:id in the proper database format - get("/challenger/database/:id", (request, result) -> { - return getChallengerDatabaseId.handle(request, result); - }); - - head("/challenger/database/:id", (request, result) -> { - getChallengerDatabaseId.handle(request, result); - return ""; - }); + get( + "/challenger/database/:id", + (request, result) -> { + return getChallengerDatabaseId.handle(request, result); + }); + head( + "/challenger/database/:id", + (request, result) -> { + getChallengerDatabaseId.handle(request, result); + return ""; + }); // endpoint to restore a saved challenger database from UI - put("/challenger/database/:id", (request, result) -> { - - String xChallengerGuid = request.params("id"); - - // challenger uuid must exist and be valid - if(xChallengerGuid==null){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID"); - } - - if(!single_player_mode) { - try { - UUID.fromString(xChallengerGuid); - } catch (Exception e) { - result.status(400); - return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID " + e.getMessage()); - } - } - - if(!challengers.inMemory(xChallengerGuid)){ - result.status(404); - return ApiResponseAsJson.getErrorMessageJson("Unknown Challenger GUID - have you created or loaded the challenger state"); - } - - // thingifier allows loading database from json extract - try { - thingifier.ensureCreatedAndPopulatedInstanceDatabaseFromJson(xChallengerGuid.trim(), request.body()); - }catch (Exception e){ - result.status(400); - return ApiResponseAsJson.getErrorMessageJson(e.getMessage()); - } - - - if(challengers.getErModel().getDatabaseNames().contains(xChallengerGuid)){ - result.status(204); - result.header("content-type", "application/json"); - result.header("X-CHALLENGER", xChallengerGuid); - return ""; - }else{ - result.status(500); - return ApiResponseAsJson.getErrorMessageJson("Unknown error, database not found"); - } - }); + put( + "/challenger/database/:id", + (request, result) -> { + String xChallengerGuid = request.params("id"); + + // challenger uuid must exist and be valid + if (xChallengerGuid == null) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson("Invalid Challenger GUID"); + } + + if (!single_player_mode) { + try { + UUID.fromString(xChallengerGuid); + } catch (Exception e) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson( + "Invalid Challenger GUID " + e.getMessage()); + } + } + + if (!challengers.inMemory(xChallengerGuid)) { + result.status(404); + return ApiResponseAsJson.getErrorMessageJson( + "Unknown Challenger GUID - have you created or loaded the challenger state"); + } + + // thingifier allows loading database from json extract + try { + thingifier.ensureCreatedAndPopulatedInstanceDatabaseFromJson( + xChallengerGuid.trim(), request.body()); + } catch (Exception e) { + result.status(400); + return ApiResponseAsJson.getErrorMessageJson(e.getMessage()); + } + + if (challengers.getErModel().getDatabaseNames().contains(xChallengerGuid)) { + result.status(204); + result.header("content-type", "application/json"); + result.header("X-CHALLENGER", xChallengerGuid); + return ""; + } else { + result.status(500); + return ApiResponseAsJson.getErrorMessageJson( + "Unknown error, database not found"); + } + }); // Document the endpoint apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.PUT, - "/challenger/database/:guid", - RoutingStatus.returnedFromCall(), - null).addDocumentation("Restore a saved set of todos for a challenger matching the supplied X-CHALLENGER guid."). - addPossibleStatuses(204,400). - addRequestUrlParam(guidField) - ); - + RoutingVerb.PUT, + "/challenger/database/:guid", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation( + "Restore a saved set of todos for a challenger matching the supplied X-CHALLENGER guid.") + .addPossibleStatuses(204, 400) + .addRequestUrlParam(guidField)); // TODO: add a protected admin page with an environment variable protection as password } - - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengesRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengesRoutes.java index 45ecfec3..13cd606e 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengesRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/ChallengesRoutes.java @@ -1,85 +1,113 @@ package uk.co.compendiumdev.challenge.challengesrouting; +import static spark.Spark.*; + +import java.util.List; import uk.co.compendiumdev.challenge.ChallengerAuthData; import uk.co.compendiumdev.challenge.apimodel.ChallengeThingifier; import uk.co.compendiumdev.challenge.challengers.Challengers; import uk.co.compendiumdev.challenge.challenges.ChallengeDefinitions; -import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; -import uk.co.compendiumdev.thingifier.api.restapihandlers.RestApiGetHandler; import uk.co.compendiumdev.thingifier.api.docgen.RoutingDefinition; import uk.co.compendiumdev.thingifier.api.docgen.RoutingStatus; import uk.co.compendiumdev.thingifier.api.docgen.RoutingVerb; +import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; +import uk.co.compendiumdev.thingifier.api.restapihandlers.RestApiGetHandler; import uk.co.compendiumdev.thingifier.application.AdhocDocumentedSparkRouteConfigurer; import uk.co.compendiumdev.thingifier.application.routehandlers.SparkApiRequestResponseHandler; import uk.co.compendiumdev.thingifier.core.domain.definitions.EntityDefinition; import uk.co.compendiumdev.thingifier.core.query.FilterBy; import uk.co.compendiumdev.thingifier.core.query.QueryFilterParams; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; -import java.util.List; - -import static spark.Spark.*; public class ChallengesRoutes { - public void configure(final Challengers challengers, final boolean single_player_mode, - final ThingifierApiDocumentationDefn apiDefn, - final ChallengeDefinitions challengeDefinitions){ - - - get("/challenges", (request, result) -> { - - ChallengerAuthData challenger = challengers.getChallenger(request.headers("X-CHALLENGER")); - - if(!single_player_mode){ - if(challenger!=null){ - result.raw().setHeader("Location", "/gui/challenges/" + challenger.getXChallenger()); - } - }else{ - result.raw().setHeader("Location", "/gui/challenges"); - } - - // Todo: use the cloneThingifierWithNewData here and simplify the ChallengeThingifier - ChallengeThingifier challengeThingifier = new ChallengeThingifier(); - final EntityDefinition challengeDefn = challengeThingifier.challengeDefn; - challengeThingifier.populateThingifierFrom(challengeDefinitions); - - return new SparkApiRequestResponseHandler(request, result, challengeThingifier.challengeThingifier). - usingHandler((anHttpApiRequest)->{ - challengeThingifier.populateThingifierFromStatus(challenger); - QueryFilterParams queryParams = anHttpApiRequest.getFilterableQueryParams(); - //final Map queryParams = anHttpApiRequest.getQueryParams(); - if(!queryParams.hasSortBy()){ - queryParams.add(new FilterBy("sort_by","+ID")); + public void configure( + final Challengers challengers, + final boolean single_player_mode, + final ThingifierApiDocumentationDefn apiDefn, + final ChallengeDefinitions challengeDefinitions) { + + get( + "/challenges", + (request, result) -> { + ChallengerAuthData challenger = + challengers.getChallenger(request.headers("X-CHALLENGER")); + + if (!single_player_mode) { + if (challenger != null) { + result.raw() + .setHeader( + "Location", + "/gui/challenges/" + challenger.getXChallenger()); } -// if(!queryParams.containsKey("sortBy") && -// !queryParams.containsKey("sort_by")){ -// // force a sort -// queryParams.put("sort_by","+ID"); -// } - return new RestApiGetHandler(challengeThingifier.challengeThingifier) - .handle(challengeDefn.getPlural(), - queryParams, anHttpApiRequest.getHeaders()); - }).handle(); - - }); + } else { + result.raw().setHeader("Location", "/gui/challenges"); + } + + // Todo: use the cloneThingifierWithNewData here and simplify the + // ChallengeThingifier + ChallengeThingifier challengeThingifier = new ChallengeThingifier(); + final EntityDefinition challengeDefn = challengeThingifier.challengeDefn; + challengeThingifier.populateThingifierFrom(challengeDefinitions); + + return new SparkApiRequestResponseHandler( + request, result, challengeThingifier.challengeThingifier) + .usingHandler( + (anHttpApiRequest) -> { + challengeThingifier.populateThingifierFromStatus( + challenger); + QueryFilterParams queryParams = + anHttpApiRequest.getFilterableQueryParams(); + // final Map queryParams = + // anHttpApiRequest.getQueryParams(); + if (!queryParams.hasSortBy()) { + queryParams.add(new FilterBy("sort_by", "+ID")); + } + // + // if(!queryParams.containsKey("sortBy") && + // + // !queryParams.containsKey("sort_by")){ + // // force a sort + // + // queryParams.put("sort_by","+ID"); + // } + return new RestApiGetHandler( + challengeThingifier.challengeThingifier) + .handle( + challengeDefn.getPlural(), + queryParams, + anHttpApiRequest.getHeaders()); + }) + .handle(); + }); apiDefn.addRouteToDocumentation( new RoutingDefinition( - RoutingVerb.GET, + RoutingVerb.GET, + "/challenges", + RoutingStatus.returnedFromCall(), + null) + .addDocumentation("Get list of challenges and their completion status") + .addPossibleStatuses(200)); + + // TODO: because these hardcode contentType and ignore Accept there should be a light weight + // wrapper available + new AdhocDocumentedSparkRouteConfigurer(apiDefn) + .add( "/challenges", - RoutingStatus.returnedFromCall(), - null).addDocumentation("Get list of challenges and their completion status"). - addPossibleStatuses(200)); - - // TODO: because these hardcode contentType and ignore Accept there should be a light weight wrapper available - new AdhocDocumentedSparkRouteConfigurer(apiDefn). - add("/challenges", RoutingVerb.HEAD, 200, "Headers for list of challenges endpoint", - (request, result) ->{ + RoutingVerb.HEAD, + 200, + "Headers for list of challenges endpoint", + (request, result) -> { result.status(200); result.type("application/json"); return ""; - }). - add("/challenges", RoutingVerb.OPTIONS, 200,"Options for list of challenges endpoint", + }) + .add( + "/challenges", + RoutingVerb.OPTIONS, + 200, + "Options for list of challenges endpoint", ((request, result) -> { result.status(200); result.type("application/json"); @@ -87,8 +115,7 @@ public void configure(final Challengers challengers, final boolean single_player return ""; })); - SimpleSparkRouteCreator.routeStatusWhenNot(405, "/challenges", List.of("get", "head", "options")); - + SimpleSparkRouteCreator.routeStatusWhenNot( + 405, "/challenges", List.of("get", "head", "options")); } - } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java index f1c60b92..6b3eddb1 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/HeartBeatRoutes.java @@ -1,44 +1,39 @@ package uk.co.compendiumdev.challenge.challengesrouting; -import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; +import java.util.List; import uk.co.compendiumdev.thingifier.api.docgen.RoutingVerb; +import uk.co.compendiumdev.thingifier.api.docgen.ThingifierApiDocumentationDefn; import uk.co.compendiumdev.thingifier.application.AdhocDocumentedSparkRouteConfigurer; import uk.co.compendiumdev.thingifier.spark.SimpleSparkRouteCreator; -import java.util.List; - public class HeartBeatRoutes { public void configure(final ThingifierApiDocumentationDefn apiDefn) { - String endpoint ="/heartbeat"; + String endpoint = "/heartbeat"; final AdhocDocumentedSparkRouteConfigurer sparkRouteConfig = new AdhocDocumentedSparkRouteConfigurer(apiDefn); - sparkRouteConfig.add(endpoint, - RoutingVerb.GET, 204, - "Is the server running? YES 204"); + sparkRouteConfig.add(endpoint, RoutingVerb.GET, 204, "Is the server running? YES 204"); - sparkRouteConfig.add(endpoint, - RoutingVerb.HEAD, 204, - "Headers for heartbeat endpoint"); + sparkRouteConfig.add(endpoint, RoutingVerb.HEAD, 204, "Headers for heartbeat endpoint"); - sparkRouteConfig.add(endpoint, - RoutingVerb.OPTIONS, 204, + sparkRouteConfig.add( + endpoint, + RoutingVerb.OPTIONS, + 204, "Options for heartbeat endpoint", (request, result) -> { result.status(204); result.header("Allow", "GET, HEAD, OPTIONS"); return ""; - } - ); + }); // undocumented handlers SimpleSparkRouteCreator simpleSparkRouteCreator = new SimpleSparkRouteCreator(endpoint); simpleSparkRouteCreator.status(405, true, List.of("post", "delete", "put")); simpleSparkRouteCreator.status(500, true, List.of("patch")); simpleSparkRouteCreator.status(501, true, List.of("trace")); - } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/XChallengerHeader.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/XChallengerHeader.java index 84297d98..f80c9268 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/XChallengerHeader.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/challengesrouting/XChallengerHeader.java @@ -6,52 +6,53 @@ public class XChallengerHeader { - public static final String NOT_FOUND_ERROR_MESSAGE = "UNKNOWN CHALLENGER - Challenger not found"; + public static final String NOT_FOUND_ERROR_MESSAGE = + "UNKNOWN CHALLENGER - Challenger not found"; - public static void setResultHeaderBasedOnChallenger(final Response result, - final ChallengerAuthData challenger) { + public static void setResultHeaderBasedOnChallenger( + final Response result, final ChallengerAuthData challenger) { - if(result==null){ + if (result == null) { return; } - if(challenger==null){ + if (challenger == null) { result.raw().setHeader("X-CHALLENGER", NOT_FOUND_ERROR_MESSAGE); - //result.raw().setHeader("X-CHALLENGER", "Challenger not recognised"); - }else{ - result.raw().setHeader("X-CHALLENGER",challenger.getXChallenger()); + // result.raw().setHeader("X-CHALLENGER", "Challenger not recognised"); + } else { + result.raw().setHeader("X-CHALLENGER", challenger.getXChallenger()); } } - public static void setResultHeaderBasedOnChallenger(final Response result, - final String challengerGUID) { + public static void setResultHeaderBasedOnChallenger( + final Response result, final String challengerGUID) { - if(result==null){ + if (result == null) { return; } - if(challengerGUID==null || challengerGUID.trim().length()==0){ + if (challengerGUID == null || challengerGUID.trim().length() == 0) { result.raw().setHeader("X-CHALLENGER", NOT_FOUND_ERROR_MESSAGE); - //result.raw().setHeader("X-CHALLENGER", "Challenger not recognised"); - }else{ - result.raw().setHeader("X-CHALLENGER",challengerGUID); + // result.raw().setHeader("X-CHALLENGER", "Challenger not recognised"); + } else { + result.raw().setHeader("X-CHALLENGER", challengerGUID); } } - public static void setResultHeaderBasedOnChallenger(final InternalHttpResponse response, - final ChallengerAuthData challenger) { + public static void setResultHeaderBasedOnChallenger( + final InternalHttpResponse response, final ChallengerAuthData challenger) { - if(response==null){ + if (response == null) { return; } - if(response.getHeaders().headerExists("X-CHALLENGER")){ + if (response.getHeaders().headerExists("X-CHALLENGER")) { return; } - if(challenger==null){ + if (challenger == null) { response.setHeader("X-CHALLENGER", NOT_FOUND_ERROR_MESSAGE); - }else{ + } else { response.setHeader("X-CHALLENGER", challenger.getXChallenger()); } } diff --git a/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java b/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java index 46bd37ac..e9b8f0c3 100644 --- a/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java +++ b/challenger/src/main/java/uk/co/compendiumdev/challenge/gui/ChallengerWebGUI.java @@ -1,5 +1,9 @@ package uk.co.compendiumdev.challenge.gui; +import static spark.Spark.*; + +import java.time.LocalDate; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spark.Request; @@ -12,11 +16,6 @@ import uk.co.compendiumdev.thingifier.core.EntityRelModel; import uk.co.compendiumdev.thingifier.htmlgui.htmlgen.DefaultGUIHTML; -import java.time.LocalDate; -import java.util.*; - -import static spark.Spark.*; - public class ChallengerWebGUI { private static final LocalDate SEO_FIXED_LASTMOD = LocalDate.parse("2026-02-18"); @@ -31,16 +30,17 @@ public ChallengerWebGUI(final DefaultGUIHTML defaultGui, final boolean guiStayAl this.pageNotFoundHtmlResponse = new PageNotFoundResponse(guiManagement); } - String getChallengesPageHtmlHeader(){ + String getChallengesPageHtmlHeader() { return guiManagement.getPageStart( "API Challenges - Improve your API Skills", - "" + - "", + "" + + "", "/gui/challenges"); } - String getSponsorMessage(){ - String sponsorMessage = """ + String getSponsorMessage() { + String sponsorMessage = + """