Java Client Usage

The typed Java HTTP client for the Loom REST API — how to build it, authenticate it and use it from a JVM application.

The loom-client-rest module provides a typed HTTP client for JVM applications.

Maven Dependency

<dependency>
    <groupId>io.metaloom.loom.client</groupId>
    <artifactId>loom-client-rest</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

Creating a Client

Use the builder to configure and construct the client:

LoomHttpClient client = LoomHttpClient.builder()
    .setHostname("localhost")
    .setPort(8092)
    .build();

Connect, read and write timeouts default to ten seconds each and can be changed with setConnectTimeout, setReadTimeout and setWriteTimeout.

Authentication

AuthLoginResponse login = client.login("admin", "secret").sync().body();
client.setToken(login.getToken());

The token is automatically included as a Bearer header in all subsequent requests.

Sending Requests

Every method returns a request that has not been sent yet, which is what allows paging and filtering to be attached afterwards. Calling sync() sends it and returns a response wrapper; body() unwraps the model:

UserResponse user = client.loadUser(userUuid).sync().body();

LoomClientResponse<UserResponse> response = client.loadUser(userUuid).sync();
System.out.println(response.statusCode());

Working with Assets

An asset can be addressed by its UUID or by the SHA-512 of its content:

AssetResponse byUuid = client.loadAsset(assetUuid).sync().body();
AssetResponse byHash = client.loadAsset(SHA512.fromString(digest)).sync().body();

Creating, updating and deleting:

AssetCreateRequest createReq = new AssetCreateRequest();
createReq.setHashes(new HashInfo().setSHA512(SHA512.fromString(digest)));
createReq.setFile(new FileInfo()
    .setFilename("photo.jpg")
    .setMimeType("image/jpeg")
    .setSize(sizeInBytes)
    .setOrigin("import-job"));

AssetResponse asset = client.createAsset(createReq).sync().body();

AssetUpdateRequest updateReq = new AssetUpdateRequest().setFilename("beach.jpg");
client.updateAsset(asset.getUuid(), updateReq).sync();

client.deleteAsset(asset.getUuid()).sync();

AssetListResponse list = client.listAssets().addLimit(50).sync().body();

An asset requires an origin — the request is rejected without one.

Deleting an asset takes everything that is about it with it: its analysis results, its locations, its tag assignments, the collections and libraries it was filed in, the tasks that referenced it, the notes people wrote on it, and the comments and reactions left on it — replies included.

What it never touches is the things it was merely linked to. The tag, the collection, the library, the task and the person all stay, along with every other asset they cover: a task about five assets keeps the other four and everything written on the task itself. A tag that no asset carries any more is an empty tag, not a deleted one.

The one direction that is still refused is deleting a library that has assets in it — a library is not removed out from under its contents.

Working with Tags

A tag always belongs to a collection.

TagCreateRequest tagReq = new TagCreateRequest()
    .setName("holiday")
    .setCollection("Travel");

TagResponse tag = client.tagAsset(assetUuid, tagReq).sync().body();

client.untagAsset(assetUuid, tag.getUuid()).sync();

Working with Users, Groups and Roles

UserCreateRequest userReq = new UserCreateRequest().setUsername("alice");
UserResponse user = client.createUser(userReq).sync().body();

GroupCreateRequest groupReq = new GroupCreateRequest().setName("editors");
GroupResponse group = client.createGroup(groupReq).sync().body();

RoleCreateRequest roleReq = new RoleCreateRequest().setName("asset-editor");
RoleResponse role = client.createRole(roleReq).sync().body();

Permissions reach a user through a group that holds a role.

Paging and Filtering

UserListResponse page = client.listUsers()
    .addLimit(50)
    .sortBy(LoomSortKey.USERNAME)
    .sync()
    .body();

// Continue from the last element of the previous page
UserListResponse next = client.listUsers()
    .addLimit(50)
    .addFrom(page.getMetainfo().getLastUuid())
    .sync()
    .body();

Pipelines

PipelineCreateRequest pipelineReq = new PipelineCreateRequest()
    .setName("ingest-pipeline")
    .setEnabled(true);
PipelineResponse pipeline = client.createPipeline(pipelineReq).sync().body();

PipelineListResponse pipelines = client.listPipelines().sync().body();

Asynchronous Usage

Requests can also be sent asynchronously, returning an RxJava Single:

// Synchronous
AssetResponse asset = client.loadAsset(assetUuid).sync().body();

// Asynchronous
client.loadAsset(assetUuid).async()
    .subscribe(response -> System.out.println("Loaded: " + response.body().getUuid()));

Error Handling

A response other than 2xx throws a LoomClientException carrying the status code and the server’s message:

try {
    client.loadAsset(assetUuid).sync().body();
} catch (LoomClientException e) {
    System.out.println(e.getStatusCode() + ": " + e.getStatusMsg());
}

Closing the Client

The client implements AutoCloseable:

try (LoomHttpClient client = LoomHttpClient.builder().setHostname("localhost").build()) {
    // use client
}

Looking for something else?