Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add voidStatement and getStatementIterator to xApiEnhancedClient #144

Merged
merged 11 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ StatementResult moreStatementResult = moreResponse.getBody();
Statement[] statements = moreStatementResult.getStatements();
```

### Getting Statements as Iterator (and processing them as a Stream)

thomasturrell marked this conversation as resolved.
Show resolved Hide resolved
Example:

```java
var statements = client.getStatementIterator().block();

// process the first 100 Statements
statements.toStream().limit(100).forEach(s -> {
// add logic here...
});
```

### Posting a Statement

Expand Down
22 changes: 22 additions & 0 deletions samples/get-statement-iterator/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.learning.xapi.samples</groupId>
<artifactId>xapi-samples-build</artifactId>
<version>1.1.5-SNAPSHOT</version>
</parent>
<artifactId>get-statement-iterator</artifactId>
<name>Get xAPI StatementIterator Sample</name>
<description>Get xAPI StatementIterator</description>
<dependencies>
<dependency>
<groupId>dev.learning.xapi</groupId>
<artifactId>xapi-client</artifactId>
</dependency>
<dependency>
<groupId>dev.learning.xapi.samples</groupId>
<artifactId>core</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
*/

package dev.learning.xapi.samples.getstatements;

import dev.learning.xapi.client.XapiClient;
import dev.learning.xapi.model.StatementResult;
import dev.learning.xapi.model.Verb;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;

/**
* Sample using xAPI client to get multiple statements as StatementIterator.
*
* @author Thomas Turrell-Croft
* @author István Rátkai (Selindek)
*/
@SpringBootApplication
public class GetStatementIteratorApplication implements CommandLineRunner {

/**
* Default xAPI client. Properties are picked automatically from application.properties.
*/
@Autowired
private XapiClient client;

public static void main(String[] args) {
SpringApplication.run(GetStatementIteratorApplication.class, args).close();
}

@Override
public void run(String... args) throws Exception {

// Get Statements as StatementIterator
var iterator = client.getStatementIterator().block();

// Print the returned statements to the console
iterator.toStream().forEach(s -> System.out.println(s));

// Get Statements with Verb filter as StatementIterator
var filteredStatements =
client.getStatementIterator(r -> r.verb(Verb.ATTEMPTED.getId())).block();

// Print the returned statements to the console
filteredStatements.toStream().forEach(s -> System.out.println(s));

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
xapi.client.username = admin
xapi.client.password = password
xapi.client.baseUrl = https://example.com/xapi/
1 change: 1 addition & 0 deletions samples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<module>core</module>
<!-- Statements Resource -->
<module>get-statement</module>
<module>get-statement-iterator</module>
<module>get-statement-with-attachment</module>
<module>post-statement</module>
<module>post-signed-statement</module>
Expand Down
197 changes: 197 additions & 0 deletions xapi-client/src/main/java/dev/learning/xapi/client/XapiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@

import dev.learning.xapi.model.About;
import dev.learning.xapi.model.Activity;
import dev.learning.xapi.model.Actor;
import dev.learning.xapi.model.Person;
import dev.learning.xapi.model.Statement;
import dev.learning.xapi.model.StatementResult;
import dev.learning.xapi.model.Verb;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import lombok.RequiredArgsConstructor;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

Expand Down Expand Up @@ -1257,4 +1267,191 @@ public Mono<ResponseEntity<About>> getAbout() {

}

// Enhanced features

/**
* Gets a list of Statements as a {@link StatementIterator}.
* <p>
* This method loads ALL of Statements which fullfills the request filters from the LRS
* dynamically. (It sends additional
* {@link XapiClient#getMoreStatements(java.util.function.Consumer)} request if all the previously
* loaded Statements were processed from the iterator.)
* </p>
*
* @param request The parameters of the get statements request
*
* @return a {@link StatementIterator} object as a {@link Mono}.
*/
public Mono<StatementIterator> getStatementIterator(GetStatementsRequest request) {

return getStatements(request).map(result -> new StatementIterator(result));

}

/**
* Gets a list of Statements as a {@link StatementIterator}.
* <p>
* This method loads ALL of Statements which fullfills the request filters from the LRS
* dynamically. (It sends additional
* {@link XapiClient#getMoreStatements(java.util.function.Consumer)} request if all the previously
* loaded Statements were processed from the iterator.)
* </p>
*
* @param request The parameters of the get statements request
*
* @return a {@link StatementIterator} object as a {@link Mono}.
*/
public Mono<StatementIterator> getStatementIterator(
Consumer<GetStatementsRequest.Builder> request) {

final var builder = GetStatementsRequest.builder();

request.accept(builder);

return getStatementIterator(builder.build());

}

/**
* Gets all of the Statements as a {@link StatementIterator}.
* <p>
* This method loads ALL of Statements which fullfills the request filters from the LRS
* dynamically. (It sends additional
* {@link XapiClient#getMoreStatements(java.util.function.Consumer)} request if all the previously
* loaded Statements were processed from the iterator.)
* </p>
*
* @return a {@link StatementIterator} object as a {@link Mono}.
*/
public Mono<StatementIterator> getStatementIterator() {

return getStatementIterator(r -> {
});

}

/**
* <p>
* Voids a {@link Statement}.
* </p>
* The Actor of the voiding statement will be the same as the Actor of the target Statement.
* <p>
* The returned ResponseEntity contains the response headers and the Statement identifier of the
* generated voiding Statement.
* </p>
*
* @param targetStatement The {@link Statement} to be voided
*
* @return the ResponseEntity
*/
public Mono<ResponseEntity<UUID>> voidStatement(Statement targetStatement) {
return voidStatement(targetStatement, targetStatement.getActor());
}

/**
* Voids a {@link Statement}.
* <p>
* The returned ResponseEntity contains the response headers and the Statement identifier of the
* generated voiding Statement.
* </p>
*
* @param targetStatement The {@link Statement} to be voided
* @param actor the Actor of the voiding Statement
*
* @return the ResponseEntity
*/
public Mono<ResponseEntity<UUID>> voidStatement(Statement targetStatement, Actor actor) {
return voidStatement(targetStatement.getId(), actor);
}

/**
* Voids a {@link Statement}.
* <p>
* The returned ResponseEntity contains the response headers and the Statement identifier of the
* generated voiding Statement.
* </p>
*
* @param targetStatementId The id of the {@link Statement} to be voided
* @param actor the Actor of the voiding Statement
*
* @return the ResponseEntity
*/
public Mono<ResponseEntity<UUID>> voidStatement(UUID targetStatementId, Actor actor) {
Assert.notNull(targetStatementId, "Target Statement id cannot be null");
Assert.notNull(actor, "Actor cannot be null");

return postStatement(r -> r

.statement(s -> s

.actor(actor)

.verb(Verb.VOIDED)

.statementReferenceObject(o -> o

.id(targetStatementId)

)

)

);

}

/**
* <p>
* StatementIterator.
* </p>
* Iterates through the Statements of the result of a
* {@link XapiClient#getStatements(GetStatementsRequest)}. If more Statements are available it
* automatically loads them from the server.
*
* @author István Rátkai (Selindek)
*/
@RequiredArgsConstructor
public class StatementIterator implements Iterator<Statement> {

private URI more;
private Iterator<Statement> statements;

private StatementIterator(ResponseEntity<StatementResult> response) {
init(response);
}

/**
* Convenient method for transforming this StatementIterator to a {@link Stream}.
*
* @return a {@link Stream} of {@link Statement}s
*/
public Stream<Statement> toStream() {
final Iterable<Statement> iterable = () -> this;
return StreamSupport.stream(iterable.spliterator(), false);
}

private void init(ResponseEntity<StatementResult> response) {
final var statementResult = response.getBody();
more = statementResult.hasMore() ? statementResult.getMore() : null;
final var s = statementResult.getStatements();
statements = s == null ? Collections.emptyIterator() : s.iterator();
}

@Override
public boolean hasNext() {
return statements.hasNext() || more != null;
}

@Override
public Statement next() {
if (!statements.hasNext()) {
if (more == null) {
throw new NoSuchElementException();
}
init(getMoreStatements(r -> r.more(more)).block());
}
return statements.next();
}

}
}
Loading