Skip to content

andrew-tembra/xapi-java

 
 

Repository files navigation

xAPI Java CodeQL Quality Gate Status

xAPI Java helps you to create applications that send or receive xAPI Statements or Documents.

There are two projects in this Monorepo, xAPI Client and xAPI Model.

Both the xAPI Client and xAPI Model use a fluent interface. Objects are immutable.

CheckStyle is used to enforce the Google Java Style Guide. Sonar performs automatic pull request reviews. CodeQL scans for vulnerabilities. The number of bugs, code smells and vulnerabilities in the codebase can be viewed in SonarCloud. The code coverage and code duplication percentages can also be viewed in SonarCloud. Over three-hundred unit tests ensure conformance with the xAPI specification.

Requirements

xAPI Java requires Java 17 or newer.

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 Java Client include the appropriate XML in the dependencies section of your pom.xml, as shown in the following example:

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>getting-started</artifactId>
    <!-- ... -->
    <dependencies>
        <!-- ... -->
        <dependency>
            <groupId>dev.learning.xapi</groupId>
            <artifactId>xapi-client</artifactId>
            <version>1.1.3</version>
        </dependency>
    </dependencies>
</project>

Configuration

The xAPI Java Client has a Spring AutoConfiguration bean which picks up the following properties:

Property Description
xapi.client.baseUrl The base url of the LRS endpoint
xapi.client.username Username for basic authorization header
xapi.client.password Password for basic authorization header
xapi.client.authorization Authorization header (has precedence over the username and password properties)

Properties can be set using any external configuration method supported by Spring Boot.

If you need more specific customization (eg. your LRS needs specific headers, or you want to set the authorization header dynamically) you can create a custom configurer by implementing the XapiClientConfigurer interface.

Statement Resource

The xAPI Java Client allows applications to store and fetch xAPI Statements.

Getting a Statement

Example:

var response = client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block();
    
Statement statement = response.getBody();

Getting a Statement with attachments

Example:

var response = client.getStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6").attachments(true).block();
    
Statement statement = response.getBody();

Getting Statements

Example:

var response = client.getStatements().block();

StatementResult statementResult = response.getBody();

Statement[] statements = statementResult.getStatements();

Getting the next page of Statements

Example:

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:

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 a Statement with an attachment

Example:

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")))

        .addAttachment(a -> a.content("Simple attachment").length(17).contentType("text/plain")
            .usageType(URI.create("https://example.com/attachments/simplestatement"))
            .addDisplay(Locale.ENGLISH, "text attachment"))

    )).block();

Posting Statements

Example:

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 a voided Statement

Example:

var response = client.getVoidedStatement(r -> r.id("4df42866-40e7-45b6-bf7c-8d5fccbdccd6")).block();

Statement voidedStatement = response.getBody();

State Resource

The xAPI Java Client allows applications to store, change, fetch, or delete state documents.

Getting a state

Example:

var response = client.getState(r -> r.activityId("https://example.com/activity/1")

    .agent(a -> a.name("A N Other").mbox("mailto:another@example.com"))

    .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")

    .stateId("bookmark"), String.class)

    .block();

String state = response.getBody();

Posting a state

Example:

client.postState(r -> r.activityId("https://example.com/activity/1")

    .agent(a -> a.name("A N Other").mbox("mailto:another@example.com"))

    .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")

    .stateId("bookmark")

    .state("Hello World!"))

    .block();

Putting a state

Example:

client.putState(r -> r.activityId("https://example.com/activity/1")

    .agent(a -> a.name("A N Other").mbox("mailto:another@example.com"))

    .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")

    .stateId("bookmark")

    .state("Hello World!"))

    .block();

Deleting a state

Example:

client.deleteState(r -> r.activityId("https://example.com/activity/1")

    .agent(a -> a.name("A N Other").mbox("mailto:another@example.com"))

    .registration("67828e3a-d116-4e18-8af3-2d2c59e27be6")

    .stateId("bookmark"))

    .block();

Samples

The samples folder in this repository contains sample applications that use the xAPI client.

xAPI Java Model

The xAPI model can be used by clients that send xAPI data or by servers that receive xAPI data.

Getting started

To use the xAPI Model include the appropriate XML in the dependencies section of your pom.xml, as shown in the following example:

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>getting-started</artifactId>
    <!-- ... -->
    <dependencies>
        <!-- ... -->
        <dependency>
            <groupId>dev.learning.xapi</groupId>
            <artifactId>xapi-model</artifactId>
            <version>1.1.3</version>
        </dependency>
    </dependencies>
</project>

Creating a statement

Example:

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();

Deserializing Statements

The Jackson ObjectMapper can be used to deserialize statements into Java objects.

Example:

String json = """
    {
        "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"
                }
            }
        }
    }""";

Statement statement = objectMapper.readValue(json, Statement.class);

Serializing Statements

The Jackson ObjectMapper can be used to serialize Statement objects into JSON.

Example:

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();

String json = objectMapper.writeValueAsString(statement);

Creating a new statement using an existing statement as template

Example:

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();

Statement completed = passed.toBuilder().verb(Verb.COMPLETED).build();

Validating Statements

Statements can be validated programmatically.

Example:

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

Statement statement = Statement.builder()

    .actor(a -> a.name("A N Other").mbox("mailto:another@example.com"))

    .activityObject(o -> o.id("https://example.com/xapi/activity/simplestatement"))

    .build();

Set<ConstraintViolation<Statement>> constraintViolations = validator.validate(statement);

System.out.println(constraintViolations)

// Prints [ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=verb, rootBeanClass=class dev.learning.xapi.model.Statement, messageTemplate='{jakarta.validation.constraints.NotNull.message}'}]

Statements can also be validated when they are received by a method in a REST controller. The following example requires Spring MVC and the Hibernate Validator.

@PostMapping
public ResponseEntity<Collection<UUID>> postStatements(
    @RequestBody List<@Valid Statement> statements) {

  // Process the statements

  return new ResponseEntity<>(HttpStatus.OK);
}

About

Java xAPI model, client and tools

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Java 100.0%