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..f6347c56 --- /dev/null +++ b/samples/get-agents/src/main/java/dev/learning/xapi/samples/getagents/GetAgentsApplication.java @@ -0,0 +1,79 @@ +package dev.learning.xapi.samples.getagents; + +import dev.learning.xapi.client.XapiClient; +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://example.com/xapi/") + // Set the Authorization value + .defaultHeader("Authorization", "") + + .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(); + + // Get Activity + 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()); + } + + 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..b0620399 --- /dev/null +++ b/xapi-client/src/main/java/dev/learning/xapi/client/GetAgentsRequest.java @@ -0,0 +1,103 @@ +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 java.util.function.Consumer; +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; + } + + /** + * 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 { + 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..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 @@ -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,48 @@ 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); + + } + + /** + * 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(); + + request.accept(builder); + + return getAgents(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 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;