diff --git a/astra-db-java/pom.xml b/astra-db-java/pom.xml index a69c2a4..c903a4a 100644 --- a/astra-db-java/pom.xml +++ b/astra-db-java/pom.xml @@ -71,6 +71,7 @@ org.junit.platform junit-platform-launcher + ${junit.platform.version} test diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/AstraOpsClient.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/AstraOpsClient.java index 8779c63..ec7202a 100644 --- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/AstraOpsClient.java +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/AstraOpsClient.java @@ -6,6 +6,7 @@ import com.dtsx.astra.sdk.org.TokensClient; import com.dtsx.astra.sdk.org.UsersClient; import com.dtsx.astra.sdk.org.domain.*; +import com.dtsx.astra.sdk.pcu.PcuGroupsClient; import com.dtsx.astra.sdk.streaming.AstraStreamingClient; import com.dtsx.astra.sdk.utils.ApiLocator; import com.dtsx.astra.sdk.utils.ApiResponseHttp; @@ -166,4 +167,17 @@ public TokensClient tokens() { return new TokensClient(token, environment); } + // ------------------------------------------------------ + // WORKING WITH PCU GROUPS + // ------------------------------------------------------ + + /** + * Work with PCU groups. + * + * @return + * pcu groups client + */ + public PcuGroupsClient pcuGroups() { // TODO `pcu()` or `pcuGroups()`? + return new PcuGroupsClient(token, environment); + } } diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/AccessListAddress.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/AccessListAddress.java index 9b35820..6ca4895 100644 --- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/AccessListAddress.java +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/AccessListAddress.java @@ -7,7 +7,7 @@ /** * Nested Address */ -public class AccessListAddress { +public class AccessListAddress { /** Address. */ private String address; diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupDatacenterAssociationsClient.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupDatacenterAssociationsClient.java new file mode 100644 index 0000000..6bc2a41 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupDatacenterAssociationsClient.java @@ -0,0 +1,118 @@ +package com.dtsx.astra.sdk.pcu; + +import com.dtsx.astra.sdk.pcu.domain.PcuGroupDatacenterAssociation; +import com.dtsx.astra.sdk.pcu.exception.PcuGroupDbAssociationNotFound; +import com.dtsx.astra.sdk.pcu.exception.PcuGroupNotFoundException; +import com.dtsx.astra.sdk.AbstractApiClient; +import com.dtsx.astra.sdk.utils.*; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.Getter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import lombok.val; + +import java.util.List; +import java.util.stream.Stream; + +@Slf4j +public class PcuGroupDatacenterAssociationsClient extends AbstractApiClient { + private static final TypeReference> PCU_GROUP_DB_ASSOCIATIONS = + new TypeReference<>() {}; + + @Getter + private final String pcuGroupId; + + public PcuGroupDatacenterAssociationsClient(String token, String pcuGroupId) { + this(token, AstraEnvironment.PROD, pcuGroupId); + } + + public PcuGroupDatacenterAssociationsClient(String token, AstraEnvironment env, String pcuGroupId) { + super(token, env); + this.pcuGroupId = pcuGroupId; + } + + @Override + public String getServiceName() { + return "pcu.group.associations.datacenter"; + } + + // --------------------------------- + // ---- CRUD ---- + // --------------------------------- + + public boolean exist(@NonNull String datacenterId) { + Assert.isDatacenterID(datacenterId, "datacenter id"); + + return findAll() + .anyMatch((assoc) -> assoc.getDatacenterUUID().equals(datacenterId)); + } + + public PcuGroupDatacenterAssociation findByDatacenterId(@NonNull String datacenterId) { + Assert.isDatacenterID(datacenterId, "datacenter id"); + + return findAll() + .filter((assoc) -> assoc.getDatacenterUUID().equals(datacenterId)) + .findFirst() + .orElseThrow(() -> new PcuGroupDbAssociationNotFound(pcuGroupId, datacenterId)); + } + + public Stream findAll() { + val res = GET(getEndpointPcuAssociations() + "/" + pcuGroupId, getOperationName("findAll")); + + return unmarshallOrThrow(res, PCU_GROUP_DB_ASSOCIATIONS, "get pcu group db associations").stream(); + } + + public PcuGroupDatacenterAssociation associate(@NonNull String datacenterId) { + Assert.isDatacenterID(datacenterId, "datacenter id"); + + val res = POST(getEndpointPcuAssociations() + "/" + pcuGroupId + "/" + datacenterId, getOperationName("associate")); + + return unmarshallOrThrow(res, new TypeReference>() {}, "associate db to pcu group").get(0); + } + + private record TransferReqBody(String fromPCUGroupUUID, String toPCUGroupUUID, String datacenterUUID) {} + + public PcuGroupDatacenterAssociation transfer(@NonNull String toPcuGroup, @NonNull String datacenterId) { + Assert.isUUID(toPcuGroup, "target pcu group id"); + Assert.isDatacenterID(datacenterId, "datacenter id"); + + val reqBody = JsonUtils.marshall(new TransferReqBody(this.pcuGroupId, toPcuGroup, datacenterId)); + val res = POST(getEndpointPcuAssociations() + "/transfer/" + pcuGroupId, reqBody, getOperationName("transfer")); + + return unmarshallOrThrow(res, new TypeReference>() {}, "transfer db to pcu group").get(0); + } + + public void dissociate(@NonNull String datacenterId) { + Assert.isDatacenterID(datacenterId, "datacenter id"); + DELETE(getEndpointPcuAssociations() + "/" + pcuGroupId + "/" + datacenterId, getOperationName("dissociate")); + } + + // --------------------------------- + // ---- Utilities ---- + // --------------------------------- + + public String getEndpointPcuAssociations() { + return ApiLocator.getApiDevopsEndpoint(environment) + "/pcus/association"; + } + + private T unmarshallOrThrow(ApiResponseHttp res, TypeReference clazz, String operation) { + try { + System.out.println(res.getBody()); + return JsonUtils.unmarshallType(res.getBody(), clazz); + } catch (Exception e) { + ApiResponseError responseError = null; + + try { + responseError = JsonUtils.unmarshallBean(res.getBody(), ApiResponseError.class); + } catch (Exception ignored) {} + + if (responseError != null && responseError.getErrors() != null && !responseError.getErrors().isEmpty()) { + if (responseError.getErrors().get(0).getId() == 2000367) { + throw PcuGroupNotFoundException.forId(pcuGroupId); + } + } + + throw new IllegalStateException("Expected code 2xx to " + operation + " but got " + res.getCode() + "body=" + res.getBody()); + } + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupOpsClient.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupOpsClient.java new file mode 100644 index 0000000..4b262e4 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupOpsClient.java @@ -0,0 +1,112 @@ +package com.dtsx.astra.sdk.pcu; + +import com.dtsx.astra.sdk.pcu.domain.PcuGroup; +import com.dtsx.astra.sdk.pcu.domain.PcuGroupStatusType; +import com.dtsx.astra.sdk.pcu.domain.PcuGroupUpdateRequest; +import com.dtsx.astra.sdk.pcu.exception.PcuGroupNotFoundException; +import com.dtsx.astra.sdk.AbstractApiClient; +import com.dtsx.astra.sdk.utils.ApiLocator; +import com.dtsx.astra.sdk.utils.AstraEnvironment; +import com.dtsx.astra.sdk.utils.JsonUtils; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import lombok.val; + +import java.util.List; +import java.util.Optional; + +@Slf4j +public class PcuGroupOpsClient extends AbstractApiClient { + @Getter + private final String pcuGroupId; + + public PcuGroupOpsClient(String token, String pcuGroupId) { + this(token, AstraEnvironment.PROD, pcuGroupId); + } + + public PcuGroupOpsClient(String token, AstraEnvironment env, String pcuGroupId) { + super(token, env); + this.pcuGroupId = pcuGroupId; + } + + @Override + public String getServiceName() { + return "pcu.group"; + } + + // --------------------------------- + // ---- READ ---- + // --------------------------------- + + public Optional find() { + try { + return Optional.of(get()); + } catch (PcuGroupNotFoundException e) { + return Optional.empty(); + } + } + + public PcuGroup get() { + return new PcuGroupsClient(token, environment).findById(pcuGroupId).orElseThrow(() -> PcuGroupNotFoundException.forId(pcuGroupId)); + } + + public boolean exist() { + return find().isPresent(); + } + + public boolean isActive() { + return PcuGroupStatusType.ACTIVE == get().getStatus(); + } + + public boolean isCreatedOrActive() { + return PcuGroupStatusType.CREATED == get().getStatus() || isActive(); + } + + // --------------------------------- + // ---- UPDATE ---- + // --------------------------------- + + public void update(PcuGroupUpdateRequest req) { + val base = get(); + PUT(getEndpointPcus(), JsonUtils.marshall(List.of(req.withDefaultsAndValidations(base))), getOperationName("update")); + } + + // --------------------------------- + // ---- MAINTENANCE ---- + // --------------------------------- + + public void park() { + val res = POST(getEndpointPcus() + "/park/" + pcuGroupId, getOperationName("park")); + + if (res.getCode() >= 300) { + throw new IllegalStateException("Expected code 200 to park pcu group but got " + res.getCode() + "body=" + res.getBody()); + } + } + + public void unpark() { + val res = POST(getEndpointPcus() + "/unpark/" + pcuGroupId, getOperationName("unpark")); + + if (res.getCode() >= 300) { + throw new IllegalStateException("Expected code 200 to unpark pcu group but got " + res.getCode() + "body=" + res.getBody()); + } + } + + public void delete() { + if (!exist()) { + throw PcuGroupNotFoundException.forId(pcuGroupId); + } + DELETE(getEndpointPcus() + "/" + pcuGroupId, getOperationName("delete")); + } + + // --------------------------------- + // ---- Utilities ---- + // --------------------------------- + + public PcuGroupDatacenterAssociationsClient datacenterAssociations() { + return new PcuGroupDatacenterAssociationsClient(token, environment, pcuGroupId); + } + + public String getEndpointPcus() { + return ApiLocator.getApiDevopsEndpoint(environment) + "/pcus"; + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupsClient.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupsClient.java new file mode 100644 index 0000000..f332380 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupsClient.java @@ -0,0 +1,129 @@ +package com.dtsx.astra.sdk.pcu; + +import com.dtsx.astra.sdk.pcu.domain.PcuGroup; +import com.dtsx.astra.sdk.pcu.domain.PcuGroupCreationRequest; +import com.dtsx.astra.sdk.pcu.exception.PcuGroupNotFoundException; +import com.dtsx.astra.sdk.pcu.exception.PcuGroupsNotFoundException; +import com.dtsx.astra.sdk.AbstractApiClient; +import com.dtsx.astra.sdk.utils.*; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import lombok.val; + +import java.net.HttpURLConnection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +@Slf4j +public class PcuGroupsClient extends AbstractApiClient { + private static final TypeReference> RESPONSE_PCU_GROUPS = + new TypeReference<>(){}; + + public PcuGroupsClient(String token) { + super(token, AstraEnvironment.PROD); + } + + public PcuGroupsClient(String token, AstraEnvironment env) { + super(token, env); + } + + @Override + public String getServiceName() { + return "pcu.groups"; + } + + // --------------------------------- + // ---- CRUD ---- + // --------------------------------- + + public PcuGroup create(PcuGroupCreationRequest req) { + val res = POST(getEndpointPcus(), JsonUtils.marshall(List.of(req.withDefaultsAndValidations())), getOperationName("create")); + + if (HttpURLConnection.HTTP_CREATED != res.getCode()) { + throw new IllegalStateException("Expected code 201 to create pcu group but got " + res.getCode() + "body=" + res.getBody()); + } + + return JsonUtils.unmarshallType(res.getBody(), RESPONSE_PCU_GROUPS).get(0); + } + + public Optional findById(String id) { + try { + return findAllImpl(List.of(id), "id", (_e) -> PcuGroupNotFoundException.forId(id)).findFirst(); + } catch (PcuGroupNotFoundException e) { + return Optional.empty(); + } + } + + public Stream findByTitle(String title) { + return findAll().filter(pg -> title.equals(pg.getTitle())); // order is important here since pg.title is nullable + } + + public Optional findFirstByTitle(String title) { + return findByTitle(title).findFirst(); + } + + public Stream findAll() { + return findAll(null); + } + + public Stream findAll(List ids) { + return findAllImpl(ids, "ids[%d]", (e) -> new PcuGroupsNotFoundException(e.getErrors().get(0).getMessage())); + } + + protected interface FindAll404Handler { + RuntimeException getError(ApiResponseError res); + } + + private record FindAllReqBody(List pcuGroupUUIDs) {} + + protected Stream findAllImpl(List ids, String validationErrorFmtStr, FindAll404Handler on404) { + if (ids != null) { + if (ids.isEmpty()) { + return Stream.of(); // TODO throw error or just return empty list or return all pcu groups? (devops api does the third) + } + + for (var i = 0; i < ids.size(); i++) { + Assert.isUUID(ids.get(i), validationErrorFmtStr.formatted(i)); + } + } + + val reqBody = JsonUtils.marshall(new FindAllReqBody(ids)); + val res = POST(getEndpointPcus() + "/actions/get", reqBody, getOperationName("find")); + + try { + return JsonUtils.unmarshallType(res.getBody(), RESPONSE_PCU_GROUPS).stream(); + } catch(Exception e) { + ApiResponseError responseError = null; + + try { + responseError = JsonUtils.unmarshallBean(res.getBody(), ApiResponseError.class); + } catch (Exception ignored) {} + + + if (responseError != null && res.getCode() == HttpURLConnection.HTTP_NOT_FOUND) { + throw on404.getError(responseError); + } + + if (responseError != null && responseError.getErrors() != null && !responseError.getErrors().isEmpty()) { + if (responseError.getErrors().get(0).getId() == 340018) { // TODO is this the right error code? also why does find all get special treatment for auth errors? + throw new IllegalArgumentException("You have provided an invalid token, please check", e); + } + } + + throw e; + } + } + + // --------------------------------- + // ---- Utilities ---- + // --------------------------------- + + public PcuGroupOpsClient group(String pcuGroupId) { + return new PcuGroupOpsClient(getToken(), getEnvironment(), pcuGroupId); + } + + public String getEndpointPcus() { + return ApiLocator.getApiDevopsEndpoint(environment) + "/pcus"; + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java new file mode 100644 index 0000000..66c848d --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java @@ -0,0 +1,36 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PcuGroup { + @JsonProperty("uuid") + private String id; + private String orgId; + + private String title; + private String description; + + private CloudProviderType cloudProvider; + private String region; + + private String instanceType; + private PcuProvisionType provisionType; + + private int min; + private int max; + private int reserved; + + private String createdAt; + private String updatedAt; + private String createdBy; + private String updatedBy; + + private PcuGroupStatusType status; +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreateUpdateRequest.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreateUpdateRequest.java new file mode 100644 index 0000000..c5d742a --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreateUpdateRequest.java @@ -0,0 +1,47 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@Getter +@Setter +@SuperBuilder +public sealed abstract class PcuGroupCreateUpdateRequest permits PcuGroupCreationRequest, PcuGroupUpdateRequest { + protected String title; + protected String description; + + protected CloudProviderType cloudProvider; + protected String region; + + protected Integer min; // Integers so they're nullable + protected Integer max; + protected Integer reserved; + + protected void validate() { + if (title == null || title.isBlank()) { + throw new IllegalArgumentException("PCU group title is required"); + } + + if (cloudProvider == null) { + throw new IllegalArgumentException("PCU group cloud provider is required"); + } + + if (region == null || region.isBlank()) { + throw new IllegalArgumentException("PCU group region is required"); + } + + if (min == null || min < 1) { + throw new IllegalArgumentException("PCU group min must be >= 1"); + } + + if (max == null || max < min) { + throw new IllegalArgumentException("PCU group max must be >= min"); + } + + if (reserved != null && (reserved < 0 || reserved > min)) { + throw new IllegalArgumentException("PCU group reserved must be non-negative and <= min"); + } + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreationRequest.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreationRequest.java new file mode 100644 index 0000000..5a50bf6 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreationRequest.java @@ -0,0 +1,30 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.SuperBuilder; + +@Getter +@Setter +@SuperBuilder +public final class PcuGroupCreationRequest extends PcuGroupCreateUpdateRequest { + private String instanceType; + private PcuProvisionType provisionType; + + public PcuGroupCreationRequest withDefaultsAndValidations() { + if (this.provisionType == null) { + this.provisionType = PcuProvisionType.SHARED; + } + + // TODO do we really want a default for this? (since pcu instance types are changing) + if (this.instanceType == null || this.instanceType.isBlank()) { + this.instanceType = "standard"; + } + + if (this.reserved == null) { + this.reserved = 0; + } + + return this; + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupDatacenterAssociation.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupDatacenterAssociation.java new file mode 100644 index 0000000..48574f5 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupDatacenterAssociation.java @@ -0,0 +1,12 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +// TODO add the rest of the fields once the PCU team is clear about what is going on +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class PcuGroupDatacenterAssociation { + private String pcuGroupUUID; + private String datacenterUUID; +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupStatusType.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupStatusType.java new file mode 100644 index 0000000..45b4d64 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupStatusType.java @@ -0,0 +1,12 @@ +package com.dtsx.astra.sdk.pcu.domain; + +public enum PcuGroupStatusType { + CREATED, + PLACING, + INITIALIZING, + ACTIVE, + PARKED, + PARKING, + UNPARKING, + OTHER // TODO make this work with Jackson +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupUpdateRequest.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupUpdateRequest.java new file mode 100644 index 0000000..02b6d42 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupUpdateRequest.java @@ -0,0 +1,44 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; +import lombok.val; + +@SuperBuilder +public non-sealed class PcuGroupUpdateRequest extends PcuGroupCreateUpdateRequest { + // TODO once the bug that causes fields to potentially be lost during partial updates is fixed, we can remove the base parameter here + public PcuGroupCreateUpdateRequest withDefaultsAndValidations(PcuGroup base) { + val internalRep = new InternalRep( + builder() + .title(this.title == null ? base.getTitle() : this.title) + .description(this.description == null ? base.getDescription() : this.description) + .cloudProvider(this.cloudProvider == null ? base.getCloudProvider() : this.cloudProvider) + .region(this.region == null ? base.getRegion() : this.region) + .min(this.min == null ? base.getMin() : this.min) + .max(this.max == null ? base.getMax() : this.max) + .reserved(this.reserved == null ? base.getReserved() : this.reserved) + ); + + internalRep.validate(); + + return internalRep + .setPcuGroupUUID(base.getId()) + .setInstanceType(base.getInstanceType()) + .setProvisionType(base.getProvisionType()); + } + + @Setter + @Getter + @Accessors(chain = true) + public static class InternalRep extends PcuGroupUpdateRequest { + private String pcuGroupUUID; + private String instanceType; + private PcuProvisionType provisionType; + + protected InternalRep(PcuGroupUpdateRequestBuilder b) { + super(b); + } + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuProvisionType.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuProvisionType.java new file mode 100644 index 0000000..ee4fe1f --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuProvisionType.java @@ -0,0 +1,17 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; + +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +public enum PcuProvisionType { + SHARED("shared"), + DEDICATED("dedicated"); + + @JsonValue + private final String fieldValue; +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java new file mode 100644 index 0000000..a830e90 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupDbAssociationNotFound.java @@ -0,0 +1,17 @@ +package com.dtsx.astra.sdk.pcu.exception; + +import lombok.Getter; + +public class PcuGroupDbAssociationNotFound extends RuntimeException { + @Getter + private final String pcuGroupId; + + @Getter + private final String datacenterId; + + public PcuGroupDbAssociationNotFound(String pcuGroupId, String datacenterId) { + super("Association not found for pcu group '" + pcuGroupId + "' and datacenter '" + datacenterId + "'"); + this.pcuGroupId = pcuGroupId; + this.datacenterId = datacenterId; + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java new file mode 100644 index 0000000..4df7f4e --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupNotFoundException.java @@ -0,0 +1,28 @@ +package com.dtsx.astra.sdk.pcu.exception; + +import lombok.Getter; + +import java.util.Optional; + +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") // what a stupid rule ~ sincerely, a haskeller +public class PcuGroupNotFoundException extends RuntimeException { + @Getter + private final Optional title; + + @Getter + private final Optional id; + + private PcuGroupNotFoundException(Optional title, Optional id) { + super("PCU group " + title.or(() -> id).map(s -> "'" + s + "' ").orElse("") + "has not been found."); + this.title = title; + this.id = id; + } + + public static PcuGroupNotFoundException forTitle(String title) { + return new PcuGroupNotFoundException(Optional.of(title), Optional.empty()); + } + + public static PcuGroupNotFoundException forId(String id) { + return new PcuGroupNotFoundException(Optional.empty(), Optional.of(id)); + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupsNotFoundException.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupsNotFoundException.java new file mode 100644 index 0000000..c079399 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/exception/PcuGroupsNotFoundException.java @@ -0,0 +1,7 @@ +package com.dtsx.astra.sdk.pcu.exception; + +public class PcuGroupsNotFoundException extends RuntimeException { + public PcuGroupsNotFoundException(String message) { + super(message); // unfortunately the devops api doesn't return a very workable error so will just use the message directly + } +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/utils/Assert.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/utils/Assert.java index 7e2158a..02db862 100644 --- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/utils/Assert.java +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/utils/Assert.java @@ -16,6 +16,8 @@ package com.dtsx.astra.sdk.utils; +import java.util.regex.Pattern; + /** * Syntaxic sugar for common validations. * @@ -69,4 +71,30 @@ public static void isTrue(Boolean b, String msg) { } } -} \ No newline at end of file + private static final String UUID_PATTERN_STR = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"; + + private static final Pattern UUID_PATTERN = Pattern.compile("^" + UUID_PATTERN_STR + "$"); + private static final Pattern DATACENTER_ID_PATTERN = Pattern.compile("^" + UUID_PATTERN_STR + "-\\d+$"); + + public static void isUUID(String id, String name) { + hasLength(id, name); + + if (!UUID_PATTERN.matcher(id).matches()) { + throw new IllegalArgumentException("Parameter '" + name + "' should be a valid UUID"); + } + } + + public static void isDatacenterID(String id, String name) { + hasLength(id, name); + + if (!DATACENTER_ID_PATTERN.matcher(id).matches()) { + var addendum = ""; + + if (UUID_PATTERN.matcher(id).matches()) { + addendum = " (missing '-' suffix; did you accidentally pass a database id instead?)"; + } + + throw new IllegalArgumentException("Parameter '" + name + "' should be a valid datacenter id of format -" + addendum); + } + } +} diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java new file mode 100644 index 0000000..0d1b53e --- /dev/null +++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java @@ -0,0 +1,10 @@ +package com.dtsx.astra.sdk.pcu; + +import com.dtsx.astra.sdk.AstraOpsClient; +import com.dtsx.astra.sdk.utils.AstraEnvironment; +import lombok.val; +import org.junit.jupiter.api.Test; + +public class ParkingTest { + +}