diff --git a/README.md b/README.md index 2a20a3e9..e3bf4d4d 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,91 @@ To use the xAPI Client include the appropriate XML in the `dependencies` section dev.learning.xapi xapi-client - 1.0.3 + 1.0.4 ``` +### Statement Resource + +The xAPI Client allows applications to store and fetch xAPI [Statements](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#statements). + +### Getting a Statement + +```java +var response = client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block(); + +Statement statement = response.getBody(); +``` + +### Getting Statements + +Example: + +```java +var response = client.getStatements().block(); + +StatementResult statementResult = response.getBody(); + +Statement[] statements = statementResult.getStatements(); +``` + +### Getting the next page of Statements + + +Example: + +```java +var response = client.getStatements().block(); + +var moreResponse = client.getMoreStatements(r -> r.more(response.getBody().getMore())).block(); + +StatementResult moreStatementResult = moreResponse.getBody(); + +Statement[] statements = moreStatementResult.getStatements(); +``` + + +### Posting a Statement + +Example: + +```java +client.postStatement( + r -> r.statement(s -> s.actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block(); +``` + +### Posting Statements + +Example: + +```java +Statement attemptedStatement = Statement.builder() + .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")).verb(Verb.ATTEMPTED) + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))) + .build(); + +Statement passedStatement = attemptedStatement.toBuilder().verb(Verb.PASSED).build(); + +client.postStatements(r -> r.statements(attemptedStatement, passedStatement)).block(); +``` + +### Getting voided a Statement + +```java +var response = client.getVoidedStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block(); + +Statement voidedStatement = response.getBody(); +``` + ### 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). @@ -39,7 +118,7 @@ The xAPI Client allows applications to store, change, fetch, or delete [state do Example: ```java -final var request = client.getState(r -> r.activityId("https://example.com/activity/1") +var request = client.getState(r -> r.activityId("https://example.com/activity/1") .agent(a -> a.name("A N Other").mbox("another@example.com")) @@ -48,8 +127,8 @@ final var request = client.getState(r -> r.activityId("https://example.com/activ .stateId("bookmark"), String.class) .block(); - -final String state = request.getBody(); + +String state = request.getBody(); ``` #### Posting a state @@ -123,7 +202,7 @@ To use the xAPI Model include the appropriate XML in the `dependencies` section dev.learning.xapi xapi-model - 1.0.3 + 1.0.4 @@ -134,7 +213,7 @@ To use the xAPI Model include the appropriate XML in the `dependencies` section Example: ```java -final Statement statement = Statement.builder() +Statement statement = Statement.builder() .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) @@ -153,7 +232,7 @@ The Jackson ObjectMapper can be used to deserialize statements into Java objects Example: ```java -final String json = """ +String json = """ { "actor":{ "objectType":"Agent", @@ -177,7 +256,7 @@ final String json = """ } }"""; -final Statement statement = objectMapper.readValue(json, Statement.class); +Statement statement = objectMapper.readValue(json, Statement.class); ``` ### Serializing Statements @@ -188,13 +267,13 @@ Example: ```java -final Statement statement = Statement.builder() +Statement statement = Statement.builder() .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")).verb(Verb.ATTEMPTED) .activityObject(o -> o.id("https://example.com/activity/simplestatement") .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))) .build(); -final String json = objectMapper.writeValueAsString(statement); +String json = objectMapper.writeValueAsString(statement); ``` @@ -204,13 +283,13 @@ Example: ```java -final Statement passed = Statement.builder() +Statement passed = Statement.builder() .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")).verb(Verb.PASSED) .activityObject(o -> o.id("https://example.com/activity/simplestatement") .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))) .build(); -final Statement completed = passed.toBuilder().verb(Verb.COMPLETED).build(); +Statement completed = passed.toBuilder().verb(Verb.COMPLETED).build(); ``` diff --git a/pom.xml b/pom.xml index 5cbd14cb..95c78302 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,6 @@ xAPI Build learning.dev xAPI Build https://github.com/BerryCloud/xapi-java - 17 0.8.8 @@ -22,19 +21,16 @@ 10.6.0 1.0.0 - Berry Cloud Ltd https://berrycloud.co.uk - Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0 - thomas_turrell @@ -66,29 +62,24 @@ - https://github.com/BerryCloud/xapi-java scm:git:https://github.com/BerryCloud/xapi-java.git scm:git:https://github.com/BerryCloud/xapi-java.git HEAD - GitHub Issues https://github.com/BerryCloud/xapi-java/issues - GitHub Actions https://github.com/BerryCloud/xapi-java/actions - xapi-model xapi-client - @@ -97,7 +88,6 @@ maven-checkstyle-plugin ${maven-checkstyle-plugin.version} - org.apache.maven.plugins maven-failsafe-plugin @@ -110,7 +100,6 @@ - org.apache.maven.plugins maven-source-plugin @@ -123,7 +112,6 @@ - org.apache.maven.plugins maven-javadoc-plugin @@ -136,7 +124,6 @@ - org.apache.maven.plugins maven-gpg-plugin @@ -158,7 +145,6 @@ - org.jacoco jacoco-maven-plugin @@ -190,7 +176,6 @@ - org.sonatype.plugins nexus-staging-maven-plugin @@ -202,8 +187,6 @@ true - - org.eclipse.m2e lifecycle-mapping @@ -238,7 +221,6 @@ - org.apache.maven.plugins @@ -259,7 +241,6 @@ - org.apache.maven.plugins maven-release-plugin @@ -273,7 +254,6 @@ - @@ -283,7 +263,6 @@ - ossrh @@ -294,7 +273,6 @@ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - release @@ -304,17 +282,14 @@ org.apache.maven.plugins maven-source-plugin - org.apache.maven.plugins maven-javadoc-plugin - org.apache.maven.plugins maven-gpg-plugin - org.sonatype.plugins nexus-staging-maven-plugin @@ -323,5 +298,4 @@ - diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetMoreStatementsRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetMoreStatementsRequest.java new file mode 100644 index 00000000..5e205605 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetMoreStatementsRequest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import java.net.URI; +import java.util.Map; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Request for getting multiple Statements. + * + * @see GET + * Statements + * + * @author Thomas Turrell-Croft + */ +@Builder +@Getter +public class GetMoreStatementsRequest implements Request { + + @NonNull + private final URI more; + + @Override + public HttpMethod getMethod() { + return HttpMethod.GET; + } + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + return UriComponentsBuilder.fromUri(more); + + } + + /** + * Builder for GetMoreStatementsRequest. + */ + public static class Builder { + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetStatementRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetStatementRequest.java new file mode 100644 index 00000000..0a284073 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetStatementRequest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import dev.learning.xapi.model.StatementFormat; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import lombok.Getter; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Request for getting a Statement. + * + * @see GET + * Statements + * + * @author Thomas Turrell-Croft + */ +@SuperBuilder +@Getter +public class GetStatementRequest implements Request { + + @NonNull + protected final UUID id; + + protected final StatementFormat format; + + protected final Boolean attachments; + + @Override + public HttpMethod getMethod() { + return HttpMethod.GET; + } + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + return uriBuilder.path("statements") + + .queryParam("statementId", id) + + .queryParamIfPresent("format", Optional.ofNullable(format)) + + .queryParamIfPresent("attachments", Optional.ofNullable(attachments)); + + } + + /** + * Builder for GetStatementRequest. + */ + public abstract static class Builder> { + + /** + * Sets the id. + * + * @param id The id of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementRequest#id + */ + public Builder id(UUID id) { + this.id = id; + return self(); + } + + /** + * Sets the id. + * + * @param id The id of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementRequest#id + */ + public Builder id(String id) { + this.id = UUID.fromString(id); + return self(); + } + + // This static class extends the lombok builder. + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetStatementsRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetStatementsRequest.java new file mode 100644 index 00000000..cf6005f4 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetStatementsRequest.java @@ -0,0 +1,255 @@ +/* + * 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 dev.learning.xapi.model.StatementFormat; +import java.net.URI; +import java.time.Instant; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Request for getting multiple Statements. + * + * @see GET + * Statements + * + * @author Thomas Turrell-Croft + */ +@Builder +@Getter +public class GetStatementsRequest implements Request { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private final Agent agent; + + private final URI verb; + + private final URI activity; + + private final UUID registration; + + private final Boolean relatedActivities; + + private final Boolean relatedAgents; + + private final Instant since; + + private final Instant until; + + private final Integer limit; + + private final StatementFormat format; + + private final Boolean attachments; + + private final Boolean ascending; + + @Override + public HttpMethod getMethod() { + return HttpMethod.GET; + } + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + // All queryParams are optional + + uriBuilder.path("statements"); + + if (agent != null) { + queryParams.put("agent", agentToJsonString()); + uriBuilder.queryParam("agent", "{agent}"); + } + + if (verb != null) { + queryParams.put("verb", verb); + uriBuilder.queryParam("verb", "{verb}"); + } + + if (activity != null) { + queryParams.put("activity", activity); + uriBuilder.queryParam("activity", "{activity}"); + } + + if (since != null) { + queryParams.put("since", since); + uriBuilder.queryParam("since", "{since}"); + } + + if (until != null) { + queryParams.put("until", until); + uriBuilder.queryParam("until", "{until}"); + } + + return uriBuilder + + .queryParamIfPresent("registration", Optional.ofNullable(registration)) + + .queryParamIfPresent("related_activities", Optional.ofNullable(relatedActivities)) + + .queryParamIfPresent("related_agents", Optional.ofNullable(relatedAgents)) + + .queryParamIfPresent("limit", Optional.ofNullable(limit)) + + .queryParamIfPresent("format", Optional.ofNullable(format)) + + .queryParamIfPresent("attachments", Optional.ofNullable(attachments)) + + .queryParamIfPresent("ascending", Optional.ofNullable(ascending)); + + } + + /** + * Builder for GetStatementsRequest. + */ + public static class Builder { + + + /** + * Sets the agent. + * + * @param agent The agent of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#agent + */ + public Builder agent(Agent agent) { + this.agent = agent; + return this; + } + + /** + * Sets the agent. + * + * @param agent The agent of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#agent + */ + public Builder agent(Consumer> agent) { + + final dev.learning.xapi.model.Agent.Builder builder = Agent.builder(); + + agent.accept(builder); + + return agent(builder.build()); + } + + /** + * Sets the verb. + * + * @param verb The verb of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#verb + */ + public Builder verb(URI verb) { + this.verb = verb; + return this; + } + + /** + * Sets the verb. + * + * @param verb The verb of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#verb + */ + public Builder verb(String verb) { + this.verb = URI.create(verb); + return this; + } + + /** + * Sets the activity. + * + * @param activity The activity of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#activity + */ + public Builder activity(URI activity) { + this.activity = activity; + return this; + } + + /** + * Sets the activity. + * + * @param activity The activity of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#activity + */ + public Builder activity(String activity) { + this.activity = URI.create(activity); + return this; + } + + + + /** + * Sets the registration. + * + * @param registration The registration of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#registration + */ + public Builder registration(UUID registration) { + this.registration = registration; + return this; + } + + /** + * Sets the registration. + * + * @param registration The registration of the GetStatementRequest. + * + * @return This builder + * + * @see GetStatementsRequest#registration + */ + public Builder registration(String registration) { + this.registration = UUID.fromString(registration); + return this; + } + + // This static class extends the lombok builder. + + } + + private String agentToJsonString() { + + try { + return objectMapper.writeValueAsString(agent); + } catch (JsonProcessingException e) { + // Should not happen + return null; + } + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetVoidedStatementRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetVoidedStatementRequest.java new file mode 100644 index 00000000..13bbd9aa --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetVoidedStatementRequest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import java.util.Map; +import java.util.Optional; +import lombok.Getter; +import lombok.experimental.SuperBuilder; +import org.springframework.web.util.UriBuilder; + +/** + * Request for getting a voided Statement. + * + * @see Voided + * Statements + * + * @author Thomas Turrell-Croft + */ +@SuperBuilder +@Getter +public class GetVoidedStatementRequest extends GetStatementRequest { + + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + return uriBuilder.path("statements") + + .queryParam("voidedStatementId", id) + + .queryParamIfPresent("format", Optional.ofNullable(format)) + + .queryParamIfPresent("attachments", Optional.ofNullable(attachments)); + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/PostStatementRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/PostStatementRequest.java new file mode 100644 index 00000000..ad016646 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/PostStatementRequest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import dev.learning.xapi.model.Statement; +import java.util.Map; +import java.util.function.Consumer; +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Request for posting multiple Statements. + * + * @see POST + * Statements + * + * @author Thomas Turrell-Croft + */ +@Builder +@Getter +public class PostStatementRequest implements Request { + + private final Statement statement; + + @Override + public HttpMethod getMethod() { + return HttpMethod.POST; + } + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + return uriBuilder.path("statements"); + + } + + /** + * Builder for PostStatementRequest. + */ + public static class Builder { + + // This static class extends the lombok builder. + + /** + * Consumer Builder for statement. + * + * @param statement The Consumer Builder for statement + * + * @return This builder + * + * @see PostStatementRequest#statement + */ + public Builder statement(Consumer statement) { + + final Statement.Builder builder = Statement.builder(); + + statement.accept(builder); + + return statement(builder.build()); + } + + /** + * Sets the statement. + * + * @param statement The Statement to post + * + * @return This builder + * + * @see PostStatementRequest#statement + */ + public Builder statement(Statement statement) { + + this.statement = statement; + + return this; + } + + } + +} diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/PostStatementsRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/PostStatementsRequest.java new file mode 100644 index 00000000..1a3bc262 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/PostStatementsRequest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.client; + +import dev.learning.xapi.model.Statement; +import java.util.Map; +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Request for posting multiple Statements. + * + * @see POST + * Statements + * + * @author Thomas Turrell-Croft + */ +@Builder +@Getter +public class PostStatementsRequest implements Request { + + private final Statement[] statements; + + @Override + public HttpMethod getMethod() { + return HttpMethod.POST; + } + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + return uriBuilder.path("statements"); + + } + + /** + * Builder for PostStatementsRequest. + */ + public static class Builder { + + // This static class extends the lombok builder. + + /** + * Sets the statements. + * + * @param statements The statements of the PostStatementsRequest. + * + * @return This builder + * + * @see PostStatementsRequest#statements + */ + public Builder statements(Statement... statements) { + this.statements = statements; + return this; + } + + } + +} 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 index 1ca66587..69d36218 100644 --- a/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java +++ b/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java @@ -4,6 +4,8 @@ package dev.learning.xapi.client; +import dev.learning.xapi.model.Statement; +import dev.learning.xapi.model.StatementResult; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; @@ -40,6 +42,311 @@ public XapiClient(WebClient.Builder builder) { .build(); } + /** + * Gets a Statement. + * + *

+ * The returned ResponseEntity contains the response headers and the Statement. + *

+ * + * @return the ResponseEntity + */ + public Mono> getStatement(GetStatementRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .retrieve() + + .toEntity(Statement.class); + + } + + /** + * Gets a Statement. + * + *

+ * The returned ResponseEntity contains the response headers and the Statement. + *

+ * + * @return the ResponseEntity + */ + public Mono> getStatement( + Consumer> request) { + + final GetStatementRequest.Builder builder = GetStatementRequest.builder(); + + request.accept(builder); + + return getStatement(builder.build()); + + } + + /** + * Posts Statement. + * + *

+ * The returned ResponseEntity contains the response headers and the Statement identifier. + *

+ * + * @return the ResponseEntity + */ + public Mono> postStatement(PostStatementRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .bodyValue(request.getStatement()) + + .retrieve() + + .toEntity(String[].class) + + .map(i -> ResponseEntity.ok().headers(i.getHeaders()).body(i.getBody()[0])); + + } + + /** + * Posts Statement. + * + *

+ * The returned ResponseEntity contains the response headers and the Statement identifier. + *

+ * + * @return the ResponseEntity + */ + public Mono> postStatement( + Consumer request) { + + final PostStatementRequest.Builder builder = PostStatementRequest.builder(); + + request.accept(builder); + + return postStatement(builder.build()); + + } + + /** + * Post Statements. + * + *

+ * The returned ResponseEntity contains the response headers and an array of Statement + * identifiers. + *

+ * + * @return the ResponseEntity + */ + public Mono> postStatements(PostStatementsRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .bodyValue(request.getStatements()) + + .retrieve() + + .toEntity(String[].class); + + } + + /** + * Posts Statements. + * + *

+ * The returned ResponseEntity contains the response headers and an array of Statement + * identifiers. + *

+ * + * @return the ResponseEntity + */ + public Mono> postStatements( + Consumer request) { + + final PostStatementsRequest.Builder builder = PostStatementsRequest.builder(); + + request.accept(builder); + + return postStatements(builder.build()); + + } + + /** + * Gets a voided Statement. + * + *

+ * The returned ResponseEntity contains the response headers and the voided Statement. + *

+ * + * @return the ResponseEntity + */ + public Mono> getVoidedStatement(GetVoidedStatementRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .retrieve() + + .toEntity(Statement.class); + + } + + /** + * Gets a voided Statement. + * + *

+ * The returned ResponseEntity contains the response headers and the voided Statement. + *

+ * + * @return the ResponseEntity + */ + public Mono> getVoidedStatement( + Consumer> request) { + + final GetVoidedStatementRequest.Builder builder = GetVoidedStatementRequest.builder(); + + request.accept(builder); + + return getVoidedStatement(builder.build()); + + } + + /** + * Gets a StatementResult object, a list of Statements. If additional results are available, an + * URL to retrieve them will be included in the StatementResult Object. + * + *

+ * The returned ResponseEntity contains the response headers and StatementResult. + *

+ * + * @return the ResponseEntity + */ + public Mono> getStatements() { + + return getStatements(GetStatementsRequest.builder().build()); + } + + /** + * Gets a StatementResult object, a list of Statements. If additional results are available, an + * URL to retrieve them will be included in the StatementResult Object. + * + *

+ * The returned ResponseEntity contains the response headers and StatementResult. + *

+ * + * @param request The parameters of the get statements request + * + * @return the ResponseEntity + */ + public Mono> getStatements(GetStatementsRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .retrieve() + + .toEntity(StatementResult.class); + + } + + /** + * Gets a StatementResult object, a list of Statements. If additional results are available, an + * URL to retrieve them will be included in the StatementResult Object. + * + *

+ * The returned ResponseEntity contains the response headers and StatementResult. + *

+ * + * @param request The Consumer Builder for the get statements request + * + * @return the ResponseEntity + */ + public Mono> getStatements( + Consumer request) { + + final GetStatementsRequest.Builder builder = GetStatementsRequest.builder(); + + request.accept(builder); + + return getStatements(builder.build()); + + } + + /** + * Gets a StatementResult object, a list of Statements. If additional results are available, an + * URL to retrieve them will be included in the StatementResult Object. + * + *

+ * The returned ResponseEntity contains the response headers and StatementResult. + *

+ * + * @param request The parameters of the get more statements request + * + * @return the ResponseEntity + */ + public Mono> getMoreStatements(GetMoreStatementsRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .retrieve() + + .toEntity(StatementResult.class); + + } + + /** + * Gets a StatementResult object, a list of Statements. If additional results are available, an + * URL to retrieve them will be included in the StatementResult Object. + * + *

+ * The returned ResponseEntity contains the response headers and StatementResult. + *

+ * + * @param request The Consumer Builder for the get more statements request + * + * @return the ResponseEntity + */ + public Mono> getMoreStatements( + Consumer request) { + + final GetMoreStatementsRequest.Builder builder = GetMoreStatementsRequest.builder(); + + request.accept(builder); + + return getMoreStatements(builder.build()); + + } + /** * Gets a single document specified by the given stateId activity, agent, and optional * registration. diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/GetMoreStatementsRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/GetMoreStatementsRequestTests.java new file mode 100644 index 00000000..39cdb842 --- /dev/null +++ b/xapi-client/src/test/java/dev/learning/xapi/client/GetMoreStatementsRequestTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ +package dev.learning.xapi.client; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.net.URI; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * GetMoreStatementsRequest Tests. + * + * @author Thomas Turrell-Croft + */ +@DisplayName("GetMoreStatementsRequest Tests") +class GetMoreStatementsRequestTests { + + @Test + void whenBuildingGetMoreStatementsRequestWithAllParametersThenNoExceptionIsThrown() { + + // When Building GetMoreStatementsRequest With All Parameters + GetMoreStatementsRequest.Builder builder = GetMoreStatementsRequest.builder() + + .more( + URI.create("https://example.com/xapi/statements/869cc589-76fa-4283-8e96-eea86f9124e1")); + + // Then No Exception Is Thrown + assertDoesNotThrow(() -> builder.build()); + + } + + @Test + void whenBuildingGetMoreStatementsRequestWithoutMoreThenNullPointerExceptionIsThrown() { + + // When Building GetMoreStatementsRequest Without More + GetMoreStatementsRequest.Builder builder = GetMoreStatementsRequest.builder(); + + // Then NullPointerException Is Thrown + assertThrows(NullPointerException.class, () -> builder.build()); + + } + + +} diff --git a/xapi-client/src/test/java/dev/learning/xapi/client/GetStatementsRequestTests.java b/xapi-client/src/test/java/dev/learning/xapi/client/GetStatementsRequestTests.java new file mode 100644 index 00000000..5e4355cd --- /dev/null +++ b/xapi-client/src/test/java/dev/learning/xapi/client/GetStatementsRequestTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ +package dev.learning.xapi.client; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import dev.learning.xapi.model.StatementFormat; +import java.net.URI; +import java.time.Instant; +import java.util.UUID; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * GetStatementsRequest Tests. + * + * @author Thomas Turrell-Croft + */ +@DisplayName("GetStatementsRequest Tests") +class GetStatementsRequestTests { + + @Test + void whenBuildingGetStatementsRequestWithAllParametersThenNoExceptionIsThrown() { + + // When Building GetStatementsRequest With All Parameters + GetStatementsRequest.Builder builder = GetStatementsRequest.builder() + + .agent(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(URI.create("http://adlnet.gov/expapi/verbs/answered")) + + .activity(URI.create("https://example.com/activity/1")) + + .registration(UUID.fromString("dbf5d9e8-d2aa-4d57-9754-b11e3f195fe3")) + + .relatedActivities(true) + + .relatedAgents(true) + + .since(Instant.parse("2016-01-01T00:00:00Z")) + + .until(Instant.parse("2018-01-01T00:00:00Z")) + + .limit(10) + + .format(StatementFormat.CANONICAL) + + .attachments(true) + + .ascending(true); + + // Then No Exception Is Thrown + assertDoesNotThrow(() -> builder.build()); + + } + +} 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 index 1debc04d..f3bbd585 100644 --- a/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java +++ b/xapi-client/src/test/java/dev/learning/xapi/client/XapiClientTests.java @@ -1,11 +1,17 @@ /* - * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + * Copyright 2016rue-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 dev.learning.xapi.model.Statement; +import dev.learning.xapi.model.StatementFormat; +import dev.learning.xapi.model.Verb; +import java.time.Instant; +import java.util.Locale; +import java.util.UUID; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; @@ -39,9 +45,8 @@ void setUp() throws Exception { mockWebServer = new MockWebServer(); mockWebServer.start(); - String baseUrl = String.format("http://localhost:%s", mockWebServer.getPort()); + webClientBuilder.baseUrl(mockWebServer.url("").toString()); - webClientBuilder.baseUrl(baseUrl); client = new XapiClient(webClientBuilder); } @@ -51,6 +56,484 @@ void tearDown() throws Exception { mockWebServer.shutdown(); } + // Get Statement + + @Test + void whenGettingStatementThenMethodIsGet() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements + client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Get + assertThat(recordedRequest.getMethod(), is("GET")); + } + + @Test + void whenGettingStatementThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statement + client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?statementId=4df42866-40e7-45b6-bf7c-8d5fccbdccd6")); + } + + @Test + void whenGettingStatementThenBodyIsInstanceOfStatement() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content") + + .setBody( + "{\"actor\":{\"objectType\":\"Agent\",\"name\":\"A N Other\",\"mbox\":\"mailto:another@example.com\"},\"verb\":{\"id\":\"http://adlnet.gov/expapi/verbs/attempted\",\"display\":{\"und\":\"attempted\"}},\"object\":{\"objectType\":\"Activity\",\"id\":\"https://example.com/activity/simplestatement\",\"definition\":{\"name\":{\"en\":\"Simple Statement\"}}}}") + .addHeader("Content-Type", "application/json; charset=utf-8")); + + // When Getting Statement + var response = client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block(); + + // Then Body Is Instance Of Statement + assertThat(response.getBody(), instanceOf(Statement.class)); + } + + @Test + void whenGettingStatementWithAttachmentsThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statement With Attachments + client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6").attachments(true)) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?statementId=4df42866-40e7-45b6-bf7c-8d5fccbdccd6&attachments=true")); + } + + @Test + void whenGettingStatementWithCanonicalFormatThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statement With Canonical Format + client + .getStatement( + r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6").format(StatementFormat.CANONICAL)) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?statementId=4df42866-40e7-45b6-bf7c-8d5fccbdccd6&format=canonical")); + } + + // Posting Statements + + @Test + void whenPostingStatementsThenMethodIsPost() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + final Statement attemptedStatement = Statement.builder() + + .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))) + + .build(); + + final Statement passedStatement = attemptedStatement.toBuilder().verb(Verb.PASSED).build(); + + Statement statements[] = {attemptedStatement, passedStatement}; + + // When posting Statements + client.postStatements(r -> r.statements(statements)).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Post + assertThat(recordedRequest.getMethod(), is("POST")); + } + + @Test + void whenPostingStatementsThenBodyIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + final Statement attemptedStatement = Statement.builder() + + .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))) + + .build(); + + final Statement passedStatement = attemptedStatement.toBuilder().verb(Verb.PASSED).build(); + + Statement statements[] = {attemptedStatement, passedStatement}; + + // When Posting Statements + client.postStatements(r -> r.statements(statements)).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Body Is Expected + assertThat(recordedRequest.getBody().readUtf8(), is( + "[{\"actor\":{\"name\":\"A N Other\",\"mbox\":\"mailto:another@example.com\"},\"verb\":{\"id\":\"http://adlnet.gov/expapi/verbs/attempted\",\"display\":{\"und\":\"attempted\"}},\"object\":{\"objectType\":\"Activity\",\"id\":\"https://example.com/activity/simplestatement\",\"definition\":{\"name\":{\"en\":\"Simple Statement\"}}}},{\"actor\":{\"name\":\"A N Other\",\"mbox\":\"mailto:another@example.com\"},\"verb\":{\"id\":\"http://adlnet.gov/expapi/verbs/passed\",\"display\":{\"und\":\"passed\"}},\"object\":{\"objectType\":\"Activity\",\"id\":\"https://example.com/activity/simplestatement\",\"definition\":{\"name\":{\"en\":\"Simple Statement\"}}}}]")); + } + + @Test + void whenPostingStatementsThenContentTypeHeaderIsApplicationJson() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + final Statement attemptedStatement = Statement.builder() + + .actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))) + + .build(); + + final Statement passedStatement = attemptedStatement.toBuilder().verb(Verb.PASSED).build(); + + Statement statements[] = {attemptedStatement, passedStatement}; + + // When Posting Statements + client.postStatements(r -> r.statements(statements)).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Content Type Header Is Application Json + assertThat(recordedRequest.getHeader("content-type"), is("application/json")); + } + + // Posting a Statement + + @Test + void whenPostingStatementThenMethodIsPost() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content") + .setBody("[\"19a74a3f-7354-4254-aa4a-1c39ab4f2ca7\"]") + .setHeader("Content-Type", "application/json")); + + // When posting Statement + client.postStatement( + r -> r.statement(s -> s.actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Post + assertThat(recordedRequest.getMethod(), is("POST")); + } + + @Test + void whenPostingStatementThenBodyIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content") + .setBody("[\"19a74a3f-7354-4254-aa4a-1c39ab4f2ca7\"]") + .setHeader("Content-Type", "application/json")); + + // When Posting Statement + client.postStatement( + r -> r.statement(s -> s.actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Body Is Expected + assertThat(recordedRequest.getBody().readUtf8(), is( + "{\"actor\":{\"name\":\"A N Other\",\"mbox\":\"mailto:another@example.com\"},\"verb\":{\"id\":\"http://adlnet.gov/expapi/verbs/attempted\",\"display\":{\"und\":\"attempted\"}},\"object\":{\"objectType\":\"Activity\",\"id\":\"https://example.com/activity/simplestatement\",\"definition\":{\"name\":{\"en\":\"Simple Statement\"}}}}")); + } + + @Test + void whenPostingStatementThenContentTypeHeaderIsApplicationJson() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content") + .setBody("[\"19a74a3f-7354-4254-aa4a-1c39ab4f2ca7\"]") + .setHeader("Content-Type", "application/json")); + + // When Posting Statement + client.postStatement( + r -> r.statement(s -> s.actor(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb(Verb.ATTEMPTED) + + .activityObject(o -> o.id("https://example.com/activity/simplestatement") + .definition(d -> d.addName(Locale.ENGLISH, "Simple Statement"))))) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Content Type Header Is Application Json + assertThat(recordedRequest.getHeader("content-type"), is("application/json")); + } + + // Get Voided Statement + + @Test + void whenGettingVoidedStatementThenMethodIsGet() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Voided Statement + client.getVoidedStatement(r -> r.id(UUID.fromString("4df42866-40e7-45b6-bf7c-8d5fccbdccd6"))) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Get + assertThat(recordedRequest.getMethod(), is("GET")); + } + + @Test + void whenGettingVoidedStatementThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Voided Statement + client.getVoidedStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?voidedStatementId=4df42866-40e7-45b6-bf7c-8d5fccbdccd6")); + } + + @Test + void whenGettingVoidedStatementWithAttachmentsThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Voided Statement With Attachments + client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6").attachments(true)) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?statementId=4df42866-40e7-45b6-bf7c-8d5fccbdccd6&attachments=true")); + } + + @Test + void whenGettingVoidedStatementWithCanonicalFormatThenPathIsExpected() + throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Voided Statement With Canonical Format + client + .getStatement( + r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6").format(StatementFormat.CANONICAL)) + .block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?statementId=4df42866-40e7-45b6-bf7c-8d5fccbdccd6&format=canonical")); + } + + // Get Statements + + @Test + void whenGettingStatementsThenMethodIsGet() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements + client.getStatements().block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Get + assertThat(recordedRequest.getMethod(), is("GET")); + } + + + @Test + void whenGettingStatementsThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements + client.getStatements().block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), is("/statements")); + } + + @Test + void whenGettingStatementsWithAllParametersThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements With All Parameters + client.getStatements(r -> r + + .agent(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + .verb("http://adlnet.gov/expapi/verbs/answered") + + .activity("https://example.com/activity/1") + + .registration("dbf5d9e8-d2aa-4d57-9754-b11e3f195fe3") + + .relatedActivities(true) + + .relatedAgents(true) + + .since(Instant.parse("2016-01-01T00:00:00Z")) + + .until(Instant.parse("2018-01-01T00:00:00Z")) + + .limit(10) + + .format(StatementFormat.CANONICAL) + + .attachments(true) + + .ascending(true) + + ).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), is( + "/statements?agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22mailto%3Aanother%40example.com%22%7D&verb=http%3A%2F%2Fadlnet.gov%2Fexpapi%2Fverbs%2Fanswered&activity=https%3A%2F%2Fexample.com%2Factivity%2F1&since=2016-01-01T00%3A00%3A00Z&until=2018-01-01T00%3A00%3A00Z®istration=dbf5d9e8-d2aa-4d57-9754-b11e3f195fe3&related_activities=true&related_agents=true&limit=10&format=canonical&attachments=true&ascending=true")); + } + + @Test + void whenGettingStatementsWithAgentParameterThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements With Agent Parameter + client.getStatements(r -> r + + .agent(a -> a.name("A N Other").mbox("mailto:another@example.com")) + + ).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), is( + "/statements?agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22mailto%3Aanother%40example.com%22%7D")); + } + + @Test + void whenGettingStatementsWithVerbParameterThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements With Verb Parameter + client.getStatements(r -> r + + .verb("http://adlnet.gov/expapi/verbs/answered") + + ).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?verb=http%3A%2F%2Fadlnet.gov%2Fexpapi%2Fverbs%2Fanswered")); + } + + @Test + void whenGettingStatementsWithActivityParameterThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements With Activity Parameter + client.getStatements(r -> r + + .activity("https://example.com/activity/1") + + ).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/statements?activity=https%3A%2F%2Fexample.com%2Factivity%2F1")); + } + + + + @Test + void whenGettingMoreStatementsThenRequestMethodIsGet() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements With Activity Parameter + client.getMoreStatements(r -> r + + .more(mockWebServer.url("/xapi/statements/869cc589-76fa-4283-8e96-eea86f9124e1").uri()) + + ).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Get + assertThat(recordedRequest.getMethod(), is("GET")); + } + + @Test + void whenGettingMoreStatementsThenRequestURLExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 No Content")); + + // When Getting Statements With Activity Parameter + client.getMoreStatements(r -> r + + .more(mockWebServer.url("/xapi/statements/869cc589-76fa-4283-8e96-eea86f9124e1").uri()) + + ).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Request URL Is Expected + assertThat(recordedRequest.getRequestUrl(), + is(mockWebServer.url("/xapi/statements/869cc589-76fa-4283-8e96-eea86f9124e1"))); + } + // Get Single State @Test @@ -73,8 +556,6 @@ void whenGettingASingleStateThenMethodIsGet() throws InterruptedException { assertThat(recordedRequest.getMethod(), is("GET")); } - - @Test void whenGettingASingleStateThenPathIsExpected() throws InterruptedException { diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/StatementFormat.java b/xapi-model/src/main/java/dev/learning/xapi/model/StatementFormat.java new file mode 100644 index 00000000..558cd21c --- /dev/null +++ b/xapi-model/src/main/java/dev/learning/xapi/model/StatementFormat.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model; + +/** + * This enumeration class represents all valid Statement formats. + * + * @author Thomas Turrell-Croft + */ +public enum StatementFormat { + + IDS("ids"), + + EXACT("exact"), + + CANONICAL("canonical"); + + private final String format; + + StatementFormat(String format) { + this.format = format; + } + + public String getFormat() { + return format; + } + + @Override + public String toString() { + return format; + } + + // **Warning** do not add fields that are not required by the xAPI specification. +} diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/StatementFormatTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/StatementFormatTests.java new file mode 100644 index 00000000..fb4613da --- /dev/null +++ b/xapi-model/src/test/java/dev/learning/xapi/model/StatementFormatTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved. + */ + +package dev.learning.xapi.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import org.junit.jupiter.api.Test; + +class StatementFormatTests { + + @Test + void whenCallingToStringOnCanonicalEnumResultIsLowerCase() { + + // When Calling ToString On Canonical Enum + String result = StatementFormat.CANONICAL.toString(); + + // Result Is Lower Case + assertThat(result, is("canonical")); + } + + @Test + void whenCallingGetFormatOnCanonicalEnumResultIsLowerCase() { + + // When Calling GetFormat On Canonical Enum + String result = StatementFormat.CANONICAL.getFormat(); + + // Result Is Lower Case + assertThat(result, is("canonical")); + } + + @Test + void whenCallingToStringOnExactEnumResultIsLowerCase() { + + // When Calling ToString On Exact Enum + String result = StatementFormat.EXACT.toString(); + + // Result Is Lower Case + assertThat(result, is("exact")); + } + + @Test + void whenCallingGetFormatOnExactEnumResultIsLowerCase() { + + // When Calling GetFormat On Exact Enum + String result = StatementFormat.EXACT.getFormat(); + + // Result Is Lower Case + assertThat(result, is("exact")); + } + + @Test + void whenCallingToStringOnIdsEnumResultIsLowerCase() { + + // When Calling ToString On Ids Enum + String result = StatementFormat.IDS.toString(); + + // Result Is Lower Case + assertThat(result, is("ids")); + } + + @Test + void whenCallingGetFormatOnIdsEnumResultIsLowerCase() { + + // When Calling GetFormat On Ids Enum + String result = StatementFormat.IDS.getFormat(); + + // Result Is Lower Case + assertThat(result, is("ids")); + } + +}