From 60f9bec8e48a4177be6c24cfbcce3f305791241e Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Sun, 5 Mar 2023 23:40:10 +0000 Subject: [PATCH 1/2] Add agents resource --- samples/get-agents/pom.xml | 22 +++++ .../getagents/GetAgentsApplication.java | 81 +++++++++++++++++++ samples/pom.xml | 7 +- .../xapi/client/GetAgentsRequest.java | 58 +++++++++++++ .../dev/learning/xapi/client/XapiClient.java | 30 +++++++ 5 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 samples/get-agents/pom.xml create mode 100644 samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java create mode 100644 xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java diff --git a/samples/get-agents/pom.xml b/samples/get-agents/pom.xml new file mode 100644 index 00000000..75e8eba8 --- /dev/null +++ b/samples/get-agents/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + dev.learning.xapi.samples + xapi-samples-build + 1.0.6-SNAPSHOT + + get-agents + Get xAPI Agents Sample + Get xAPI Agents + + + dev.learning.xapi + xapi-client + + + dev.learning.xapi.samples + core + + + diff --git a/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java b/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java new file mode 100644 index 00000000..77adff5d --- /dev/null +++ b/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java @@ -0,0 +1,81 @@ +package dev.learning.xapi.samples.getagents; + +import dev.learning.xapi.client.XapiClient; +import dev.learning.xapi.model.Agent; +import dev.learning.xapi.model.Person; +import dev.learning.xapi.model.Verb; +import java.util.Locale; +import java.util.UUID; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; + +/** + * Sample using xAPI client to get agents. + * + * @author Thomas Turrell-Croft + */ +@SpringBootApplication +public class GetAgentsApplication implements CommandLineRunner { + + private final XapiClient client; + + /** + * Constructor for application. In this sample the WebClient.Builder instance is injected by the + * Spring Framework. + */ + public GetAgentsApplication(WebClient.Builder webClientBuilder) { + + webClientBuilder + // Change for the URL of your LRS + .baseUrl("https://cloud.scorm.com/lrs/6SX8FQA8Q9/") + // Set the Authorization value + .defaultHeader("Authorization", + "Basic NUJZLWhXay1IcXdtOVFGWFh3Yzo1LTRNSDFBSFVvbDJGM2x1SE1J") + + .build(); + + + client = new XapiClient(webClientBuilder); + } + + public static void main(String[] args) { + SpringApplication.run(GetAgentsApplication.class, args).close(); + } + + @Override + public void run(String... args) throws Exception { + + // Post statement for later retrieval of activity + postStatement(); + + Agent x = Agent.builder().name("A N Other").mbox("mailto:another@example.com").build(); + + // Get Activity + ResponseEntity response = client.getAgents(r -> r.agent(x)).block(); + + // Print the returned activity to the console + System.out.println(response.getBody()); + } + + private UUID postStatement() { + + // Post a statement + ResponseEntity< + UUID> response = + 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(); + + return response.getBody(); + } + +} diff --git a/samples/pom.xml b/samples/pom.xml index 5cc5d176..14b349a4 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -12,9 +12,9 @@ xAPI Samples Build learning.dev xAPI Samples Build - + true - + org.springframework.boot @@ -34,10 +34,11 @@ core delete-agent-profile - delete-state + delete-state get-activity get-agent-profile get-agent-profiles + get-agents get-state get-statement get-statements diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java new file mode 100644 index 00000000..eb772900 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java @@ -0,0 +1,58 @@ +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.util.Map; +import lombok.Builder; +import lombok.NonNull; +import org.springframework.http.HttpMethod; +import org.springframework.web.util.UriBuilder; + +/** + * Return a special, Person Object for a specified Agent. The Person Object is very similar to an + * Agent Object, but instead of each attribute having a single value, each attribute has an array + * value, and it is legal to include multiple identifying properties. + * + * @see Agents + * Resource + * + * @author Thomas Turrell-Croft + */ +@Builder +public class GetAgentsRequest implements Request { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * The Agent representation to use in fetching expanded Agent information. + */ + @NonNull + private Agent agent; + + @Override + public UriBuilder url(UriBuilder uriBuilder, Map queryParams) { + + queryParams.put("agent", agentToJsonString()); + + return uriBuilder.path("/agents").queryParam("agent", "{agent}"); + } + + @Override + public HttpMethod getMethod() { + return HttpMethod.GET; + } + + 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/XapiClient.java b/xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java index f378bd70..a343488e 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 @@ -5,6 +5,7 @@ package dev.learning.xapi.client; import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.Person; import dev.learning.xapi.model.Statement; import dev.learning.xapi.model.StatementResult; import java.util.HashMap; @@ -944,4 +945,33 @@ public Mono> getActivity(Consumer> getAgents(GetAgentsRequest request) { + + Map queryParams = new HashMap<>(); + + return this.webClient + + .method(request.getMethod()) + + .uri(u -> request.url(u, queryParams).build(queryParams)) + + .retrieve() + + .toEntity(Person.class); + + } + + + public Mono> getAgents(Consumer request) { + + final GetAgentsRequest.Builder builder = GetAgentsRequest.builder(); + + request.accept(builder); + + return getAgents(builder.build()); + + } + } From 9660dd2926981d0b271cc4cf377d3b4488d2a749 Mon Sep 17 00:00:00 2001 From: Thomas Turrell-Croft Date: Mon, 6 Mar 2023 13:22:32 +0000 Subject: [PATCH 2/2] Tip --- .../getagents/GetAgentsApplication.java | 12 ++-- .../xapi/client/GetAgentsRequest.java | 45 ++++++++++++++ .../dev/learning/xapi/client/XapiClient.java | 21 ++++++- .../learning/xapi/client/XapiClientTests.java | 61 +++++++++++++++++-- 4 files changed, 124 insertions(+), 15 deletions(-) diff --git a/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java b/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java index 77adff5d..f6347c56 100644 --- a/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java +++ b/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java @@ -1,7 +1,6 @@ package dev.learning.xapi.samples.getagents; import dev.learning.xapi.client.XapiClient; -import dev.learning.xapi.model.Agent; import dev.learning.xapi.model.Person; import dev.learning.xapi.model.Verb; import java.util.Locale; @@ -30,10 +29,9 @@ public GetAgentsApplication(WebClient.Builder webClientBuilder) { webClientBuilder // Change for the URL of your LRS - .baseUrl("https://cloud.scorm.com/lrs/6SX8FQA8Q9/") + .baseUrl("https://example.com/xapi/") // Set the Authorization value - .defaultHeader("Authorization", - "Basic NUJZLWhXay1IcXdtOVFGWFh3Yzo1LTRNSDFBSFVvbDJGM2x1SE1J") + .defaultHeader("Authorization", "") .build(); @@ -51,10 +49,10 @@ public void run(String... args) throws Exception { // Post statement for later retrieval of activity postStatement(); - Agent x = Agent.builder().name("A N Other").mbox("mailto:another@example.com").build(); - // Get Activity - ResponseEntity response = client.getAgents(r -> r.agent(x)).block(); + ResponseEntity response = + client.getAgents(r -> r.agent(a -> a.name("A N Other").mbox("mailto:another@example.com"))) + .block(); // Print the returned activity to the console System.out.println(response.getBody()); diff --git a/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java b/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java index eb772900..b0620399 100644 --- a/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import dev.learning.xapi.model.Agent; import java.util.Map; +import java.util.function.Consumer; import lombok.Builder; import lombok.NonNull; import org.springframework.http.HttpMethod; @@ -44,6 +45,50 @@ public HttpMethod getMethod() { return HttpMethod.GET; } + /** + * Builder for GetAgentsRequest. + */ + public static class Builder { + + /** + * Consumer Builder for agent. + * + * @param agent The Consumer Builder for agent. + * + * @return This builder + * + * @see GetAgentsRequest#agent + */ + public Builder 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 GetAgentsRequest. + * + * @return This builder + * + * @see GetAgentsRequest#agent + */ + public Builder agent(Agent agent) { + + this.agent = agent; + + return this; + + } + + } + + private String agentToJsonString() { try { 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 a343488e..26d5102f 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 @@ -945,8 +945,15 @@ public Mono> getActivity(Consumer> getAgents(GetAgentsRequest request) { Map queryParams = new HashMap<>(); @@ -963,7 +970,15 @@ public Mono> getAgents(GetAgentsRequest request) { } - + /** + * Return a special, Person Object for a specified Agent. The Person Object is very similar to an + * Agent Object, but instead of each attribute having a single value, each attribute has an array + * value, and it is legal to include multiple identifying properties. + * + * @param request The Consumer Builder for the get agents request + * + * @return the ResponseEntity + */ public Mono> getAgents(Consumer request) { final GetAgentsRequest.Builder builder = GetAgentsRequest.builder(); 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 1cc2035f..25c4e965 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 @@ -7,6 +7,7 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import dev.learning.xapi.model.Activity; +import dev.learning.xapi.model.Person; import dev.learning.xapi.model.Statement; import dev.learning.xapi.model.StatementFormat; import dev.learning.xapi.model.Verb; @@ -1475,7 +1476,7 @@ void whenPuttingASingleAgentProfileWithoutContentTypeThenBodyIsExpected() .profileId("person") - .profile(new Person("A N", "Other"))) + .profile(new SamplePerson("A N", "Other"))) .block(); @@ -1593,7 +1594,7 @@ void whenPostingASingleAgentProfileWithoutContentTypeThenBodyIsExpected() .profileId("person") - .profile(new Person("A N", "Other"))) + .profile(new SamplePerson("A N", "Other"))) .block(); @@ -1668,7 +1669,7 @@ void whenGettingProfilesWithSinceParameterThenPathIsExpected() throws Interrupte "/agents/profile?agent=%7B%22name%22%3A%22A%20N%20Other%22%2C%22mbox%22%3A%22mailto%3Aanother%40example.com%22%7D&since=2016-01-01T00%3A00%3A00Z")); } - // Get Statement + // Get Activity @Test void whenGettingActivityThenMethodIsGet() throws InterruptedException { @@ -1723,13 +1724,63 @@ void whenGettingActivityThenBodyIsInstanceOfActivity() throws InterruptedExcepti + // Get Agents + + @Test + void whenGettingAgentsThenMethodIsGet() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK")); + + // When Getting Agents + client.getAgents(r -> r.agent(a -> a.mbox("mailto:another@example.com"))).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Method Is Get + assertThat(recordedRequest.getMethod(), is("GET")); + } + + @Test + void whenGettingAgentsThenPathIsExpected() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK")); + + // When Getting Agents + client.getAgents(r -> r.agent(a -> a.mbox("mailto:another@example.com"))).block(); + + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + // Then Path Is Expected + assertThat(recordedRequest.getPath(), + is("/agents?agent=%7B%22mbox%22%3A%22mailto%3Aanother%40example.com%22%7D")); + } + + @Test + void whenGettingAgentsThenBodyIsInstanceOfPerson() throws InterruptedException { + + mockWebServer.enqueue(new MockResponse().setStatus("HTTP/1.1 200 OK") + + .setBody( + "{\"name\":[\"A N Other\"],\"mbox\":[\"mailto:another@example.com\"],\"objectType\":\"Person\"}") + .addHeader("Content-Type", "application/json; charset=utf-8")); + + // When Getting Agents + var response = + client.getAgents(r -> r.agent(a -> a.mbox("mailto:another@example.com"))).block(); + + // Then Body Is Instance Of Activity + assertThat(response.getBody(), instanceOf(Person.class)); + } + + + @Getter - private static class Person { + private static class SamplePerson { private String firstName; private String lastName; - public Person(String firstName, String lastName) { + public SamplePerson(String firstName, String lastName) { super(); this.firstName = firstName; this.lastName = lastName;