feat: Phase 2 Workstream 4 — Java SDK (ai.sentryagent:idp-sdk)
Java 17 SDK in sdk-java/: - AgentIdPClient composing AgentRegistryClient, CredentialClient, TokenClient, AuditClient — all 14 endpoints covered - Both sync methods and CompletableFuture<T> async counterparts on each client - Thread-safe TokenManager (synchronized) with 60s refresh buffer - AgentIdPException (extends RuntimeException) with Code/HTTPStatus/Details - Builder pattern for all request types; Jackson 2.17 for JSON - Zero external HTTP dependencies — java.net.http.HttpClient (Java 11+) - No-dep JDK HttpServer used for unit tests (no WireMock needed) - mvn verify: 49/49 tests passed | JaCoCo coverage gate: >80% ✓ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
39
sdk-java/src/main/java/ai/sentryagent/idp/models/Agent.java
Normal file
39
sdk-java/src/main/java/ai/sentryagent/idp/models/Agent.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/** A registered AI agent identity. */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class Agent {
|
||||
|
||||
@JsonProperty("agentId") private String agentId;
|
||||
@JsonProperty("email") private String email;
|
||||
@JsonProperty("agentType") private String agentType;
|
||||
@JsonProperty("version") private String version;
|
||||
@JsonProperty("capabilities") private java.util.List<String> capabilities;
|
||||
@JsonProperty("owner") private String owner;
|
||||
@JsonProperty("deploymentEnv") private String deploymentEnv;
|
||||
@JsonProperty("status") private String status;
|
||||
@JsonProperty("createdAt") private String createdAt;
|
||||
@JsonProperty("updatedAt") private String updatedAt;
|
||||
|
||||
/** Required by Jackson. */
|
||||
public Agent() {}
|
||||
|
||||
public String getAgentId() { return agentId; }
|
||||
public String getEmail() { return email; }
|
||||
public String getAgentType() { return agentType; }
|
||||
public String getVersion() { return version; }
|
||||
public java.util.List<String> getCapabilities() { return capabilities; }
|
||||
public String getOwner() { return owner; }
|
||||
public String getDeploymentEnv() { return deploymentEnv; }
|
||||
public String getStatus() { return status; }
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
public String getUpdatedAt() { return updatedAt; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Agent{agentId='" + agentId + "', email='" + email + "', status='" + status + "'}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.Map;
|
||||
|
||||
/** An immutable audit event record. */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class AuditEvent {
|
||||
|
||||
@JsonProperty("eventId") private String eventId;
|
||||
@JsonProperty("agentId") private String agentId;
|
||||
@JsonProperty("action") private String action;
|
||||
@JsonProperty("outcome") private String outcome;
|
||||
@JsonProperty("ipAddress") private String ipAddress;
|
||||
@JsonProperty("userAgent") private String userAgent;
|
||||
@JsonProperty("metadata") private Map<String, Object> metadata;
|
||||
@JsonProperty("timestamp") private String timestamp;
|
||||
|
||||
public AuditEvent() {}
|
||||
|
||||
public String getEventId() { return eventId; }
|
||||
public String getAgentId() { return agentId; }
|
||||
public String getAction() { return action; }
|
||||
public String getOutcome() { return outcome; }
|
||||
public String getIpAddress() { return ipAddress; }
|
||||
public String getUserAgent() { return userAgent; }
|
||||
public Map<String, Object> getMetadata() { return metadata; }
|
||||
public String getTimestamp() { return timestamp; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuditEvent{eventId='" + eventId + "', action='" + action + "', outcome='" + outcome + "'}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/** A credential record (clientSecret is never included). */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class Credential {
|
||||
|
||||
@JsonProperty("credentialId") protected String credentialId;
|
||||
@JsonProperty("clientId") protected String clientId;
|
||||
@JsonProperty("status") protected String status;
|
||||
@JsonProperty("createdAt") protected String createdAt;
|
||||
@JsonProperty("expiresAt") protected String expiresAt;
|
||||
@JsonProperty("revokedAt") protected String revokedAt;
|
||||
|
||||
public Credential() {}
|
||||
|
||||
public String getCredentialId() { return credentialId; }
|
||||
public String getClientId() { return clientId; }
|
||||
public String getStatus() { return status; }
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
public String getExpiresAt() { return expiresAt; }
|
||||
public String getRevokedAt() { return revokedAt; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Credential{credentialId='" + credentialId + "', status='" + status + "'}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* Credential with a one-time plaintext clientSecret.
|
||||
* Returned only on credential creation and rotation.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class CredentialWithSecret extends Credential {
|
||||
|
||||
@JsonProperty("clientSecret") private String clientSecret;
|
||||
|
||||
public CredentialWithSecret() {}
|
||||
|
||||
/** The one-time plaintext secret. Store it securely; it is never shown again. */
|
||||
public String getClientSecret() { return clientSecret; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/** Token introspection response (RFC 7662). */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class IntrospectResponse {
|
||||
|
||||
@JsonProperty("active") private boolean active;
|
||||
@JsonProperty("sub") private String sub;
|
||||
@JsonProperty("client_id") private String clientId;
|
||||
@JsonProperty("scope") private String scope;
|
||||
@JsonProperty("token_type") private String tokenType;
|
||||
@JsonProperty("iat") private Long iat;
|
||||
@JsonProperty("exp") private Long exp;
|
||||
|
||||
public IntrospectResponse() {}
|
||||
|
||||
public boolean isActive() { return active; }
|
||||
public String getSub() { return sub; }
|
||||
public String getClientId() { return clientId; }
|
||||
public String getScope() { return scope; }
|
||||
public String getTokenType() { return tokenType; }
|
||||
public Long getIat() { return iat; }
|
||||
public Long getExp() { return exp; }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
/** Optional query parameters for listing agents. */
|
||||
public final class ListAgentsParams {
|
||||
private final String status;
|
||||
private final String agentType;
|
||||
private final String deploymentEnv;
|
||||
private final Integer page;
|
||||
private final Integer limit;
|
||||
|
||||
private ListAgentsParams(Builder b) {
|
||||
this.status = b.status;
|
||||
this.agentType = b.agentType;
|
||||
this.deploymentEnv = b.deploymentEnv;
|
||||
this.page = b.page;
|
||||
this.limit = b.limit;
|
||||
}
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public String getAgentType() { return agentType; }
|
||||
public String getDeploymentEnv() { return deploymentEnv; }
|
||||
public Integer getPage() { return page; }
|
||||
public Integer getLimit() { return limit; }
|
||||
|
||||
public static Builder builder() { return new Builder(); }
|
||||
|
||||
public static final class Builder {
|
||||
private String status;
|
||||
private String agentType;
|
||||
private String deploymentEnv;
|
||||
private Integer page;
|
||||
private Integer limit;
|
||||
|
||||
public Builder status(String status) { this.status = status; return this; }
|
||||
public Builder agentType(String agentType) { this.agentType = agentType; return this; }
|
||||
public Builder deploymentEnv(String env) { this.deploymentEnv = env; return this; }
|
||||
public Builder page(int page) { this.page = page; return this; }
|
||||
public Builder limit(int limit) { this.limit = limit; return this; }
|
||||
|
||||
public ListAgentsParams build() { return new ListAgentsParams(this); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/** Paginated list of agents. */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class PaginatedAgents {
|
||||
|
||||
@JsonProperty("data") private List<Agent> data;
|
||||
@JsonProperty("total") private int total;
|
||||
@JsonProperty("page") private int page;
|
||||
@JsonProperty("limit") private int limit;
|
||||
|
||||
public PaginatedAgents() {}
|
||||
|
||||
public List<Agent> getData() { return data; }
|
||||
public int getTotal() { return total; }
|
||||
public int getPage() { return page; }
|
||||
public int getLimit() { return limit; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/** Paginated list of audit events. */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class PaginatedAuditEvents {
|
||||
|
||||
@JsonProperty("data") private List<AuditEvent> data;
|
||||
@JsonProperty("total") private int total;
|
||||
@JsonProperty("page") private int page;
|
||||
@JsonProperty("limit") private int limit;
|
||||
|
||||
public PaginatedAuditEvents() {}
|
||||
|
||||
public List<AuditEvent> getData() { return data; }
|
||||
public int getTotal() { return total; }
|
||||
public int getPage() { return page; }
|
||||
public int getLimit() { return limit; }
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/** Paginated list of credentials. */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class PaginatedCredentials {
|
||||
|
||||
@JsonProperty("data") private List<Credential> data;
|
||||
@JsonProperty("total") private int total;
|
||||
@JsonProperty("page") private int page;
|
||||
@JsonProperty("limit") private int limit;
|
||||
|
||||
public PaginatedCredentials() {}
|
||||
|
||||
public List<Credential> getData() { return data; }
|
||||
public int getTotal() { return total; }
|
||||
public int getPage() { return page; }
|
||||
public int getLimit() { return limit; }
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
/** Optional query parameters for querying the audit log. */
|
||||
public final class QueryAuditParams {
|
||||
private final String agentId;
|
||||
private final String action;
|
||||
private final String outcome;
|
||||
private final String fromDate;
|
||||
private final String toDate;
|
||||
private final Integer page;
|
||||
private final Integer limit;
|
||||
|
||||
private QueryAuditParams(Builder b) {
|
||||
this.agentId = b.agentId;
|
||||
this.action = b.action;
|
||||
this.outcome = b.outcome;
|
||||
this.fromDate = b.fromDate;
|
||||
this.toDate = b.toDate;
|
||||
this.page = b.page;
|
||||
this.limit = b.limit;
|
||||
}
|
||||
|
||||
public String getAgentId() { return agentId; }
|
||||
public String getAction() { return action; }
|
||||
public String getOutcome() { return outcome; }
|
||||
public String getFromDate() { return fromDate; }
|
||||
public String getToDate() { return toDate; }
|
||||
public Integer getPage() { return page; }
|
||||
public Integer getLimit() { return limit; }
|
||||
|
||||
public static Builder builder() { return new Builder(); }
|
||||
|
||||
public static final class Builder {
|
||||
private String agentId;
|
||||
private String action;
|
||||
private String outcome;
|
||||
private String fromDate;
|
||||
private String toDate;
|
||||
private Integer page;
|
||||
private Integer limit;
|
||||
|
||||
public Builder agentId(String agentId) { this.agentId = agentId; return this; }
|
||||
public Builder action(String action) { this.action = action; return this; }
|
||||
public Builder outcome(String outcome) { this.outcome = outcome; return this; }
|
||||
public Builder fromDate(String from) { this.fromDate = from; return this; }
|
||||
public Builder toDate(String to) { this.toDate = to; return this; }
|
||||
public Builder page(int page) { this.page = page; return this; }
|
||||
public Builder limit(int limit) { this.limit = limit; return this; }
|
||||
|
||||
public QueryAuditParams build() { return new QueryAuditParams(this); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/** Request body for POST /api/v1/agents. */
|
||||
public final class RegisterAgentRequest {
|
||||
|
||||
@JsonProperty("email") private final String email;
|
||||
@JsonProperty("agentType") private final String agentType;
|
||||
@JsonProperty("version") private final String version;
|
||||
@JsonProperty("capabilities") private final List<String> capabilities;
|
||||
@JsonProperty("owner") private final String owner;
|
||||
@JsonProperty("deploymentEnv") private final String deploymentEnv;
|
||||
|
||||
private RegisterAgentRequest(Builder b) {
|
||||
this.email = b.email;
|
||||
this.agentType = b.agentType;
|
||||
this.version = b.version;
|
||||
this.capabilities = b.capabilities;
|
||||
this.owner = b.owner;
|
||||
this.deploymentEnv = b.deploymentEnv;
|
||||
}
|
||||
|
||||
public String getEmail() { return email; }
|
||||
public String getAgentType() { return agentType; }
|
||||
public String getVersion() { return version; }
|
||||
public List<String> getCapabilities() { return capabilities; }
|
||||
public String getOwner() { return owner; }
|
||||
public String getDeploymentEnv() { return deploymentEnv; }
|
||||
|
||||
public static Builder builder() { return new Builder(); }
|
||||
|
||||
public static final class Builder {
|
||||
private String email;
|
||||
private String agentType;
|
||||
private String version;
|
||||
private List<String> capabilities;
|
||||
private String owner;
|
||||
private String deploymentEnv;
|
||||
|
||||
public Builder email(String email) { this.email = email; return this; }
|
||||
public Builder agentType(String agentType) { this.agentType = agentType; return this; }
|
||||
public Builder version(String version) { this.version = version; return this; }
|
||||
public Builder capabilities(List<String> capabilities) { this.capabilities = capabilities; return this; }
|
||||
public Builder owner(String owner) { this.owner = owner; return this; }
|
||||
public Builder deploymentEnv(String deploymentEnv) { this.deploymentEnv = deploymentEnv; return this; }
|
||||
|
||||
public RegisterAgentRequest build() { return new RegisterAgentRequest(this); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/** OAuth 2.0 access token response (RFC 6749). */
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class TokenResponse {
|
||||
|
||||
@JsonProperty("access_token") private String accessToken;
|
||||
@JsonProperty("token_type") private String tokenType;
|
||||
@JsonProperty("expires_in") private int expiresIn;
|
||||
@JsonProperty("scope") private String scope;
|
||||
|
||||
public TokenResponse() {}
|
||||
|
||||
public String getAccessToken() { return accessToken; }
|
||||
public String getTokenType() { return tokenType; }
|
||||
public int getExpiresIn() { return expiresIn; }
|
||||
public String getScope() { return scope; }
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package ai.sentryagent.idp.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Request body for PATCH /api/v1/agents/:id.
|
||||
* All fields are optional — null fields are omitted from the JSON body.
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public final class UpdateAgentRequest {
|
||||
|
||||
@JsonProperty("agentType") private final String agentType;
|
||||
@JsonProperty("version") private final String version;
|
||||
@JsonProperty("capabilities") private final List<String> capabilities;
|
||||
@JsonProperty("owner") private final String owner;
|
||||
@JsonProperty("deploymentEnv") private final String deploymentEnv;
|
||||
@JsonProperty("status") private final String status;
|
||||
|
||||
private UpdateAgentRequest(Builder b) {
|
||||
this.agentType = b.agentType;
|
||||
this.version = b.version;
|
||||
this.capabilities = b.capabilities;
|
||||
this.owner = b.owner;
|
||||
this.deploymentEnv = b.deploymentEnv;
|
||||
this.status = b.status;
|
||||
}
|
||||
|
||||
public String getAgentType() { return agentType; }
|
||||
public String getVersion() { return version; }
|
||||
public List<String> getCapabilities() { return capabilities; }
|
||||
public String getOwner() { return owner; }
|
||||
public String getDeploymentEnv() { return deploymentEnv; }
|
||||
public String getStatus() { return status; }
|
||||
|
||||
public static Builder builder() { return new Builder(); }
|
||||
|
||||
public static final class Builder {
|
||||
private String agentType;
|
||||
private String version;
|
||||
private List<String> capabilities;
|
||||
private String owner;
|
||||
private String deploymentEnv;
|
||||
private String status;
|
||||
|
||||
public Builder agentType(String agentType) { this.agentType = agentType; return this; }
|
||||
public Builder version(String version) { this.version = version; return this; }
|
||||
public Builder capabilities(List<String> capabilities) { this.capabilities = capabilities; return this; }
|
||||
public Builder owner(String owner) { this.owner = owner; return this; }
|
||||
public Builder deploymentEnv(String deploymentEnv) { this.deploymentEnv = deploymentEnv; return this; }
|
||||
public Builder status(String status) { this.status = status; return this; }
|
||||
|
||||
public UpdateAgentRequest build() { return new UpdateAgentRequest(this); }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user