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
}