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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package uk.co.compendiumdev.thingifier.application.examples;

import static uk.co.compendiumdev.thingifier.apiconfig.EntityPatchUpdateStyle.JSON_MERGE_PATCH_RFC7396;
import static uk.co.compendiumdev.thingifier.apiconfig.EntityPatchUpdateStyle.JSON_PATCH_RFC6902;
import static uk.co.compendiumdev.thingifier.apiconfig.EntityPatchUpdateStyle.PARTIAL_JSON_UPDATE;
import static uk.co.compendiumdev.thingifier.apiconfig.EntityWriteOperation.CREATE;
import static uk.co.compendiumdev.thingifier.apiconfig.EntityWriteOperation.UPDATE;
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.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.validation.VRule;

public class ConfiguredWriteMethodsThingifier {

public Thingifier get() {
Thingifier notes = new Thingifier();
notes.setDocumentation(
"Configured Write Methods",
"A sample API configured so POST creates, PATCH updates, and PUT updates.");

EntityDefinition note = notes.defineThing("note", "notes");
note.addAsPrimaryKeyField(Field.is("id", FieldType.AUTO_INCREMENT));
note.addFields(
Field.is("title", STRING).makeMandatory().withValidation(VRule.notEmpty()),
Field.is("description", STRING));

notes.apiDefaults().writeMethods().entities().postCan(CREATE);
notes.apiDefaults()
.writeMethods()
.entities()
.patchCan(PARTIAL_JSON_UPDATE, JSON_MERGE_PATCH_RFC7396, JSON_PATCH_RFC6902);
notes.apiDefaults().writeMethods().entities().putCan(UPDATE);

return notes;
}
}
3 changes: 3 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
<thingifier.version>1.5.6-SNAPSHOT</thingifier.version>
<swagger-models-version>2.2.52</swagger-models-version>
<gson-version>2.8.6</gson-version>
<jackson-version>2.21.1</jackson-version>
<zjsonpatch-version>0.6.3</zjsonpatch-version>
<sqlite-jdbc-version>3.53.2.0</sqlite-jdbc-version>
<snakeyaml-version>2.6</snakeyaml-version>
<junit.jupiter.version>5.6.2</junit.jupiter.version>
Expand Down Expand Up @@ -140,6 +142,7 @@
<module>thingifier-crud-ui</module>
<module>todoManagerRestAuto</module>
<module>standAloneTodoListRestApi</module>
<module>standAloneConfiguredWriteMethodsRestApi</module>
<module>standAloneTodoListManagerRestApi</module>
<module>standAloneSqlLiteTodoListManagerRestApi</module>
<module>standAloneTodoListManagerRestApiAuto</module>
Expand Down
81 changes: 81 additions & 0 deletions standAloneConfiguredWriteMethodsRestApi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>uk.co.compendiumdev.thingifier</groupId>
<artifactId>thingifier-root</artifactId>
<version>1.5.6-SNAPSHOT</version>
</parent>

<artifactId>standAloneConfiguredWriteMethodsRestApi</artifactId>

<dependencies>
<dependency>
<groupId>uk.co.compendiumdev</groupId>
<artifactId>thingifier</artifactId>
<version>${thingifier.version}</version>
</dependency>
<dependency>
<groupId>uk.co.compendiumdev.thingifier</groupId>
<artifactId>examplemodels</artifactId>
<version>${thingifier.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>runConfiguredWriteMethodsRestAPI-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<archive>
<manifest>
<mainClass>uk.co.compendiumdev.configuredwritemethods.application.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package uk.co.compendiumdev.configuredwritemethods.application;

import static uk.co.compendiumdev.thingifier.adapter.httpserver.ServerRoutes.get;

import uk.co.compendiumdev.thingifier.adapter.httpserver.MainImplementation;
import uk.co.compendiumdev.thingifier.application.examples.ConfiguredWriteMethodsThingifier;

public class Main {

public static void main(String[] args) {
MainImplementation app = new MainImplementation();
app.registerModel("configuredWriteMethods", new ConfiguredWriteMethodsThingifier().get());

app.setDefaultsFromArgs(args);

app.configurePortAndDefaultRoutes();
app.setupBuiltInConfigurableRoutes();

app.chooseThingifier();
app.configureThingifierWithProfile();

app.setupDefaultGui();

get(
"/",
(request, response) -> {
response.redirect("/gui/entities");
return "";
});

app.startRestServer();
app.addBuiltInArgConfiguredHooks();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package uk.co.compendiumdev.configuredwritemethods.application;

import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.createStoredNote;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.currentNote;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.noteJson;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.patch;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.post;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.put;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.sample;
import static uk.co.compendiumdev.thingifier.apiconfig.EntityPatchUpdateStyle.JSON_MERGE_PATCH_RFC7396;
import static uk.co.compendiumdev.thingifier.apiconfig.EntityPatchUpdateStyle.JSON_PATCH_RFC6902;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import uk.co.compendiumdev.thingifier.Thingifier;
import uk.co.compendiumdev.thingifier.api.response.ApiResponse;

class ConfiguredWriteMethodsDirectApiTest {

@Test
void postNotesCreatesANote() {
final Thingifier thingifier = sample();

final ApiResponse response = post(thingifier, "notes", noteJson("One"));

Assertions.assertEquals(201, response.getStatusCode());
Assertions.assertEquals(
"One", response.getReturnedInstance().getFieldValue("title").asString());
Assertions.assertEquals(
"sample", response.getReturnedInstance().getFieldValue("description").asString());
}

@Test
void postExistingNoteIsBlocked() {
final Thingifier thingifier = sample();
final String id = createStoredNote(thingifier).getPrimaryKeyValue();

final ApiResponse response = post(thingifier, "notes/" + id, "{\"title\":\"Blocked\"}");

Assertions.assertEquals(405, response.getStatusCode());
Assertions.assertEquals(
"Existing", currentNote(thingifier, id).getFieldValue("title").asString());
}

@Test
void partialJsonPatchUpdatesTheExistingNote() {
final Thingifier thingifier = sample();
final String id = createStoredNote(thingifier).getPrimaryKeyValue();

final ApiResponse response =
patch(thingifier, "notes/" + id, "{\"title\":\"Patched Direct\"}");

Assertions.assertEquals(200, response.getStatusCode());
Assertions.assertEquals(
"Patched Direct", response.getReturnedInstance().getFieldValue("title").asString());
Assertions.assertEquals(
"sample", response.getReturnedInstance().getFieldValue("description").asString());
}

@Test
void mergePatchUpdatesTheExistingNote() {
final Thingifier thingifier = sample();
final String id = createStoredNote(thingifier).getPrimaryKeyValue();

final ApiResponse response =
patch(
thingifier,
"notes/" + id,
"{\"description\":\"Merged Direct\"}",
JSON_MERGE_PATCH_RFC7396.mediaType());

Assertions.assertEquals(200, response.getStatusCode());
Assertions.assertEquals(
"Existing", response.getReturnedInstance().getFieldValue("title").asString());
Assertions.assertEquals(
"Merged Direct",
response.getReturnedInstance().getFieldValue("description").asString());
}

@Test
void jsonPatchUpdatesTheExistingNote() {
final Thingifier thingifier = sample();
final String id = createStoredNote(thingifier).getPrimaryKeyValue();

final ApiResponse response =
patch(
thingifier,
"notes/" + id,
"[{\"op\":\"replace\",\"path\":\"/title\",\"value\":\"Json Patch Direct\"}]",
JSON_PATCH_RFC6902.mediaType());

Assertions.assertEquals(200, response.getStatusCode());
Assertions.assertEquals(
"Json Patch Direct",
response.getReturnedInstance().getFieldValue("title").asString());
Assertions.assertEquals(
"sample", response.getReturnedInstance().getFieldValue("description").asString());
}

@Test
void putExistingNoteUpdatesTheNote() {
final Thingifier thingifier = sample();
final String id = createStoredNote(thingifier).getPrimaryKeyValue();

final ApiResponse response = put(thingifier, "notes/" + id, "{\"title\":\"Put Direct\"}");

Assertions.assertEquals(200, response.getStatusCode());
Assertions.assertEquals(
"Put Direct", response.getReturnedInstance().getFieldValue("title").asString());
}

@Test
void putMissingNoteIsBlockedBecausePutCannotCreate() {
final ApiResponse response = put(sample(), "notes/999", "{\"title\":\"Missing\"}");

Assertions.assertEquals(405, response.getStatusCode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package uk.co.compendiumdev.configuredwritemethods.application;

import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.documentation;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.patchFormatsHeader;
import static uk.co.compendiumdev.configuredwritemethods.application.ConfiguredWriteMethodsSampleApiTestSupport.route;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import uk.co.compendiumdev.thingifier.api.docgen.ApiRoutingDefinition;
import uk.co.compendiumdev.thingifier.api.docgen.RoutingVerb;

class ConfiguredWriteMethodsDocumentationTest {

@Test
void exposesPostCreateOnTheCollectionRoute() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertTrue(
route(definition, RoutingVerb.POST, "notes").status().isReturnedFromCall());
}

@Test
void blocksPostUpdateOnTheInstanceRoute() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertEquals(
405, route(definition, RoutingVerb.POST, "notes/:id").status().value());
}

@Test
void blocksPutAndPatchOnTheCollectionRoute() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertEquals(405, route(definition, RoutingVerb.PUT, "notes").status().value());
Assertions.assertEquals(
405, route(definition, RoutingVerb.PATCH, "notes").status().value());
}

@Test
void exposesPatchAndPutUpdateOnTheInstanceRoute() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertTrue(
route(definition, RoutingVerb.PATCH, "notes/:id").status().isReturnedFromCall());
Assertions.assertTrue(
route(definition, RoutingVerb.PUT, "notes/:id").status().isReturnedFromCall());
}

@Test
void collectionOptionsAllowOnlyTheCollectionMethods() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertEquals(
"OPTIONS, GET, HEAD, POST, QUERY",
route(definition, RoutingVerb.OPTIONS, "notes").headerValue());
}

@Test
void instanceOptionsAllowOnlyTheInstanceMethods() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertEquals(
"OPTIONS, GET, HEAD, PUT, PATCH, DELETE",
route(definition, RoutingVerb.OPTIONS, "notes/:id").headerValue());
}

@Test
void instanceOptionsExposeConfiguredPatchMediaTypes() {
final ApiRoutingDefinition definition = documentation();

Assertions.assertEquals(
patchFormatsHeader(),
route(definition, RoutingVerb.OPTIONS, "notes/:id")
.getResponseHeaderValue("Accept-Patch"));
}
}
Loading
Loading