queryParams = new HashMap<>();
+
+ return this.webClient
+
+ .method(request.getMethod())
+
+ .uri(u -> request.url(u, queryParams).build(queryParams))
+
+ .retrieve()
+
+ .toEntity(bodyType);
+
+ }
+
+ /**
+ * Gets a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers and body.
+ *
+ *
+ * @param request The Consumer Builder for the get state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> getState(Consumer> request,
+ Class bodyType) {
+
+ final GetStateRequest.Builder, ?> builder = GetStateRequest.builder();
+
+ request.accept(builder);
+
+ return getState(builder.build(), bodyType);
+
+ }
+
+ /**
+ * Posts a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers and body.
+ *
+ *
+ * @param request The parameters of the post state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> postState(PostStateRequest request) {
+
+ Map queryParams = new HashMap<>();
+
+ return this.webClient
+
+ .method(request.getMethod())
+
+ .uri(u -> request.url(u, queryParams).build(queryParams))
+
+ .contentType(request.getContentType())
+
+ .bodyValue(request.getState())
+
+ .retrieve()
+
+ .toBodilessEntity();
+
+ }
+
+ /**
+ * Posts a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers and body.
+ *
+ *
+ * @param request The Consumer Builder for the post state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> postState(Consumer> request) {
+
+ final PostStateRequest.Builder, ?> builder = PostStateRequest.builder();
+
+ request.accept(builder);
+
+ return postState(builder.build());
+
+ }
+
+ /**
+ * Puts a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers and body.
+ *
+ *
+ * @param request The parameters of the put state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> putState(PutStateRequest request) {
+
+ Map queryParams = new HashMap<>();
+
+ return this.webClient
+
+ .method(request.getMethod())
+
+ .uri(u -> request.url(u, queryParams).build(queryParams))
+
+ .contentType(request.getContentType())
+
+ .bodyValue(request.getState())
+
+ .retrieve()
+
+ .toBodilessEntity();
+
+ }
+
+ /**
+ * Puts a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers and body.
+ *
+ *
+ * @param request The Consumer Builder for the put state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> putState(Consumer> request) {
+
+ final PutStateRequest.Builder, ?> builder = PutStateRequest.builder();
+
+ request.accept(builder);
+
+ return putState(builder.build());
+
+ }
+
+ /**
+ * Deletes a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers.
+ *
+ *
+ * @param request The parameters of the delete state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> deleteState(DeleteStateRequest request) {
+
+ Map queryParams = new HashMap<>();
+
+ return this.webClient
+
+ .method(request.getMethod())
+
+ .uri(u -> request.url(u, queryParams).build(queryParams))
+
+ .retrieve()
+
+ .toBodilessEntity();
+
+ }
+
+ /**
+ * Deletes a single document specified by the given stateId activity, agent, and optional
+ * registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers.
+ *
+ *
+ * @param request The Consumer Builder for the delete state request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> deleteState(
+ Consumer> request) {
+
+ final DeleteStateRequest.Builder, ?> builder = DeleteStateRequest.builder();
+
+ request.accept(builder);
+
+ return deleteState(builder.build());
+
+ }
+
+ /**
+ * Gets all stateId's specified by the given activityId, agent and optional registration and since
+ * parameters.
+ *
+ * @param request The parameters of the get states request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> getStates(GetStatesRequest request) {
+
+ Map queryParams = new HashMap<>();
+
+ return this.webClient
+
+ .method(request.getMethod())
+
+ .uri(u -> request.url(u, queryParams).build(queryParams))
+
+ .retrieve()
+
+ .toEntity(String[].class);
+
+ }
+
+ /**
+ * Gets all stateId's specified by the given activityId, agent and optional registration and since
+ * parameters.
+ *
+ *
+ * The returned ResponseEntity contains the response headers.
+ *
+ *
+ * @param request The Consumer Builder for the get states request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> getStates(
+ Consumer> request) {
+
+ final GetStatesRequest.Builder, ?> builder = GetStatesRequest.builder();
+
+ request.accept(builder);
+
+ return getStates(builder.build());
+
+ }
+
+ /**
+ * Deletes all documents specified by the given activityId, agent and optional registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers.
+ *
+ *
+ * @param request The parameters of the delete states request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> deleteStates(DeleteStatesRequest request) {
+
+ Map queryParams = new HashMap<>();
+
+ return this.webClient
+
+ .method(request.getMethod())
+
+ .uri(u -> request.url(u, queryParams).build(queryParams))
+
+ .retrieve()
+
+ .toBodilessEntity();
+
+ }
+
+ /**
+ * Deletes all documents specified by the given activityId, agent and optional registration.
+ *
+ *
+ * The returned ResponseEntity contains the response headers.
+ *
+ *
+ * @param request The Consumer Builder for the delete states request
+ *
+ * @return the ResponseEntity
+ */
+ public Mono> deleteStates(
+ Consumer> request) {
+
+ final DeleteStatesRequest.Builder, ?> builder = DeleteStatesRequest.builder();
+
+ request.accept(builder);
+
+ return deleteStates(builder.build());
+
+ }
+
+}
diff --git a/xapi-client/src/main/resources/application.properties b/xapi-client/src/main/resources/application.properties
new file mode 100644
index 00000000..e69de29b
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/DeleteStateRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/DeleteStateRequestTests.java
new file mode 100644
index 00000000..229e3b35
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/DeleteStateRequestTests.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import dev.learning.xapi.client.DeleteStateRequest.Builder;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * DeleteStateRequest Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("DeleteStateRequest Tests")
+class DeleteStateRequestTests {
+
+ @Test
+ void whenBuildingDeleteStateRequestWithAllParametersThenNoExceptionIsThrown() {
+
+ // When Building DeleteStateRequest With All Parameters
+ Builder, ?> builder = DeleteStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStateRequestWithoutRegistrationThenNoExceptionIsThrown() {
+
+ // When Building DeleteStateRequest Without Registration
+ Builder, ?> builder = DeleteStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStateRequestWithoutActivityIdThenExceptionIsThrown() {
+
+ // When Building DeleteStateRequest Without ActivityId
+ Builder, ?> builder = DeleteStateRequest.builder()
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark");
+
+ // Then NullPointerException Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStateRequestWithoutAgentThenExceptionIsThrown() {
+
+ // When Building DeleteStateRequest Without Agent
+ Builder, ?> builder = DeleteStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark");
+
+ // Then NullPointerException Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStateRequestWithoutStateIdThenExceptionIsThrown() {
+
+ // When Building DeleteStateRequest Without StateId
+ Builder, ?> builder = DeleteStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6");
+
+ // Then NullPointerException Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void givenDeleteStateRequestWithAllParametersWhenGettingURLThenResultIsExpected() {
+
+ // Given DeleteStateRequest With All Parameters
+ DeleteStateRequest request = DeleteStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark")));
+
+ }
+
+ @Test
+ void givenDeleteStateRequestWithoutRegistrationWhenGettingURLThenResultIsExpected() {
+
+ // Given DeleteStateRequest Without Registration
+ DeleteStateRequest request = DeleteStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark")));
+
+ }
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/DeleteStatesRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/DeleteStatesRequestTests.java
new file mode 100644
index 00000000..43843060
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/DeleteStatesRequestTests.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import dev.learning.xapi.client.DeleteStatesRequest.Builder;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * DeleteStatesRequest Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("DeleteStatesRequest Tests")
+class DeleteStatesRequestTests {
+
+ @Test
+ void whenBuildingDeleteStatesRequestWithAllParametersThenNoExceptionIsThrown() {
+
+ // When Building DeleteStatesRequest With All Parameters
+ Builder, ?> builder = DeleteStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStatesRequestWithRegistrationAsUUIDTypeThenNoExceptionIsThrown() {
+
+ // When Building Delete States Request With Registration As UUID Type
+ Builder, ?> builder = DeleteStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration(UUID.fromString("67828e3a-d116-4e18-8af3-2d2c59e27be6"));
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStatesRequestWithActivityIdAsURITypeThenNoExceptionIsThrown() {
+
+ // When Building Delete States Request With ActivityId As URI Type
+ Builder, ?> builder = DeleteStatesRequest.builder()
+
+ .activityId(URI.create("https://example.com/activity/1"))
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration(("67828e3a-d116-4e18-8af3-2d2c59e27be6"));
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStatesRequestWithoutRegistrationThenNoExceptionIsThrown() {
+
+ // When Building DeleteStatesRequest Without Registration
+ Builder, ?> builder = DeleteStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"));
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStatesRequestWithoutActivityIdThenExceptionIsThrown() {
+
+ // When Building DeleteStatesRequest Without ActivityId
+ Builder, ?> builder = DeleteStatesRequest.builder()
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6");
+
+ // Then NullPointerException Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingDeleteStatesRequestWithoutAgentThenExceptionIsThrown() {
+
+ // When Building DeleteStatesRequest Without Agent
+ Builder, ?> builder = DeleteStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6");
+
+ // Then NullPointerException Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void givenDeleteStatesRequestWithAllParametersWhenGettingURLThenResultIsExpected() {
+
+ // Given DeleteStatesRequest With All Parameters
+ DeleteStatesRequest request = DeleteStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI url =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(url, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6")));
+
+ }
+
+
+ @Test
+ void givenDeleteStatesRequestWithoutRegistrationWhenGettingURLThenResultIsExpected() {
+
+ // Given DeleteStatesRequest Without Registration
+ DeleteStatesRequest request = DeleteStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI url =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(url, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D")));
+
+ }
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/GetStateRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/GetStateRequestTests.java
new file mode 100644
index 00000000..6b8801a6
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/GetStateRequestTests.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import dev.learning.xapi.client.GetStateRequest.Builder;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * GetStateRequest Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("GetStateRequest Tests")
+class GetStateRequestTests {
+
+ @Test
+ void whenBuildingGetStateRequestWithAllParametersThenNoExceptionIsThrown() {
+
+ // When Building GetStateRequest With All Parameters
+ Builder, ?> builder = GetStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStateRequestWithoutRegistrationThenNoExceptionIsThrown() {
+
+ // When Building GetStateRequest Without Registration
+ Builder, ?> builder = GetStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStateRequestWithoutActivityIdThenNullPointerExceptionIsThrown() {
+
+ // When Building GetStateRequest Without activityId
+ Builder, ?> builder = GetStateRequest.builder()
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark");
+
+ // Then Null Pointer Exception Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStateRequestWithoutAgentThenNullPointerExceptionIsThrown() {
+
+ // When Building GetStateRequest Without Agent
+ Builder, ?> builder = GetStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .stateId("bookmark");
+
+ // Then Null Pointer Exception Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStateRequestWithoutStateIdThenNullPointerExceptionIsThrown() {
+
+ // When Building GetStateRequest Without StateId
+ Builder, ?> builder = GetStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"));
+
+ // Then Null Pointer Exception Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void givenGetStateRequestWithAllParametersWhenGettingURLThenResultIsExpected() {
+
+ // Given GetStateRequest With All Parameters
+ GetStateRequest request = GetStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark")));
+
+ }
+
+ @Test
+ void givenGetStateRequestWithoutRegistrationWhenGettingURLThenResultIsExpected() {
+
+ // Given GetStateRequest Without Registration
+ GetStateRequest request = GetStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark")));
+
+ }
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/GetStatesRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/GetStatesRequestTests.java
new file mode 100644
index 00000000..8328918a
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/GetStatesRequestTests.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import dev.learning.xapi.client.GetStatesRequest.Builder;
+import java.net.URI;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * GetStatesRequest Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("GetStatesRequest Tests")
+class GetStatesRequestTests {
+
+ @Test
+ void whenBuildingGetStatesRequestWithAllParametersThenNoExceptionIsThrown() {
+
+ // When Building GetStatesRequest With All Parameters
+ Builder, ?> builder = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .since(Instant.now());
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStatesRequestWithoutSinceParameterThenNoExceptionIsThrown() {
+
+ // When Building GetStatesRequest Without Since Parameter
+ Builder, ?> builder = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStatesRequestWithoutRegistrationParameterThenNoExceptionIsThrown() {
+
+ // When Building GetStatesRequest Without Registration Parameter
+ Builder, ?> builder = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"));
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStatesRequestWithRequiredParametersThenNoExceptionIsThrown() {
+
+ // When Building GetStatesRequest With Required Parameters
+ Builder, ?> builder = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"));
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStatesRequestWithoutActivityIdThenNullPointerExceptionIsThrown() {
+
+ // When Building GetStatesRequest Without ActivityId
+ Builder, ?> builder = GetStatesRequest.builder()
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"));
+
+ // Then Null Pointer Exception Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void whenBuildingGetStatesRequestWithoutAgentThenNullPointerExceptionIsThrown() {
+
+ // When Building GetStatesRequest Without Agent
+ Builder, ?> builder = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1");
+
+ // Then Null Pointer Exception Is Thrown
+ assertThrows(NullPointerException.class, () -> builder.build());
+
+ }
+
+ @Test
+ void givenGetStatesRequestWithAllParametersWhenGettingURLThenResultIsExpected() {
+
+ // Given GetStatesRequest With All Parameters
+ GetStatesRequest request = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .since(Instant.parse("2016-01-01T00:00:00Z"))
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&since=2016-01-01T00:00:00Z")));
+
+ }
+
+ @Test
+ void givenGetStatesRequestWithoutRegistrationParameterWhenGettingURLThenResultIsExpected() {
+
+ // Given GetStatesRequest Without Registration Parameter
+ GetStatesRequest request = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D")));
+
+ }
+
+ @Test
+ void givenGetStatesRequestWithoutSinceParameterWhenGettingURLThenResultIsExpected() {
+
+ // Given GetStatesRequest Without Since Parameter
+ GetStatesRequest request = GetStatesRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6")));
+
+ }
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/PostStateRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/PostStateRequestTests.java
new file mode 100644
index 00000000..2f92d584
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/PostStateRequestTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import dev.learning.xapi.client.PostStateRequest.Builder;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * PostStateRequest Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("PostStateRequest Tests")
+class PostStateRequestTests {
+
+ @Test
+ void whenBuildingPostStateRequestWithAllParametersThenNoExceptionIsThrown() {
+
+ // When Building PostStateRequest With All Parameters
+ Builder, ?> builder = PostStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void givenPostStateRequestWithAllParametersWhenGettingURLThenResultIsExpected() {
+
+ // Given PostStateRequest With All Parameters
+ PostStateRequest request = PostStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark")));
+
+ }
+
+ @Test
+ void givenPostStateRequestWithoutRegistrationWhenGettingURLThenResultIsExpected() {
+
+ // Given PostStateRequest Without Registration
+ PostStateRequest request = PostStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .state("Hello World!")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark")));
+
+ }
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/PutStateRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/PutStateRequestTests.java
new file mode 100644
index 00000000..fe9bd0ff
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/PutStateRequestTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import dev.learning.xapi.client.PutStateRequest.Builder;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.util.UriComponentsBuilder;
+
+/**
+ * PutStateRequest Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("PutStateRequest Tests")
+class PutStateRequestTests {
+
+ @Test
+ void whenBuildingPutStateRequestWithAllParametersThenNoExceptionIsThrown() {
+
+ // When Building PutStateRequest With All Parameters
+ Builder, ?> builder = PutStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!");
+
+ // Then No Exception Is Thrown
+ assertDoesNotThrow(() -> builder.build());
+
+ }
+
+ @Test
+ void givenPutStateRequestWithAllParametersWhenGettingURLThenResultIsExpected() {
+
+ // Given PutStateRequest With All Parameters
+ PutStateRequest request = PutStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark")));
+
+ }
+
+ @Test
+ void givenPutStateRequestWithoutRegistrationWhenGettingURLThenResultIsExpected() {
+
+ // Given PutStateRequest Without Registration
+ PutStateRequest request = PutStateRequest.builder()
+
+ .activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .state("Hello World!")
+
+ .build();
+
+ Map queryParams = new HashMap<>();
+
+ // When Getting URL
+ URI result =
+ request.url(UriComponentsBuilder.fromUriString("https://example.com/xapi/"), queryParams)
+ .build(queryParams);
+
+ // Then Result Is Expected
+ assertThat(result, is(URI.create(
+ "https://example.com/xapi/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark")));
+
+ }
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/TestApp.java b/xapi-client/src/test/java/dev/learning/xapi/client/TestApp.java
new file mode 100644
index 00000000..e03afd41
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/TestApp.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TestApp {
+
+}
diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java
new file mode 100644
index 00000000..1debc04d
--- /dev/null
+++ b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java
@@ -0,0 +1,783 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+package dev.learning.xapi.client;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.reactive.function.client.WebClient;
+
+/**
+ * XapiClient Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("XapiClient Tests")
+@SpringBootTest
+class XapiClientTests {
+
+ @Autowired
+ private WebClient.Builder webClientBuilder;
+
+ private MockWebServer mockWebServer;
+ private XapiClient client;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ mockWebServer = new MockWebServer();
+ mockWebServer.start();
+
+ String baseUrl = String.format("http://localhost:%s", mockWebServer.getPort());
+
+ webClientBuilder.baseUrl(baseUrl);
+ client = new XapiClient(webClientBuilder);
+
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ mockWebServer.shutdown();
+ }
+
+ // Get Single State
+
+ @Test
+ void whenGettingASingleStateThenMethodIsGet() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content"));
+
+ // When Getting A Single State
+ client.getState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark"), String.class).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Get
+ assertThat(recordedRequest.getMethod(), is("GET"));
+ }
+
+
+
+ @Test
+ void whenGettingASingleStateThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content"));
+
+ // When Getting A Single State
+ client.getState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark"), String.class).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark"));
+ }
+
+ @Test
+ void whenGettingASingleStateWithoutRegistrationThenMethodIsGet() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Getting A Single State Without Registration
+ client.getState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark"), String.class)
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Get
+ assertThat(recordedRequest.getMethod(), is("GET"));
+ }
+
+ @Test
+ void whenGettingASingleStateWithoutRegistrationThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Getting A Single State Without Registration
+ client.getState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark"), String.class)
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark"));
+ }
+
+ @Test
+ void givenStateContentTypeIsTextPlainWhenGettingStateThenBodyIsInstanceOfString()
+ throws InterruptedException {
+
+ // Given State Content Type Is Text Plain
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("Hello World!").addHeader("Content-Type", "text/plain; charset=utf-8"));
+
+ // When Getting State
+ ResponseEntity response = client
+ .getState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark"), String.class)
+
+ .block();
+
+ // Then Body Is Instance Of String
+ assertThat(response.getBody(), instanceOf(String.class));
+ }
+
+ @Test
+ void givenStateContentTypeIsTextPlainWhenGettingStateThenBodyIsExpected()
+ throws InterruptedException {
+
+ // Given State Content Type Is Text Plain
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("Hello World!").addHeader("Content-Type", "text/plain; charset=utf-8"));
+
+ // When Getting State
+ ResponseEntity response = client
+ .getState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark"), String.class)
+
+ .block();
+
+ // Then Body Is Expected
+ assertThat(response.getBody(), is("Hello World!"));
+ }
+
+ // Post Single State
+
+ @Test
+ void whenPostingASingleStateThenMethodIsPost() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Posting A Single State
+ client.postState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Post
+ assertThat(recordedRequest.getMethod(), is("POST"));
+ }
+
+ @Test
+ void whenPostingASingleStateThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Posting A Single State
+ client.postState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark"));
+ }
+
+ @Test
+ void whenPostingASingleStateWithContentTypeTextPlainThenContentTypeHeaderIsTextPlain()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Posting A Single State With Content Type Text Plain
+ client.postState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!")
+
+ .contentType(MediaType.TEXT_PLAIN))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Content Type Header Is Text Plain
+ assertThat(recordedRequest.getHeader("content-type"), is("text/plain"));
+ }
+
+ @Test
+ void whenPostingASingleStateWithoutContentTypeThenContentTypeHeaderIsApplicationJson()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Posting A Single State Without Content Type
+ client.postState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Content Type Header Is Application Json
+ assertThat(recordedRequest.getHeader("content-type"), is("application/json"));
+ }
+
+ @Test
+ void whenPostingASingleStateWithoutRegistrationThenMethodIsPost() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Posting A Single State Without Registration
+ client.postState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Post
+ assertThat(recordedRequest.getMethod(), is("POST"));
+ }
+
+ @Test
+ void whenPostingASingleStateWithoutRegistrationThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Posting A Single State Without Registration
+ client.postState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark"));
+ }
+
+ // Put Single State
+
+ @Test
+ void whenPuttingASingleStateThenMethodIsPut() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Putting A Single State
+ client.putState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Post
+ assertThat(recordedRequest.getMethod(), is("PUT"));
+ }
+
+ @Test
+ void whenPuttingASingleStateThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Putting A Single State
+ client.putState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark"));
+ }
+
+ @Test
+ void whenPuttingASingleStateWithContentTypeTextPlainThenContentTypeHeaderIsTextPlain()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Putting A Single State With Content Type Text Plain
+ client.putState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!")
+
+ .contentType(MediaType.TEXT_PLAIN))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Content Type Header Is Text Plain
+ assertThat(recordedRequest.getHeader("content-type"), is("text/plain"));
+ }
+
+ @Test
+ void whenPuttingASingleStateWithoutContentTypeThenContentTypeHeaderIsApplicationJson()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Putting A Single State Without Content Type
+ client.putState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Content Type Header Is Application Json
+ assertThat(recordedRequest.getHeader("content-type"), is("application/json"));
+ }
+
+ @Test
+ void whenPuttingASingleStateWithoutRegistrationThenMethodIsPut() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Putting A Single State Without Registration
+ client.putState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Post
+ assertThat(recordedRequest.getMethod(), is("PUT"));
+ }
+
+ @Test
+ void whenPuttingASingleStateWithoutRegistrationThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Putting A Single State Without Registration
+ client.putState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")
+
+ .state("Hello World!"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D&stateId=bookmark"));
+ }
+
+ // Deleting Single State
+
+ @Test
+ void whenDeletingASingleStateThenMethodIsDelete() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting A Single State
+ client.deleteState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Delete
+ assertThat(recordedRequest.getMethod(), is("DELETE"));
+ }
+
+ @Test
+ void whenDeletingASingleStateThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting A Single State
+ client.deleteState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark"));
+ }
+
+ @Test
+ void whenDeletingASingleStateWithoutRegistrationThenMethodIsDelete() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting A Single State Without Registration
+ client.deleteState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .stateId("bookmark")).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Delete
+ assertThat(recordedRequest.getMethod(), is("DELETE"));
+ }
+
+ @Test
+ void whenDeletingASingleStateWithoutRegistrationThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting A Single State Without Registration
+ client.deleteState(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ .stateId("bookmark")).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6&stateId=bookmark"));
+ }
+
+ // Getting Multiple States
+
+ @Test
+ void whenGettingMultipleStatesThenMethodIsGet() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("[\"State1\", \"State2\", \"State3\"]")
+ .addHeader("Content-Type", "application/json; charset=utf-8"));
+
+ // When Getting Multiple States
+ client.getStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Get
+ assertThat(recordedRequest.getMethod(), is("GET"));
+ }
+
+ @Test
+ void whenGettingMultipleStatesThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("[\"State1\", \"State2\", \"State3\"]")
+ .addHeader("Content-Type", "application/json; charset=utf-8"));
+
+ // When Getting Multiple States
+ client.getStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6"))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6"));
+ }
+
+ @Test
+ void whenGettingMultipleStatesWithoutRegistrationThenMethodIsGet() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("[\"State1\", \"State2\", \"State3\"]")
+ .addHeader("Content-Type", "application/json; charset=utf-8"));
+
+ // When Getting Multiple States Without Registration
+ client.getStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com")))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Get
+ assertThat(recordedRequest.getMethod(), is("GET"));
+ }
+
+ @Test
+ void whenGettingMultipleStatesWithoutRegistrationThenPathIsExpected()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("[\"State1\", \"State2\", \"State3\"]")
+ .addHeader("Content-Type", "application/json; charset=utf-8"));
+
+ // When Getting Multiple States Without Registration
+ client.getStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com")))
+
+ .block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D"));
+ }
+
+ @Test
+ void givenMultipleStatesExistWhenGettingMultipleStatesThenBodyIsInstanceOfStringArray()
+ throws InterruptedException {
+
+ // Given Multiple States Exist
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("[\"State1\", \"State2\", \"State3\"]")
+ .addHeader("Content-Type", "application/json; charset=utf-8"));
+
+ // When Getting Multiple States
+ ResponseEntity response = client
+ .getStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6"))
+
+ .block();
+
+ // Then Body Is Instance Of String Array
+ assertThat(response.getBody(), instanceOf(String[].class));
+ }
+
+ @Test
+ void givenMultipleStatesExistWhenGettingMultipleStatesThenBodyIsExpected()
+ throws InterruptedException {
+
+ // Given Multiple States Exist
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")
+ .setBody("[\"State1\", \"State2\", \"State3\"]")
+ .addHeader("Content-Type", "application/json; charset=utf-8"));
+
+ // When Getting Multiple States
+ ResponseEntity response = client
+ .getStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6"))
+
+ .block();
+
+ // Then Body Is Expected
+ assertThat(response.getBody(), is(new String[] {"State1", "State2", "State3"}));
+ }
+
+ // Deleting Multiple States
+
+ @Test
+ void whenDeletingMultipleStatesThenMethodIsDelete() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting Multiple States
+ client.deleteStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ ).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Delete
+ assertThat(recordedRequest.getMethod(), is("DELETE"));
+ }
+
+ @Test
+ void whenDeletingMultipleStatesThenPathIsExpected() throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting Multiple States
+ client.deleteStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")
+
+ ).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D®istration=67828e3a-d116-4e18-8af3-2d2c59e27be6"));
+ }
+
+ @Test
+ void whenDeletingMultipleStatesWithoutRegistrationThenMethodIsDelete()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting Multiple States Without Registration
+ client.deleteStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ ).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Method Is Delete
+ assertThat(recordedRequest.getMethod(), is("DELETE"));
+ }
+
+ @Test
+ void whenDeletingMultipleStatesWithoutRegistrationThenPathIsExpected()
+ throws InterruptedException {
+
+ mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 204 No Content"));
+
+ // When Deleting Multiple States Without Registration
+ client.deleteStates(r -> r.activityId("https://example.com/activity/1")
+
+ .agent(a -> a.name("A N Other").mbox("another@example.com"))
+
+ ).block();
+
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ // Then Path Is Expected
+ assertThat(recordedRequest.getPath(), is(
+ "/activities/state?activityId=https%3A%2F%2Fexample.com%2Factivity%2F1&agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22another%40example.com%22%7D"));
+ }
+
+}
diff --git a/xapi-client/src/test/resources/application.properties b/xapi-client/src/test/resources/application.properties
new file mode 100644
index 00000000..ae4d6668
--- /dev/null
+++ b/xapi-client/src/test/resources/application.properties
@@ -0,0 +1 @@
+logging.level.dev.learning.xapi = DEBUG
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java b/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java
index 3860246d..a6c86dbd 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java
@@ -32,7 +32,7 @@
@ToString
@NoArgsConstructor
@EqualsAndHashCode(exclude = "name")
-@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "objectType", visible = true,
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "objectType", visible = false,
defaultImpl = Agent.class, include = As.EXISTING_PROPERTY)
@JsonSubTypes({@JsonSubTypes.Type(value = Agent.class, name = "Agent"),
@JsonSubTypes.Type(value = Agent.class, name = "Person"),
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Agent.java b/xapi-model/src/main/java/dev/learning/xapi/model/Agent.java
index dcaf5145..17558360 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Agent.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Agent.java
@@ -7,7 +7,6 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import lombok.AllArgsConstructor;
-import lombok.Builder.Default;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@@ -30,12 +29,10 @@
@JsonSubTypes.Type(value = Agent.class, name = "Person")})
public class Agent extends Actor {
- @Default
- private final ObjectType objectType = ObjectType.AGENT;
+ private ObjectType objectType;
// **Warning** do not add fields that are not required by the xAPI specification.
-
/**
* Builder for Agent.
*/
@@ -46,5 +43,4 @@ public abstract static class Builder>
}
-
}
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java
index adb98115..6e9f1d25 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Group.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Group.java
@@ -6,6 +6,8 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import jakarta.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
@@ -22,7 +24,7 @@
* This class represents the xAPI Group object.
*
* @author Thomas Turrell-Croft
- *
+ *
* @see xAPI Group
*/
@Getter
@@ -30,6 +32,8 @@
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "objectType", visible = true,
+ include = As.EXISTING_PROPERTY)
@JsonSubTypes({@JsonSubTypes.Type(value = Group.class, name = "Group")})
public class Group extends Actor {
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java
index 3355ee65..43ebfd86 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java
@@ -54,6 +54,19 @@ void whenDeserializingAgentWithoutObjectTypeThenResultIsInstanceOfAgent() throws
}
+ @Test
+ void whenDeserializingAgentWithObjectTypeThenResultIsInstanceOfAgent() throws IOException {
+
+ final File file = ResourceUtils.getFile("classpath:agent/agent_with_object_type.json");
+
+ // When Deserializing Agent Without Object Type
+ final Agent result = objectMapper.readValue(file, Agent.class);
+
+ // Then Result Is Instance Of Agent
+ assertThat(result, instanceOf(Agent.class));
+
+ }
+
@Test
void whenDeserializingAgentWithNameThenNameIsExpected() throws IOException {
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java
index 4493d2bb..e54ff72f 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/ContextTests.java
@@ -268,7 +268,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException {
// Then Result Is Expected
assertThat(result, is(
- "Context(registration=1d527164-ed0d-4b1d-9f9b-39aab0e4a089, instructor=Agent(super=Actor(name=Andrew Downes, mbox=mailto:andrew@example.co.uk, mboxSha1sum=null, openid=null, account=null), objectType=AGENT), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), objectType=GROUP, member=null), contextActivities=ContextActivities(parent=[Activity(objectType=ACTIVITY, id=https://example.com/activity/1, definition=null)], grouping=[Activity(objectType=ACTIVITY, id=https://example.com/activity/2, definition=null)], category=[Activity(objectType=ACTIVITY, id=https://example.com/activity/3, definition=null)], other=[Activity(objectType=ACTIVITY, id=https://example.com/activity/4, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(objectType=null, id=e9b6b9ed-ef48-4986-9b86-2ef697578bf7), extensions={http://url=www.example.com})"));
+ "Context(registration=1d527164-ed0d-4b1d-9f9b-39aab0e4a089, instructor=Agent(super=Actor(name=Andrew Downes, mbox=mailto:andrew@example.co.uk, mboxSha1sum=null, openid=null, account=null), objectType=null), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), objectType=GROUP, member=null), contextActivities=ContextActivities(parent=[Activity(objectType=ACTIVITY, id=https://example.com/activity/1, definition=null)], grouping=[Activity(objectType=ACTIVITY, id=https://example.com/activity/2, definition=null)], category=[Activity(objectType=ACTIVITY, id=https://example.com/activity/3, definition=null)], other=[Activity(objectType=ACTIVITY, id=https://example.com/activity/4, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(objectType=null, id=e9b6b9ed-ef48-4986-9b86-2ef697578bf7), extensions={http://url=www.example.com})"));
}
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java
index f87c2a39..cd40484e 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/GroupTests.java
@@ -40,6 +40,21 @@ void whenDeserializingGroupThenResultIsInstanceOfGroup() throws IOException {
}
+ @Test
+ void whenDeserializingGroupWithMembersWithObjectTypeThenResultIsInstanceOfGroup()
+ throws IOException {
+
+ final File file =
+ ResourceUtils.getFile("classpath:group/group_with_members_with_object_type.json");
+
+ // When Deserializing Group With Members With Object Type
+ final Group result = objectMapper.readValue(file, Group.class);
+
+ // Then Result Is Instance Of Group
+ assertThat(result, instanceOf(Group.class));
+
+ }
+
@Test
void whenDeserializingGroupThenMemberIsInstanceOfAgent() throws IOException {
@@ -87,7 +102,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException {
// Then Result Is Expected
assertThat(result, is(
- "Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=Account(homePage=http://example.com/homePage, name=GroupAccount)), objectType=GROUP, member=[Agent(super=Actor(name=Member 1, mbox=mailto:member1@example.com, mboxSha1sum=null, openid=null, account=null), objectType=AGENT), Agent(super=Actor(name=Member 2, mbox=null, mboxSha1sum=null, openid=https://example.com/openId, account=null), objectType=AGENT)])"));
+ "Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=Account(homePage=http://example.com/homePage, name=GroupAccount)), objectType=GROUP, member=[Agent(super=Actor(name=Member 1, mbox=mailto:member1@example.com, mboxSha1sum=null, openid=null, account=null), objectType=null), Agent(super=Actor(name=Member 2, mbox=null, mboxSha1sum=null, openid=https://example.com/openId, account=null), objectType=null)])"));
}
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java
index d601f1e3..1fb74bcc 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/SubStatementTests.java
@@ -350,7 +350,7 @@ void whenCallingToStringThenResultIsExpected() throws IOException {
// Then Result Is Expected
assertThat(result, is(
- "SubStatement(objectType=null, actor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null), objectType=AGENT), verb=Verb(id=http://example.com/confirmed, display={en=confirmed}), object=StatementReference(objectType=STATEMENTREF, id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), result=Result(score=Score(scaled=1.0, raw=1.0, min=0.0, max=5.0), success=true, completion=true, response=test, duration=P1D, extensions=null), context=Context(registration=6d969975-8d7e-4506-ac19-877c57f2921a, instructor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null), objectType=AGENT), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), objectType=GROUP, member=null), contextActivities=ContextActivities(parent=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)], grouping=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)], category=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)], other=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(objectType=null, id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), extensions={http://url=www.example.com}), timestamp=2015-11-18T11:17:00Z, attachments=[Attachment(usageType=http://example.com, display={en=value}, description={en=value}, contentType=file, length=123, sha2=123, fileUrl=http://example.com)])"));
+ "SubStatement(objectType=null, actor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null), objectType=null), verb=Verb(id=http://example.com/confirmed, display={en=confirmed}), object=StatementReference(objectType=STATEMENTREF, id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), result=Result(score=Score(scaled=1.0, raw=1.0, min=0.0, max=5.0), success=true, completion=true, response=test, duration=P1D, extensions=null), context=Context(registration=6d969975-8d7e-4506-ac19-877c57f2921a, instructor=Agent(super=Actor(name=null, mbox=mailto:agent@example.com, mboxSha1sum=null, openid=null, account=null), objectType=null), team=Group(super=Actor(name=Example Group, mbox=null, mboxSha1sum=null, openid=null, account=null), objectType=GROUP, member=null), contextActivities=ContextActivities(parent=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)], grouping=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)], category=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)], other=[Activity(objectType=ACTIVITY, id=http://www.example.co.uk/exampleactivity, definition=null)]), revision=revision, platform=platform, language=en, statement=StatementReference(objectType=null, id=9e13cefd-53d3-4eac-b5ed-2cf6693903bb), extensions={http://url=www.example.com}), timestamp=2015-11-18T11:17:00Z, attachments=[Attachment(usageType=http://example.com, display={en=value}, description={en=value}, contentType=file, length=123, sha2=123, fileUrl=http://example.com)])"));
}
diff --git a/xapi-model/src/test/resources/agent/agent_with_account.json b/xapi-model/src/test/resources/agent/agent_with_account.json
index 2023404c..9213e34e 100644
--- a/xapi-model/src/test/resources/agent/agent_with_account.json
+++ b/xapi-model/src/test/resources/agent/agent_with_account.json
@@ -3,6 +3,5 @@
"account": {
"name": "another",
"homePage": "https://www.example.com"
- },
- "objectType": "Agent"
+ }
}
diff --git a/xapi-model/src/test/resources/agent/agent_with_mbox.json b/xapi-model/src/test/resources/agent/agent_with_mbox.json
index d9122c52..bd7805f0 100644
--- a/xapi-model/src/test/resources/agent/agent_with_mbox.json
+++ b/xapi-model/src/test/resources/agent/agent_with_mbox.json
@@ -1,5 +1,4 @@
{
"name": "A N Other",
- "mbox": "mailto:other@example.com",
- "objectType": "Agent"
+ "mbox": "mailto:other@example.com"
}
diff --git a/xapi-model/src/test/resources/agent/agent_with_mbox_sha1sum.json b/xapi-model/src/test/resources/agent/agent_with_mbox_sha1sum.json
index 71a3422b..2b4f2421 100644
--- a/xapi-model/src/test/resources/agent/agent_with_mbox_sha1sum.json
+++ b/xapi-model/src/test/resources/agent/agent_with_mbox_sha1sum.json
@@ -1,5 +1,4 @@
{
"name": "A N Other",
- "mbox_sha1sum": "1234",
- "objectType": "Agent"
+ "mbox_sha1sum": "1234"
}
diff --git a/xapi-model/src/test/resources/agent/agent_with_name.json b/xapi-model/src/test/resources/agent/agent_with_name.json
index d9122c52..bd7805f0 100644
--- a/xapi-model/src/test/resources/agent/agent_with_name.json
+++ b/xapi-model/src/test/resources/agent/agent_with_name.json
@@ -1,5 +1,4 @@
{
"name": "A N Other",
- "mbox": "mailto:other@example.com",
- "objectType": "Agent"
+ "mbox": "mailto:other@example.com"
}
diff --git a/xapi-model/src/test/resources/agent/agent_with_object_type.json b/xapi-model/src/test/resources/agent/agent_with_object_type.json
new file mode 100644
index 00000000..d9122c52
--- /dev/null
+++ b/xapi-model/src/test/resources/agent/agent_with_object_type.json
@@ -0,0 +1,5 @@
+{
+ "name": "A N Other",
+ "mbox": "mailto:other@example.com",
+ "objectType": "Agent"
+}
diff --git a/xapi-model/src/test/resources/agent/agent_with_openid.json b/xapi-model/src/test/resources/agent/agent_with_openid.json
index 68ac4f29..cc967d90 100644
--- a/xapi-model/src/test/resources/agent/agent_with_openid.json
+++ b/xapi-model/src/test/resources/agent/agent_with_openid.json
@@ -1,5 +1,4 @@
{
"name": "A N Other",
- "openid": "1234",
- "objectType": "Agent"
+ "openid": "1234"
}
diff --git a/xapi-model/src/test/resources/context/context.json b/xapi-model/src/test/resources/context/context.json
index 9146036b..7e646aff 100644
--- a/xapi-model/src/test/resources/context/context.json
+++ b/xapi-model/src/test/resources/context/context.json
@@ -5,8 +5,7 @@
"language": "en",
"instructor": {
"name": "Andrew Downes",
- "mbox": "mailto:andrew@example.co.uk",
- "objectType": "Agent"
+ "mbox": "mailto:andrew@example.co.uk"
},
"team": {
"name": "Example Group",
diff --git a/xapi-model/src/test/resources/context/context_with_empty_registration.json b/xapi-model/src/test/resources/context/context_with_empty_registration.json
index e72ab073..990ffb74 100644
--- a/xapi-model/src/test/resources/context/context_with_empty_registration.json
+++ b/xapi-model/src/test/resources/context/context_with_empty_registration.json
@@ -5,8 +5,7 @@
"language": "en",
"instructor": {
"name": "Andrew Downes",
- "mbox": "mailto:andrew@example.co.uk",
- "objectType": "Agent"
+ "mbox": "mailto:andrew@example.co.uk"
},
"team": {
"name": "Example Group",
diff --git a/xapi-model/src/test/resources/context/context_with_group_instructor.json b/xapi-model/src/test/resources/context/context_with_group_instructor.json
index 788d38e4..1e69c06e 100644
--- a/xapi-model/src/test/resources/context/context_with_group_instructor.json
+++ b/xapi-model/src/test/resources/context/context_with_group_instructor.json
@@ -3,12 +3,10 @@
"objectType":"Group",
"member":[
{
- "objectType":"Agent",
"name":"Leader 1",
"mbox":"mailto:leader1@example.com"
},
{
- "objectType":"Agent",
"name":"Leader 2",
"mbox":"mailto:leader2@example.com"
}
diff --git a/xapi-model/src/test/resources/context/context_without_registration.json b/xapi-model/src/test/resources/context/context_without_registration.json
index 68e1ea8b..cd324ee9 100644
--- a/xapi-model/src/test/resources/context/context_without_registration.json
+++ b/xapi-model/src/test/resources/context/context_without_registration.json
@@ -4,8 +4,7 @@
"language": "en",
"instructor": {
"name": "Andrew Downes",
- "mbox": "mailto:andrew@example.co.uk",
- "objectType": "Agent"
+ "mbox": "mailto:andrew@example.co.uk"
},
"team": {
"name": "Example Group",
diff --git a/xapi-model/src/test/resources/group/group.json b/xapi-model/src/test/resources/group/group.json
index bdd83d56..3d7463ab 100644
--- a/xapi-model/src/test/resources/group/group.json
+++ b/xapi-model/src/test/resources/group/group.json
@@ -8,13 +8,11 @@
"member": [
{
"name": "Member 1",
- "mbox": "mailto:member1@example.com",
- "objectType": "Agent"
+ "mbox": "mailto:member1@example.com"
},
{
"name": "Member 2",
- "openid": "https://example.com/openId",
- "objectType": "Agent"
+ "openid": "https://example.com/openId"
}
]
}
diff --git a/xapi-model/src/test/resources/group/group_with_members_with_object_type.json b/xapi-model/src/test/resources/group/group_with_members_with_object_type.json
new file mode 100644
index 00000000..3d7463ab
--- /dev/null
+++ b/xapi-model/src/test/resources/group/group_with_members_with_object_type.json
@@ -0,0 +1,18 @@
+{
+ "name": "Example Group",
+ "account": {
+ "homePage": "http://example.com/homePage",
+ "name": "GroupAccount"
+ },
+ "objectType": "Group",
+ "member": [
+ {
+ "name": "Member 1",
+ "mbox": "mailto:member1@example.com"
+ },
+ {
+ "name": "Member 2",
+ "openid": "https://example.com/openId"
+ }
+ ]
+}