diff --git a/README.md b/README.md index fd081d32..2a20a3e9 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,112 @@ xAPI Java helps you to create applications that send or receive xAPI [Statements](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#statements) or [Documents](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#10-documents). +There are two projects in this [Monorepo](https://en.wikipedia.org/wiki/Monorepo), xAPI Client and xAPI Model. + +Both the xAPI client and xAPI Model use a [fluent interface](https://en.wikipedia.org/wiki/Fluent_interface). Objects are [immutable](https://en.wikipedia.org/wiki/Immutable_object). + +## xAPI Java Client + +The xAPI Java Client can be used by learning record providers (LRP) to communicate with learning record stores (LRS) or a system which follows the LRS requirements of one or more of the xAPI resources. + +### Getting started + +To use the xAPI Client include the appropriate XML in the `dependencies` section of your `pom.xml`, as shown in the following example: + +```xml + + 4.0.0 + getting-started + + + + + dev.learning.xapi + xapi-client + 1.0.3 + + + +``` + +### State Resource + +The xAPI Client allows applications to store, change, fetch, or delete [state documents](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Communication.md#23-state-resource). + +#### Getting a state + +Example: + +```java +final var request = 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(); + +final String state = request.getBody(); +``` + +#### Posting a state + +Example: + +```java +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(); +``` + +#### Putting a state + +Example: + +```java +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(); +``` + +#### Deleting a state + +Example: + +```java +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(); +``` + + ## xAPI Java Model -The xAPI Model has a [fluent interface](https://en.wikipedia.org/wiki/Fluent_interface). Objects are [immutable](https://en.wikipedia.org/wiki/Immutable_object). +The xAPI model can be used by clients that send xAPI data or by servers that receive xAPI data. ### Getting started @@ -20,7 +123,7 @@ To use the xAPI Model include the appropriate XML in the `dependencies` section dev.learning.xapi xapi-model - 1.0.1 + 1.0.3 @@ -95,7 +198,7 @@ final String json = objectMapper.writeValueAsString(statement); ``` -### Creating a new statment using an existing statement as template +### Creating a new statement using an existing statement as template Example: diff --git a/pom.xml b/pom.xml index 58dfe495..70a79cde 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ xapi-model + xapi-client diff --git a/xapi-client/lombok.config b/xapi-client/lombok.config new file mode 100644 index 00000000..b2661bf9 --- /dev/null +++ b/xapi-client/lombok.config @@ -0,0 +1,3 @@ +# Suppress code coverage on Lombok annotations +lombok.addLombokGeneratedAnnotation = true +lombok.builder.className = Builder diff --git a/xapi-client/pom.xml b/xapi-client/pom.xml new file mode 100644 index 00000000..c551b3db --- /dev/null +++ b/xapi-client/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + dev.learning.xapi + xapi-build + 1.0.3-SNAPSHOT + + + xapi-client + + xAPI Client + learning.dev xAPI Client + + + + org.springframework.boot + spring-boot-starter-webflux + + + dev.learning.xapi + xapi-model + + + org.projectlombok + lombok + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + com.squareup.okhttp3 + mockwebserver + test + + + com.squareup.okhttp3 + okhttp + test + + + io.netty + netty-resolver-dns-native-macos + osx-aarch_64 + test + + + + + + + org.jacoco + jacoco-maven-plugin + + + + + diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/DeleteStateRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/DeleteStateRequest.java new file mode 100644 index 00000000..0f89fcf9 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/DeleteStateRequest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; + +/** + * Request for deleting a single State document. + * + * @see Single + * State Document DELETE + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +public class DeleteStateRequest extends StateRequest { + + @Override + protected HttpMethod getMethod() { + return HttpMethod.DELETE; + } + + /** + * Builder for DeleteStateRequest. + */ + public abstract static class Builder> + extends StateRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/DeleteStatesRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/DeleteStatesRequest.java new file mode 100644 index 00000000..34fbefed --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/DeleteStatesRequest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; + +/** + * Request for deleting multiple State documents. + * + * @see Multiple + * State Document DELETE + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +public class DeleteStatesRequest extends StatesRequest { + + @Override + protected HttpMethod getMethod() { + return HttpMethod.DELETE; + } + + /** + * Builder for DeleteStatesRequest. + */ + public abstract static class Builder> + extends StatesRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetStateRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetStateRequest.java new file mode 100644 index 00000000..097e31ad --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetStateRequest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; + +/** + * Request for getting a single State document. + * + * @see Single + * State Document GET + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +public class GetStateRequest extends StateRequest { + + @Override + protected HttpMethod getMethod() { + return HttpMethod.GET; + } + + /** + * Builder for GetStateRequest. + */ + public abstract static class Builder> + extends StateRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetStatesRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetStatesRequest.java new file mode 100644 index 00000000..a3163192 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetStatesRequest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import java.time.Instant; +import java.util.Map; +import java.util.Optional; +import lombok.Getter; +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Request for getting multiple State documents. + * + * @see Multiple + * State Document GET + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +@Getter +public class GetStatesRequest extends StatesRequest { + + /** + * Only ids of states stored since the specified instant (exclusive) are returned. + */ + private final Instant since; + + @Override + protected HttpMethod getMethod() { + return HttpMethod.GET; + } + + @Override + protected UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + return super.url(uriBuilder, queryParams).queryParamIfPresent("since", + Optional.ofNullable(since)); + + } + + /** + * Builder for DeleteStateRequest. + */ + public abstract static class Builder> + extends StatesRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/PostStateRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/PostStateRequest.java new file mode 100644 index 00000000..81f04638 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/PostStateRequest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import lombok.Builder.Default; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; + +/** + * Request for posting a single State document. + * + * @see Single + * State Document POST + * @see JSON + * Procedure with Requirements + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +@Getter +public class PostStateRequest extends StateRequest { + + @Default + private MediaType contentType = MediaType.APPLICATION_JSON; + + /** + * The state object to store. + */ + @NonNull + private final Object state; + + @Override + protected HttpMethod getMethod() { + return HttpMethod.POST; + } + + /** + * Builder for PostStateRequest. + */ + public abstract static class Builder> + extends StateRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/PutStateRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/PutStateRequest.java new file mode 100644 index 00000000..db8c08a7 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/PutStateRequest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import lombok.Builder.Default; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; + +/** + * Request for putting a single State document. + * + * @see Single + * State Document PUT + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +@Getter +public class PutStateRequest extends StateRequest { + + @Default + private MediaType contentType = MediaType.APPLICATION_JSON; + + /** + * The state object to store. + */ + @NonNull + private final Object state; + + @Override + protected HttpMethod getMethod() { + return HttpMethod.PUT; + } + + /** + * Builder for PutStateRequest. + */ + public abstract static class Builder> + extends StateRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/Request.java b/xapi-client/src/main/java/dev/learning/xapi/client/Request.java new file mode 100644 index 00000000..6e092dbf --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/Request.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import java.util.Map; +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Base class for xAPI request. + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +abstract class Request { + + protected abstract UriBuilder url(UriBuilder uriBuilder, Map queryParams); + + /** + * The request method. + * + * @return the request method as a {@link HttpMethod} object. + */ + protected abstract HttpMethod getMethod(); + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/StateRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/StateRequest.java new file mode 100644 index 00000000..f08df53d --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/StateRequest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import java.util.Map; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import org.springframework.web.util.UriBuilder; + +/** + * Abstract superclass for state requests manipulating a single state document. + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +@Getter +abstract class StateRequest extends StatesRequest { + + /** + * The stateId query parameter. + */ + @NonNull + private final String stateId; + + @Override + protected UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + queryParams.put("stateId", stateId); + + return super.url(uriBuilder, queryParams).queryParam("stateId", "{stateId}"); + + } + + /** + * Builder for StateRequest. + */ + public abstract static class Builder> + extends StatesRequest.Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/StatesRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/StatesRequest.java new file mode 100644 index 00000000..e7bff8b7 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/StatesRequest.java @@ -0,0 +1,188 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import dev.learning.xapi.model.Agent; +import java.net.URI; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import org.springframework.web.util.UriBuilder; + +/** + * Abstract superclass of xAPI state resource request. + * + * @see State + * Resource + * + * @author István Rátkai (Selindek) + */ +@SuperBuilder +@Getter +abstract class StatesRequest extends Request { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * The activityId query parameter. + */ + @NonNull + private final URI activityId; + + /** + * The agent query parameter. + */ + @NonNull + private final Agent agent; + + /** + * The optional registration query parameter. + */ + private final UUID registration; + + @Override + protected UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + queryParams.put("activityId", activityId); + queryParams.put("agent", agentToJsonString()); + + return uriBuilder.path("activities/state") + + .queryParam("activityId", "{activityId}") + + .queryParam("agent", "{agent}") + + .queryParamIfPresent("registration", Optional.ofNullable(registration)); + } + + + private String agentToJsonString() { + + try { + return objectMapper.writeValueAsString(agent); + } catch (JsonProcessingException e) { + // Should not happen + return null; + } + + } + + public abstract static class Builder> + extends Request.Builder { + + /** + * Consumer Builder for agent. + * + * @param agent The Consumer Builder for agent. + * + * @return This builder + * + * @see StatesRequest#agent + */ + public B agent(Consumer> agent) { + + final Agent.Builder builder = Agent.builder(); + + agent.accept(builder); + + return agent(builder.build()); + + } + + /** + * Sets the agent. + * + * @param agent The Agent of the StatesRequest. + * + * @return This builder + * + * @see StatesRequest#agent + */ + public B agent(Agent agent) { + + this.agent = agent; + + return self(); + + } + + /** + * Sets the activityId. + * + * @param activityId The activityId of the StatesRequest. + * + * @return This builder + * + * @see StatesRequest#activityId + */ + public B activityId(String activityId) { + + this.activityId = URI.create(activityId); + + return self(); + + } + + /** + * Sets the activityId. + * + * @param activityId The activityId of the StatesRequest. + * + * @return This builder + * + * @see StatesRequest#activityId + */ + public B activityId(URI activityId) { + + this.activityId = activityId; + + return self(); + + } + + /** + * Sets the registration. + * + * @param registration The registration of the StatesRequest. + * + * @return This builder + * + * @see StatesRequest#registration + */ + public B registration(String registration) { + + this.registration = UUID.fromString(registration); + + return self(); + + } + + /** + * Sets the registration. + * + * @param registration The registration of the StatesRequest. + * + * @return This builder + * + * @see StatesRequest#registration + */ + public B registration(UUID registration) { + + this.registration = registration; + + return self(); + + } + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java b/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java new file mode 100644 index 00000000..1ca66587 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java @@ -0,0 +1,349 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +/** + * Client for communicating with LRS or service which implements some of the xAPI communication + * resources. + * + * @author Thomas Turrell-Croft + * @author István Rátkai (Selindek) + * + * @see xAPI + * communication resources + */ +public class XapiClient { + + private final WebClient webClient; + + /** + * Default constructor for XapiClient. + * + * @param builder a {@link WebClient.Builder} object. The caller must set the baseUrl and the + * authorization header. + */ + public XapiClient(WebClient.Builder builder) { + this.webClient = builder + + .defaultHeader("X-Experience-API-Version", "1.0.3") + + .build(); + } + + /** + * 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 parameters of the get state request + * + * @return the ResponseEntity + */ + public Mono> getState(GetStateRequest request, Class bodyType) { + + Map 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" + } + ] +}