Skip to content

Commit

Permalink
Adds com.structurizr.api.AdminApiClient as a client for the cloud s…
Browse files Browse the repository at this point in the history
…ervice/on-premises admin APIs.
  • Loading branch information
simonbrowndotje committed Dec 22, 2023
1 parent 8a7da17 commit 1a90ec8
Show file tree
Hide file tree
Showing 9 changed files with 788 additions and 502 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ subprojects { proj ->

description = 'Structurizr'
group = 'com.structurizr'
version = '1.28.1'
version = '1.29.0'

repositories {
mavenCentral()
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.29.0 (unreleased)

- Adds `com.structurizr.api.AdminApiClient` as a client for the cloud service/on-premises admin APIs.

## 1.28.1 (11th December 2023)

- `AbstractWorkspace.clearConfiguration()` creates a new instance rather than nulling it.
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
rootProject.name = 'structurizr'
rootProject.name = 'structurizr-java'

include 'structurizr-client'
include 'structurizr-core'
54 changes: 54 additions & 0 deletions structurizr-client/src/com/structurizr/api/AbstractApiClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.structurizr.api;

import com.structurizr.util.StringUtils;

public abstract class AbstractApiClient {

protected static final String VERSION = Package.getPackage("com.structurizr.api").getImplementationVersion();
protected static final String STRUCTURIZR_FOR_JAVA_AGENT = "structurizr-java/" + VERSION;

protected static final String STRUCTURIZR_CLOUD_SERVICE_API_URL = "https://api.structurizr.com";
protected static final String WORKSPACE_PATH = "/workspace";

protected String url;
protected String agent = STRUCTURIZR_FOR_JAVA_AGENT;

String getUrl() {
return url;
}

protected void setUrl(String url) {
if (url == null || url.trim().length() == 0) {
throw new IllegalArgumentException("The API URL must not be null or empty.");
}

if (url.endsWith("/")) {
this.url = url.substring(0, url.length() - 1);
} else {
this.url = url;
}
}

/**
* Gets the agent string used to identify this client instance.
*
* @return "structurizr-java/{version}", unless overridden
*/
public String getAgent() {
return agent;
}

/**
* Sets the agent string used to identify this client instance.
*
* @param agent the agent string
*/
public void setAgent(String agent) {
if (StringUtils.isNullOrEmpty(agent)) {
throw new IllegalArgumentException("An agent must be provided.");
}

this.agent = agent.trim();
}

}
156 changes: 156 additions & 0 deletions structurizr-client/src/com/structurizr/api/AdminApiClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package com.structurizr.api;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.structurizr.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hc.core5.http.HttpStatus;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;

/**
* A client for the Structurizr Admin API.
*/
public class AdminApiClient extends AbstractApiClient {

private static final Log log = LogFactory.getLog(AdminApiClient.class);

private final String username;
private final String apiKey;

/**
* Creates a new API client with the specified on-premises API URL and key.
*
* @param url the URL of your Structurizr instance
* @param username the username (only required for the Structurizr cloud service)
* @param apiKey the API key of your workspace
*/
public AdminApiClient(String url, String username, String apiKey) {
setUrl(url);

this.username = username;

if (apiKey == null || apiKey.trim().length() == 0) {
throw new IllegalArgumentException("The API key must not be null or empty.");
}

this.apiKey = apiKey;
}

/**
* Gets a list of all workspaces.
*
* @return a List of WorkspaceMetadata objects
* @throws StructurizrClientException if an error occurs
*/
public List<WorkspaceMetadata> getWorkspaces() throws StructurizrClientException {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url + WORKSPACE_PATH))
.header(HttpHeaders.AUTHORIZATION, createAuthorizationHeader())
.header(HttpHeaders.USER_AGENT, agent)
.build();
HttpClient client = HttpClient.newHttpClient();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String json = response.body();

if (response.statusCode() == HttpStatus.SC_OK) {
Workspaces workspaces = objectMapper.readValue(response.body(), Workspaces.class);
return workspaces.getWorkspaces();
} else {
ApiResponse apiResponse = ApiResponse.parse(json);
throw new StructurizrClientException(apiResponse.getMessage());
}
} catch (Exception e) {
log.error(e);
throw new StructurizrClientException(e);
}
}

/**
* Creates a new workspace.
*
* @return a WorkspaceMetadata object representing the new workspace
* @throws StructurizrClientException if an error occurs
*/
public WorkspaceMetadata createWorkspace() throws StructurizrClientException {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url + WORKSPACE_PATH))
.POST(HttpRequest.BodyPublishers.noBody())
.header(HttpHeaders.AUTHORIZATION, createAuthorizationHeader())
.header(HttpHeaders.USER_AGENT, agent)
.build();
HttpClient client = HttpClient.newHttpClient();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String json = response.body();

if (response.statusCode() == HttpStatus.SC_OK) {
return objectMapper.readValue(json, WorkspaceMetadata.class);
} else {
ApiResponse apiResponse = ApiResponse.parse(json);
throw new StructurizrClientException(apiResponse.getMessage());
}
} catch (Exception e) {
log.error(e);
throw new StructurizrClientException(e);
}
}

/**
* Deletes a workspace.
*
* @param workspaceId the ID of the workspace to delete
* @throws StructurizrClientException if an error occurs
*/
public void deleteWorkspace(int workspaceId) throws StructurizrClientException {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url + WORKSPACE_PATH + "/" + workspaceId))
.DELETE()
.header(HttpHeaders.AUTHORIZATION, createAuthorizationHeader())
.header(HttpHeaders.USER_AGENT, agent)
.build();
HttpClient client = HttpClient.newHttpClient();

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
String json = response.body();

if (response.statusCode() == HttpStatus.SC_OK) {
ApiResponse apiResponse = ApiResponse.parse(json);
log.debug(apiResponse.getMessage());
} else {
ApiResponse apiResponse = ApiResponse.parse(json);
throw new StructurizrClientException(apiResponse.getMessage());
}
} catch (Exception e) {
log.error(e);
throw new StructurizrClientException(e);
}
}

private String createAuthorizationHeader() {
if (StringUtils.isNullOrEmpty(username)) {
return apiKey;
} else {
return username + ":" + apiKey;
}
}

}
Loading

0 comments on commit 1a90ec8

Please sign in to comment.