diff --git a/astra-db-java/pom.xml b/astra-db-java/pom.xml index b235a7d3..c1b9e290 100644 --- a/astra-db-java/pom.xml +++ b/astra-db-java/pom.xml @@ -158,6 +158,20 @@ + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + test-jar + + + + diff --git a/astra-db-java/src/main/java/com/datastax/astra/client/admin/AstraDBAdmin.java b/astra-db-java/src/main/java/com/datastax/astra/client/admin/AstraDBAdmin.java index 6c453237..b1cfd30c 100644 --- a/astra-db-java/src/main/java/com/datastax/astra/client/admin/AstraDBAdmin.java +++ b/astra-db-java/src/main/java/com/datastax/astra/client/admin/AstraDBAdmin.java @@ -21,11 +21,15 @@ */ import com.datastax.astra.client.admin.commands.AstraAvailableRegionInfo; +import com.datastax.astra.client.admin.definition.DatabaseDefinition; +import com.datastax.astra.client.admin.definition.PCUGroupDefinition; import com.datastax.astra.client.admin.options.AdminOptions; import com.datastax.astra.client.admin.options.AstraFindAvailableRegionsOptions; +import com.datastax.astra.client.admin.options.CreateDatabaseOptions; import com.datastax.astra.client.core.options.DataAPIClientOptions; import com.datastax.astra.client.databases.definition.DatabaseInfo; import com.datastax.astra.client.databases.DatabaseOptions; +import com.datastax.astra.client.exceptions.AstraDevOpsAPIException; import com.datastax.astra.internal.api.AstraApiEndpoint; import com.datastax.astra.internal.command.LoggingCommandObserver; import com.datastax.astra.internal.utils.Assert; @@ -38,6 +42,8 @@ import com.dtsx.astra.sdk.db.domain.FilterByOrgType; import com.dtsx.astra.sdk.db.domain.RegionType; import com.dtsx.astra.sdk.db.exception.DatabaseNotFoundException; +import com.dtsx.astra.sdk.pcu.PCUGroupsOpsClient; +import com.dtsx.astra.sdk.pcu.domain.PCUGroup; import com.dtsx.astra.sdk.utils.AstraRc; import com.dtsx.astra.sdk.utils.observability.ApiRequestObserver; import com.dtsx.astra.sdk.utils.observability.LoggingRequestObserver; @@ -46,7 +52,6 @@ import java.net.http.HttpClient; import java.time.Duration; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -77,6 +82,9 @@ public class AstraDBAdmin { /** Client for Astra Devops Api. */ final AstraDBOpsClient devopsDbClient; + /** Client for Astra Devops Api PCU. */ + final PCUGroupsOpsClient devopsPcuClient; + /** Options to personalized http client other client options. */ final AdminOptions adminOptions; @@ -110,14 +118,17 @@ public AstraDBAdmin(AdminOptions options) { if (dataAPIClientOptions.getObservers() != null) { Map devopsObservers = new HashMap<>(); if (dataAPIClientOptions.getObservers().containsKey(LoggingCommandObserver.class.getSimpleName())) { - System.out.println("Logging enabled for AstraDBAdmin operations."); devopsObservers.put("logging", new LoggingRequestObserver(AstraDBAdmin.class)); } this.devopsDbClient = new AstraDBOpsClient(options.getToken(), dataAPIClientOptions.getAstraEnvironment(), devopsObservers); + this.devopsPcuClient = new PCUGroupsOpsClient(options.getToken(), + dataAPIClientOptions.getAstraEnvironment(), devopsObservers); } else { this.devopsDbClient = new AstraDBOpsClient(options.getToken(), dataAPIClientOptions.getAstraEnvironment()); + this.devopsPcuClient = new PCUGroupsOpsClient(options.getToken(), + dataAPIClientOptions.getAstraEnvironment()); } // Local Agent for Resume @@ -158,6 +169,85 @@ public List findAvailableRegions(AstraFindAvailableReg .toList(); } + // -------------------- + // -- PCU Support --- + // -------------------- + + /** + * Lists PCU (Processing Capacity Units) groups filtered by cloudProvider provider and region. + * PCU groups manage compute resources for databases across cloudProvider providers and regions. + * + * @param cloudProvider + * cloudProvider provider to filter by (AWS, GCP, AZURE), or null for all providers + * @param region + * cloudProvider region to filter by (e.g., "us-east-1"), or null for all regions + * @return + * list of PCU groups matching the specified filters + */ + public List listPCUGroups(CloudProviderType cloudProvider, String region) { + List pcus = devopsPcuClient.findAll().toList(); + // Filter by cloudProvider provider if specified + if (cloudProvider != null) { + pcus = pcus.stream() + .filter(pcu -> cloudProvider.equals(pcu.getCloudProvider())) + .collect(Collectors.toList()); + } + + // Filter by region if specified + if (region != null && !region.isBlank()) { + pcus = pcus.stream() + .filter(pcu -> region.equals(pcu.getRegion())) + .collect(Collectors.toList()); + } + return pcus + .stream() + .map(PCUGroupDefinition::new) + .collect(Collectors.toList()); + } + + /** + * Lists all PCU (Processing Capacity Units) groups in the organization. + * This is a convenience method that returns all PCU groups without filtering. + * + * @return + * list of all PCU groups + */ + public List listPCUGroups() { + return listPCUGroups(null, null); + } + + /** + * Checks if a PCU group exists by its identifier. + * This is a convenience method that checks existence without filtering by cloud or region. + * + * @param PCUGroupId + * PCU group UUID to check + * @return + * true if the PCU group exists, false otherwise + */ + public boolean PCUGroupExists(UUID PCUGroupId) { + return PCUGroupExists(PCUGroupId, null, null); + } + + /** + * Checks if a PCU group exists by its identifier, optionally filtered by cloudProvider provider and region. + * + * @param PCUGroupId + * PCU group UUID to check + * @param cloudProvider + * cloudProvider provider to filter by (AWS, GCP, AZURE), or null for all providers + * @param region + * cloudProvider region to filter by (e.g., "us-east-1"), or null for all regions + * @return + * true if the PCU group exists and matches the filters, false otherwise + */ + public boolean PCUGroupExists(UUID PCUGroupId, CloudProviderType cloudProvider, String region) { + Assert.notNull(PCUGroupId, "PCUGroupId"); + return listPCUGroups(cloudProvider, region) + .stream() + .anyMatch(pcuGroup -> PCUGroupId.equals(pcuGroup.getId())); + } + // -------------------- // -- Databases --- // -------------------- @@ -298,6 +388,32 @@ public DatabaseAdmin createDatabase(String name, CloudProviderType cloud, String return createDatabase(name, cloud, cloudRegion, true); } + /** + * Create new database with a name on free tier. The database name should not exist in the tenant. + * + * @param name + * unique name for the database + * @param definition + * definition of the database + * @return + * database admin object + */ + public DatabaseAdmin createDatabase(String name, DatabaseDefinition definition, CreateDatabaseOptions options) { + Assert.notNull(definition, "definition"); + Assert.hasLength(name, "name"); + DatabaseCreationRequest req = definition.asRequest(); + req.setName(name); + if (definition.getPCUGroupId() != null) { + validatePCUGroup(definition); + } + UUID newDbId = UUID.fromString(devopsDbClient.create(req)); + log.info("Database {} is starting (id={}): it will take about a minute please wait...", name, newDbId); + if (options != null && options.isWaitForDb()) { + waitForDatabase(devopsDbClient.database(newDbId.toString())); + } + return getDatabaseAdmin(newDbId); + } + /** * Delete a Database if exists from its identifier. * @@ -436,6 +552,47 @@ public AstraDBDatabaseAdmin getDatabaseAdmin(String databaseEndpoint) { return getDatabaseAdmin(AstraApiEndpoint.parse(databaseEndpoint).getDatabaseId()); } + /** + * Validates that the PCU group specified in the database definition exists and is in the correct region. + * This method checks both the existence of the PCU group globally and its availability in the + * specified cloud provider and region. + * + * @param definition + * the database definition containing PCU group ID, cloud provider, and region + * @throws AstraDevOpsAPIException + * if the PCU group does not exist or is not in the expected cloud/region + */ + private void validatePCUGroup(DatabaseDefinition definition) { + // Testing PCUGroup in proper region but swallow error if cannot list the PCU groupsInRegion. + List groupsInRegion = null; + List all = null; + try { + all = listPCUGroups(); + groupsInRegion = listPCUGroups(definition.getCloudProvider(), definition.getRegion()); + } catch (AstraDevOpsAPIException e) { + log.warn("Could not list PCU group - The PCUGroup id will not be tested " + e.getMessage()); + } + if (all != null) { + boolean groupExist = all + .stream() + .anyMatch(pcuGroup -> definition.getPCUGroupId().equals(pcuGroup.getId())); + if (!groupExist) { + throw new AstraDevOpsAPIException("Pcu group " + definition.getPCUGroupId() + " does not exist"); + } + } + if (groupsInRegion != null) { + boolean groupExist = groupsInRegion + .stream() + .anyMatch(pcuGroup -> definition.getPCUGroupId().equals(pcuGroup.getId())); + if (!groupExist) { + throw new AstraDevOpsAPIException("Pcu group " + definition.getPCUGroupId() + + " is not in expected cloud/region : " + + definition.getCloudProvider() + "/" + + definition.getRegion()); + } + } + } + /** * Wait for db to have proper status. * diff --git a/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/DatabaseDefinition.java b/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/DatabaseDefinition.java new file mode 100644 index 00000000..8ccfea59 --- /dev/null +++ b/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/DatabaseDefinition.java @@ -0,0 +1,98 @@ +package com.datastax.astra.client.admin.definition; + +/*- + * #%L + * Data API Java Client + * -- + * Copyright (C) 2024 - 2026 DataStax + * -- + * Licensed under the Apache License, Version 2.0 + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import com.dtsx.astra.sdk.db.domain.DatabaseCreationBuilder; +import com.dtsx.astra.sdk.db.domain.DatabaseCreationRequest; +import com.dtsx.astra.sdk.db.domain.DatabaseCreationType; +import lombok.Builder; +import lombok.Data; + +import java.util.UUID; + +@Data +@Builder +public class DatabaseDefinition { + + /** Default region. **/ + public static final String DEFAULT_REGION = "us-east1"; + + /** Default tier. **/ + public static final String DEFAULT_TIER = "serverless"; + + /** Default cloud. **/ + public static final CloudProviderType DEFAULT_CLOUD = CloudProviderType.GCP; + + /** CloudProvider where the database lives. */ + private CloudProviderType cloudProvider = DEFAULT_CLOUD; + + /** Region. */ + private String region = DEFAULT_REGION; + + /** Database type. */ + private String tier = DEFAULT_TIER; + + /** Name of the database--user friendly identifier. */ + private String name; + + /** Keyspace name in database */ + private String keyspace; + + /** + * CapacityUnits is the amount of space available (horizontal scaling) + * for the database. For free tier the max CU's is 1, and 100 + * for CXX/DXX the max is 12 on startup. + */ + private Integer capacityUnits = 1; + + /** + * Default is null, if vector will be added + */ + private DatabaseCreationType dbType; + + /** + * Identifier to assign a database to a PCU group directly. + */ + private UUID PCUGroupId; + + /** + * Projection as the creation request + * + * @return + * db creation request + */ + public DatabaseCreationRequest asRequest() { + DatabaseCreationBuilder builder = DatabaseCreationRequest.builder(); + builder.capacityUnit(capacityUnits); + builder.name(name); + builder.cloudProvider(cloudProvider); + builder.cloudRegion(region); + builder.tier(tier); + builder.keyspace(keyspace); + builder.withVector(); + builder.dbType(dbType); + if (PCUGroupId != null) { + builder.assignToPCUGroup(PCUGroupId); + } + return builder.build(); + } +} diff --git a/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/PCUGroupDefinition.java b/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/PCUGroupDefinition.java new file mode 100644 index 00000000..fe3d06c7 --- /dev/null +++ b/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/PCUGroupDefinition.java @@ -0,0 +1,161 @@ +package com.datastax.astra.client.admin.definition; + +/*- + * #%L + * Data API Java Client + * -- + * Copyright (C) 2024 - 2026 DataStax + * -- + * Licensed under the Apache License, Version 2.0 + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import com.dtsx.astra.sdk.pcu.domain.PCUGroup; +import com.dtsx.astra.sdk.pcu.domain.PCUType; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.util.UUID; + +@Data +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PCUGroupDefinition { + + /** + * Unique identifier for the PCU group. + */ + private UUID id; + + /** + * Associated PCU Types + */ + private PCUTypeDefinition pcuType; + + @JsonProperty("name") + private String name; + + /** + * Organization identifier. + */ + private String orgId; + + /** + * Human-readable title for the PCU group. + */ + private String title; + + /** + * Description of the PCU group. + */ + private String description; + + /** + * Cloud provider where the PCU group is deployed. + */ + private CloudProviderType cloudProvider; + + /** + * Cloud region where the PCU group is deployed. + */ + private String region; + + /** + * Instance type for the PCU group. + */ + private String instanceType; + + /** + * Provisioning type (e.g., on-demand, reserved). + */ + private String provisionType; + + /** + * Minimum number of PCUs. + */ + private int min; + + /** + * Maximum number of PCUs. + */ + private int max; + + /** + * Number of reserved PCUs. + */ + private int reserved; + + /** + * Timestamp when the PCU group was created. + */ + private Instant createdAt; + + /** + * Timestamp when the PCU group was last updated. + */ + private Instant updatedAt; + + /** + * User identifier who created the PCU group. + * This is an Astra user identifier, not a standard UUID. + */ + private String createdBy; + + /** + * User identifier who last updated the PCU group. + * This is an Astra user identifier, not a standard UUID. + */ + private String updatedBy; + + /** + * Current status of the PCU group. + */ + private String status; + + /** + * Constructor to map from PCUGroup (devops API) to PCUGroupDefinition. + * + * @param devopsPCUGroup the PCUGroup from devops API + */ + public PCUGroupDefinition(PCUGroup devopsPCUGroup) { + if (devopsPCUGroup != null) { + this.id = devopsPCUGroup.getId(); + this.name = devopsPCUGroup.getName(); + this.orgId = devopsPCUGroup.getOrgId(); + this.title = devopsPCUGroup.getTitle(); + this.description = devopsPCUGroup.getDescription(); + this.cloudProvider = devopsPCUGroup.getCloudProvider(); + this.region = devopsPCUGroup.getRegion(); + this.instanceType = devopsPCUGroup.getInstanceType(); + this.provisionType = devopsPCUGroup.getProvisionType(); + this.min = devopsPCUGroup.getMin(); + this.max = devopsPCUGroup.getMax(); + this.reserved = devopsPCUGroup.getReserved(); + this.createdAt = devopsPCUGroup.getCreatedAt(); + this.updatedAt = devopsPCUGroup.getUpdatedAt(); + this.createdBy = devopsPCUGroup.getCreatedBy(); + this.updatedBy = devopsPCUGroup.getUpdatedBy(); + this.status = devopsPCUGroup.getStatus(); + + // Map PCUType to PCUTypeDefinition + if (devopsPCUGroup.getPcuType() != null) { + this.pcuType = new PCUTypeDefinition(devopsPCUGroup.getPcuType()); + } + } + } + +} diff --git a/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/PCUTypeDefinition.java b/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/PCUTypeDefinition.java new file mode 100644 index 00000000..7ca6836e --- /dev/null +++ b/astra-db-java/src/main/java/com/datastax/astra/client/admin/definition/PCUTypeDefinition.java @@ -0,0 +1,63 @@ +package com.datastax.astra.client.admin.definition; + +/*- + * #%L + * Data API Java Client + * -- + * Copyright (C) 2024 - 2026 DataStax + * -- + * Licensed under the Apache License, Version 2.0 + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import com.dtsx.astra.sdk.pcu.domain.PCUType; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PCUTypeDefinition { + String type; + String region; + CloudProviderType cloudProvider; + Map details; + boolean enabled; + + /** + * Constructor to map from PCUType to PCUTypeDefinition. + * + * @param pcuType the PCUType from devops API + */ + public PCUTypeDefinition(PCUType pcuType) { + if (pcuType != null) { + this.type = pcuType.getType(); + this.region = pcuType.getRegion(); + // Map provider string to CloudProviderType enum + if (pcuType.getProvider() != null) { + try { + this.cloudProvider = CloudProviderType.valueOf(pcuType.getProvider().toUpperCase()); + } catch (IllegalArgumentException e) { + // If provider string doesn't match enum, leave cloudProvider as null + this.cloudProvider = null; + } + } + this.details = pcuType.getDetails(); + this.enabled = pcuType.isEnabled(); + } + } +} diff --git a/astra-db-java/src/main/java/com/datastax/astra/client/admin/options/CreateDatabaseOptions.java b/astra-db-java/src/main/java/com/datastax/astra/client/admin/options/CreateDatabaseOptions.java new file mode 100644 index 00000000..31d6d990 --- /dev/null +++ b/astra-db-java/src/main/java/com/datastax/astra/client/admin/options/CreateDatabaseOptions.java @@ -0,0 +1,41 @@ +package com.datastax.astra.client.admin.options; + +/*- + * #%L + * Data API Java Client + * -- + * Copyright (C) 2024 - 2026 DataStax + * -- + * Licensed under the Apache License, Version 2.0 + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import com.datastax.astra.client.core.options.BaseOptions; +import lombok.Setter; +import lombok.experimental.Accessors; + +@Setter +@Accessors(fluent = true, chain = true) +public class CreateDatabaseOptions extends BaseOptions { + + boolean waitForDb = false; + + /** + * Gets waitForDb + * + * @return value of waitForDb + */ + public boolean isWaitForDb() { + return waitForDb; + } +} diff --git a/astra-db-java/src/main/java/com/datastax/astra/client/exceptions/AstraDevOpsAPIException.java b/astra-db-java/src/main/java/com/datastax/astra/client/exceptions/AstraDevOpsAPIException.java new file mode 100644 index 00000000..3a628c1a --- /dev/null +++ b/astra-db-java/src/main/java/com/datastax/astra/client/exceptions/AstraDevOpsAPIException.java @@ -0,0 +1,90 @@ +package com.datastax.astra.client.exceptions; + +/*- + * #%L + * Data API Java Client + * -- + * Copyright (C) 2024 - 2026 DataStax + * -- + * Licensed under the Apache License, Version 2.0 + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import lombok.Getter; + +import java.util.Optional; +import java.util.UUID; + +/** + * Exception thrown when an error occurs during Astra DevOps API operations. + *

+ * This runtime exception is used to signal failures in interactions with the + * Astra DevOps API, such as database creation, deletion, or configuration errors. + * It provides multiple constructors to accommodate different error scenarios, + * including wrapping underlying exceptions and providing custom error messages. + *

+ * + * @see RuntimeException + */ +@Getter +public class AstraDevOpsAPIException extends RuntimeException { + + /** + * Default error message used when no specific message is provided. + * This message indicates an unexpected error occurred during Astra DevOps API operations. + */ + public static final String DEFAULT_ERROR_MESSAGE = "Unexpected error occurred for Astra Devops API"; + + /** + * Constructs a new AstraDevOpsAPIException with the specified detail message and cause. + *

+ * This constructor is useful when you want to provide both a descriptive error message + * and wrap an underlying exception that caused the failure. + *

+ * + * @param message the detail message explaining the reason for the exception + * @param parent the underlying cause of this exception (can be null) + */ + public AstraDevOpsAPIException(String message, Throwable parent) { + super(message, parent); + } + + /** + * Constructs a new AstraDevOpsAPIException with the specified detail message. + *

+ * Use this constructor when you have a specific error message to communicate + * but no underlying exception to wrap. + *

+ * + * @param message the detail message explaining the reason for the exception + */ + public AstraDevOpsAPIException(String message) { + super(message); + } + + /** + * Constructs a new AstraDevOpsAPIException wrapping an underlying throwable. + *

+ * This constructor is useful when you want to propagate an exception from + * a lower layer while converting it to an AstraDevOpsAPIException. The message + * will be derived from the throwable's message. + *

+ * + * @param throwable the underlying cause of this exception + */ + public AstraDevOpsAPIException(Throwable throwable) { + super(throwable); + } + + +} 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 ec7202ab..982b2f54 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,7 +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.pcu.PCUGroupsOpsClient; import com.dtsx.astra.sdk.streaming.AstraStreamingClient; import com.dtsx.astra.sdk.utils.ApiLocator; import com.dtsx.astra.sdk.utils.ApiResponseHttp; @@ -172,12 +172,13 @@ public TokensClient tokens() { // ------------------------------------------------------ /** - * Work with PCU groups. + * Work with PCU groups. With are using 'pcus' matching both devops path, also pcus support types + * https://docs.datastax.com/en/astra-api-docs/_attachments/devops-api/index.html#tag/PCU/operation/pcuGet * * @return * pcu groups client */ - public PcuGroupsClient pcuGroups() { // TODO `pcu()` or `pcuGroups()`? - return new PcuGroupsClient(token, environment); + public PCUGroupsOpsClient pcus() { + return new PCUGroupsOpsClient(token, environment); } } diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationBuilder.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationBuilder.java index 1251c888..7250c00c 100644 --- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationBuilder.java +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationBuilder.java @@ -16,6 +16,8 @@ package com.dtsx.astra.sdk.db.domain; +import java.util.UUID; + /** * Builder for database creation. */ @@ -48,9 +50,15 @@ public class DatabaseCreationBuilder { /** Option to enable the vector preview. */ protected boolean vector = false; + /** Option to enable the vector preview. */ + protected DatabaseCreationType dbType; + /** capacity unit. */ protected int capacityUnits = 1; + /** Identifier to assign a database to a PCU group directly. */ + protected UUID pcuGroupUUID; + /** Default constructor. */ public DatabaseCreationBuilder() {} @@ -132,6 +140,19 @@ public DatabaseCreationBuilder capacityUnit(int unit) { return this; } + /** + * Builder for a PCU group + * + * @param pcuGroupId + * identifier for the PCU group + * @return + * current instance + */ + public DatabaseCreationBuilder assignToPCUGroup(UUID pcuGroupId) { + this.pcuGroupUUID = pcuGroupId; + return this; + } + /** * Enable Vector. * @@ -143,6 +164,17 @@ public DatabaseCreationBuilder withVector() { return this; } + /** + * Builder for a dbType + * + * @return + * database creation request + */ + public DatabaseCreationBuilder dbType(DatabaseCreationType dbType) { + this.dbType = dbType; + return this; + } + /** * Build the immutable beans. * diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationRequest.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationRequest.java index 14d3beba..76ae47ea 100644 --- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationRequest.java +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/db/domain/DatabaseCreationRequest.java @@ -16,6 +16,11 @@ package com.dtsx.astra.sdk.db.domain; +import lombok.AccessLevel; +import lombok.Setter; + +import java.util.UUID; + /** * Database creation request * @@ -58,7 +63,12 @@ public class DatabaseCreationRequest { /** * Default is null, if vector will be added */ - private DatabaseCreationType dbType; + protected DatabaseCreationType dbType; + + /** + * Identifier to assign a database to a PCU group directly. + */ + private UUID pcuGroupUUID; /** * default constructor. @@ -78,7 +88,10 @@ public DatabaseCreationRequest(DatabaseCreationBuilder builder) { this.name = builder.name; this.region = builder.region; this.tier = builder.tier; - if (builder.vector) { + this.pcuGroupUUID = builder.pcuGroupUUID; + if (builder.dbType != null) { + this.dbType = builder.dbType; + } else if (builder.vector) { this.dbType = DatabaseCreationType.vector; } } @@ -103,6 +116,16 @@ public String getName() { return name; } + /** + * Setter name update + * + * @param name + * name update + */ + public void setName(String name) { + this.name = name; + } + /** * Getter accessor for attribute 'keyspace'. * @@ -132,6 +155,16 @@ public CloudProviderType getCloudProvider() { public String getTier() { return tier; } + + /** + * Getter accessor for attribute 'pcuGroupUUID'. + * + * @return + * current value of 'pcuGroupUUID' + */ + public UUID getPcuGroupUUID() { + return pcuGroupUUID; + } /** * Getter accessor for attribute 'capacityUnits'. 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 similarity index 86% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupDatacenterAssociationsClient.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PCUGroupDatacenterAssociationsClient.java index c61a5c11..c25f3a59 100644 --- 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 @@ -1,6 +1,6 @@ package com.dtsx.astra.sdk.pcu; -import com.dtsx.astra.sdk.pcu.domain.PcuGroupDatacenterAssociation; +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; @@ -12,6 +12,7 @@ import lombok.val; import java.util.List; +import java.util.UUID; import java.util.stream.Stream; /** @@ -19,15 +20,15 @@ * Provides operations to associate, dissociate, transfer, and query datacenter associations. */ @Slf4j -public class PcuGroupDatacenterAssociationsClient extends AbstractApiClient { - private static final TypeReference> PCU_GROUP_DB_ASSOCIATIONS = +public class PCUGroupDatacenterAssociationsClient extends AbstractApiClient { + private static final TypeReference> PCU_GROUP_DB_ASSOCIATIONS = new TypeReference<>() {}; /** * PCU group unique identifier. */ @Getter - private final String pcuGroupId; + private final UUID pcuGroupId; /** * Constructor with token and PCU group ID for production environment. @@ -37,7 +38,7 @@ public class PcuGroupDatacenterAssociationsClient extends AbstractApiClient { * @param pcuGroupId * PCU group UUID */ - public PcuGroupDatacenterAssociationsClient(String token, String pcuGroupId) { + public PCUGroupDatacenterAssociationsClient(String token, UUID pcuGroupId) { this(token, AstraEnvironment.PROD, pcuGroupId); } @@ -51,7 +52,7 @@ public PcuGroupDatacenterAssociationsClient(String token, String pcuGroupId) { * @param pcuGroupId * PCU group UUID */ - public PcuGroupDatacenterAssociationsClient(String token, AstraEnvironment env, String pcuGroupId) { + public PCUGroupDatacenterAssociationsClient(String token, AstraEnvironment env, UUID pcuGroupId) { super(token, env); this.pcuGroupId = pcuGroupId; } @@ -91,7 +92,7 @@ public boolean exist(@NonNull String datacenterId) { * @throws PcuGroupDbAssociationNotFound * if the datacenter is not associated with this PCU group */ - public PcuGroupDatacenterAssociation findByDatacenterId(@NonNull String datacenterId) { + public PCUGroupDatacenterAssociation findByDatacenterId(@NonNull String datacenterId) { Assert.isDatacenterID(datacenterId, "datacenter id"); return findAll() @@ -106,7 +107,7 @@ public PcuGroupDatacenterAssociation findByDatacenterId(@NonNull String datacent * @return * stream of datacenter associations */ - public Stream findAll() { + public Stream findAll() { val res = GET(getEndpointPcuAssociations() + "/" + pcuGroupId, getOperationName("findAll")); return unmarshallOrThrow(res, PCU_GROUP_DB_ASSOCIATIONS, "get pcu group db associations").stream(); @@ -120,15 +121,15 @@ public Stream findAll() { * @return * the created datacenter association */ - public PcuGroupDatacenterAssociation associate(@NonNull String datacenterId) { + 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); + return unmarshallOrThrow(res, new TypeReference>() {}, "associate db to pcu group").get(0); } - private record TransferReqBody(String fromPCUGroupUUID, String toPCUGroupUUID, String datacenterUUID) {} + private record TransferReqBody(UUID fromPCUGroupUUID, UUID toPCUGroupUUID, String datacenterUUID) {} /** * Transfers a datacenter association from this PCU group to another PCU group. @@ -140,14 +141,14 @@ private record TransferReqBody(String fromPCUGroupUUID, String toPCUGroupUUID, S * @return * the updated datacenter association */ - public PcuGroupDatacenterAssociation transfer(@NonNull String toPcuGroup, @NonNull String datacenterId) { - Assert.isUUID(toPcuGroup, "target pcu group id"); + public PCUGroupDatacenterAssociation transfer(@NonNull UUID toPcuGroup, @NonNull String datacenterId) { + Assert.notNull(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); + return unmarshallOrThrow(res, new TypeReference>() {}, "transfer db to pcu group").get(0); } /** 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 similarity index 83% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupOpsClient.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PCUGroupOpsClient.java index 5aaa748a..cc98002c 100644 --- 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 @@ -1,8 +1,8 @@ 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.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; @@ -14,18 +14,20 @@ import java.util.List; import java.util.Optional; +import java.util.UUID; /** * Operations client for managing a specific PCU (Processing Capacity Units) Group. * Provides CRUD operations, maintenance actions, and datacenter association management. */ @Slf4j -public class PcuGroupOpsClient extends AbstractApiClient { +public class PCUGroupOpsClient extends AbstractApiClient { + /** * PCU group unique identifier. */ @Getter - private final String pcuGroupId; + private final UUID pcuGroupId; /** * Constructor with token and PCU group ID for production environment. @@ -35,7 +37,7 @@ public class PcuGroupOpsClient extends AbstractApiClient { * @param pcuGroupId * PCU group UUID */ - public PcuGroupOpsClient(String token, String pcuGroupId) { + public PCUGroupOpsClient(String token, UUID pcuGroupId) { this(token, AstraEnvironment.PROD, pcuGroupId); } @@ -49,7 +51,7 @@ public PcuGroupOpsClient(String token, String pcuGroupId) { * @param pcuGroupId * PCU group UUID */ - public PcuGroupOpsClient(String token, AstraEnvironment env, String pcuGroupId) { + public PCUGroupOpsClient(String token, AstraEnvironment env, UUID pcuGroupId) { super(token, env); this.pcuGroupId = pcuGroupId; } @@ -70,7 +72,7 @@ public String getServiceName() { * @return * optional containing the PCU group if found */ - public Optional find() { + public Optional find() { try { return Optional.of(get()); } catch (PcuGroupNotFoundException e) { @@ -86,8 +88,8 @@ public Optional find() { * @throws PcuGroupNotFoundException * if the PCU group does not exist */ - public PcuGroup get() { - return new PcuGroupsClient(token, environment).findById(pcuGroupId).orElseThrow(() -> PcuGroupNotFoundException.forId(pcuGroupId)); + public PCUGroup get() { + return new PCUGroupsOpsClient(token, environment).findById(pcuGroupId).orElseThrow(() -> PcuGroupNotFoundException.forId(pcuGroupId)); } /** @@ -107,7 +109,7 @@ public boolean exist() { * true if the PCU group status is ACTIVE */ public boolean isActive() { - return PcuGroupStatusType.ACTIVE == get().getStatus(); + return PCUGroupStatusType.ACTIVE.name().equals(get().getStatus()); } /** @@ -117,7 +119,7 @@ public boolean isActive() { * true if the PCU group status is CREATED or ACTIVE */ public boolean isCreatedOrActive() { - return PcuGroupStatusType.CREATED == get().getStatus() || isActive(); + return PCUGroupStatusType.CREATED.name().equals(get().getStatus()) || isActive(); } // --------------------------------- @@ -130,7 +132,7 @@ public boolean isCreatedOrActive() { * @param req * PCU group update request with new configuration */ - public void update(PcuGroupUpdateRequest req) { + public void update(PCUGroupUpdateRequest req) { val base = get(); PUT(getEndpointPcus(), JsonUtils.marshall(List.of(req.withDefaultsAndValidations(base))), getOperationName("update")); } @@ -190,8 +192,8 @@ public void delete() { * @return * datacenter associations client */ - public PcuGroupDatacenterAssociationsClient datacenterAssociations() { - return new PcuGroupDatacenterAssociationsClient(token, environment, pcuGroupId); + public PCUGroupDatacenterAssociationsClient datacenterAssociations() { + return new PCUGroupDatacenterAssociationsClient(token, environment, pcuGroupId); } /** 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/PCUGroupsOpsClient.java similarity index 56% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PcuGroupsClient.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/PCUGroupsOpsClient.java index 9ad0f7e7..336f0cbe 100644 --- 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/PCUGroupsOpsClient.java @@ -1,18 +1,24 @@ 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.domain.PCUGroup; +import com.dtsx.astra.sdk.pcu.domain.PCUGroupCreationRequest; +import com.dtsx.astra.sdk.pcu.domain.PCUType; +import com.dtsx.astra.sdk.pcu.domain.PCUTypeLocationFilter; 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.dtsx.astra.sdk.utils.observability.ApiRequestObserver; import com.fasterxml.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; import lombok.val; import java.net.HttpURLConnection; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.stream.Stream; /** @@ -20,38 +26,93 @@ * Provides operations for creating, finding, and managing PCU groups. */ @Slf4j -public class PcuGroupsClient extends AbstractApiClient { - private static final TypeReference> RESPONSE_PCU_GROUPS = +public class PCUGroupsOpsClient extends AbstractApiClient { + + private static final TypeReference> RESPONSE_PCU_GROUPS = new TypeReference<>(){}; /** * Constructor with token for production environment. * - * @param token - * authentication token + * @param token authentication token */ - public PcuGroupsClient(String token) { - super(token, AstraEnvironment.PROD); + public PCUGroupsOpsClient(String token) { + this(token, AstraEnvironment.PROD); } /** - * Constructor with token and environment. + * Constructor with token for different environment + * + * @param token authentication token + * @param environment astra environment + */ + public PCUGroupsOpsClient(String token, AstraEnvironment environment) { + super(token, environment); + } + + /** + * As immutable object use builder to initiate the object. * - * @param token - * authentication token * @param env - * target Astra environment + * define target environment to be used + * @param token + * authenticated token + * @param observers + * list of observers */ - public PcuGroupsClient(String token, AstraEnvironment env) { - super(token, env); + public PCUGroupsOpsClient(String token, AstraEnvironment env, Map observers) { + super(token, env, observers); + HttpClientWrapper.registerObservers(observers); } + // --------------------------------- + // ---- TYPES ---- + // --------------------------------- + + private static final TypeReference> RESPONSE_PCU_TYPES = new TypeReference<>(){}; + /** {@inheritDoc} */ @Override public String getServiceName() { return "pcu.groups"; } + // --------------------------------- + // ---- PCU TYPES ---- + // --------------------------------- + public Stream listPcuTypes() { + return listPcuTypes(null); + } + public Stream listPcuTypes(PCUTypeLocationFilter request) { + String contextPath = "/types"; + boolean first = true; + if (request != null) { + if (Utils.hasLength(request.getProvider())) { + first = false; + contextPath = contextPath + "?provider=" + request.getProvider(); + } + if (Utils.hasLength(request.getRegion())) { + if (!first) { + contextPath = contextPath + "®ion=" + request.getRegion(); + } else { + contextPath = contextPath + "?region=" + request.getRegion(); + } + } + } + + val res = GET(getEndpointPcus() + contextPath, getOperationName("find")); + try { + return JsonUtils.unmarshallType(res.getBody(), RESPONSE_PCU_TYPES).stream(); + } catch (Exception e) { + ApiResponseError responseError = null; + try { + responseError = JsonUtils.unmarshallBean(res.getBody(), ApiResponseError.class); + System.out.println(responseError.toString()); + } catch (Exception ignored) {} + throw e; + } + } + // --------------------------------- // ---- CRUD ---- // --------------------------------- @@ -66,8 +127,10 @@ public String getServiceName() { * @throws IllegalStateException * if creation fails */ - public PcuGroup create(PcuGroupCreationRequest req) { - val res = POST(getEndpointPcus(), JsonUtils.marshall(List.of(req.withDefaultsAndValidations())), getOperationName("create")); + public PCUGroup create(PCUGroupCreationRequest req) { + String payload = JsonUtils.marshall(List.of(req.withDefaultsAndValidations())); + System.out.println(payload); + val res = POST(getEndpointPcus(), payload, 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()); @@ -84,9 +147,10 @@ public PcuGroup create(PcuGroupCreationRequest req) { * @return * optional containing the PCU group if found */ - public Optional findById(String id) { + public Optional findById(UUID id) { try { - return findAllImpl(List.of(id), "id", (_e) -> PcuGroupNotFoundException.forId(id)).findFirst(); + return findAllImpl(Collections.singletonList(id), "id", + (_e) -> PcuGroupNotFoundException.forId(id)).findFirst(); } catch (PcuGroupNotFoundException e) { return Optional.empty(); } @@ -100,7 +164,7 @@ public Optional findById(String id) { * @return * stream of matching PCU groups */ - public Stream findByTitle(String title) { + public Stream findByTitle(String title) { return findAll().filter(pg -> title.equals(pg.getTitle())); // order is important here since pg.title is nullable } @@ -112,7 +176,7 @@ public Stream findByTitle(String title) { * @return * optional containing the first matching PCU group */ - public Optional findFirstByTitle(String title) { + public Optional findFirstByTitle(String title) { return findByTitle(title).findFirst(); } @@ -122,7 +186,7 @@ public Optional findFirstByTitle(String title) { * @return * stream of all PCU groups */ - public Stream findAll() { + public Stream findAll() { return findAll(null); } @@ -136,7 +200,7 @@ public Stream findAll() { * @throws PcuGroupsNotFoundException * if any of the specified groups are not found */ - public Stream findAll(List ids) { + public Stream findAll(List ids) { return findAllImpl(ids, "ids[%d]", (e) -> new PcuGroupsNotFoundException(e.getErrors().get(0).getMessage())); } @@ -144,21 +208,18 @@ protected interface FindAll404Handler { RuntimeException getError(ApiResponseError res); } - private record FindAllReqBody(List pcuGroupUUIDs) {} + private record FindAllReqBody(List pcuGroupUUIDs) {} - protected Stream findAllImpl(List ids, String validationErrorFmtStr, FindAll404Handler on404) { + 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")); + System.out.println(res.getBody()); try { return JsonUtils.unmarshallType(res.getBody(), RESPONSE_PCU_GROUPS).stream(); @@ -196,8 +257,8 @@ protected Stream findAllImpl(List ids, String validationErrorF * @return * operations client for the specified PCU group */ - public PcuGroupOpsClient group(String pcuGroupId) { - return new PcuGroupOpsClient(getToken(), getEnvironment(), pcuGroupId); + public PCUGroupOpsClient group(UUID pcuGroupId) { + return new PCUGroupOpsClient(getToken(), getEnvironment(), pcuGroupId); } /** 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/PCUCapacityWorkloadType.java similarity index 77% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuProvisionType.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUCapacityWorkloadType.java index 87718e8c..5a2af2ad 100644 --- 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/PCUCapacityWorkloadType.java @@ -12,22 +12,19 @@ @Getter @Accessors(fluent = true) @RequiredArgsConstructor -public enum PcuProvisionType { +public enum PCUCapacityWorkloadType { + /** * Shared provisioning - resources are shared across multiple tenants. * More cost-effective but with potential resource contention. */ - SHARED("shared"), + flexible, /** * Dedicated provisioning - resources are exclusively allocated. * Higher cost but guaranteed performance and isolation. */ - DEDICATED("dedicated"); + commited; + - /** - * JSON serialization value for the provision type. - */ - @JsonValue - private final String fieldValue; } 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 00000000..820eb235 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUGroup.java @@ -0,0 +1,438 @@ +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 java.time.Instant; +import java.util.UUID; + +/** + * Represents a PCU (Processing Capacity Units) Group in Astra. + * A PCU group manages compute resources for databases across cloud providers and regions. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class PCUGroup { + + /** + * Unique identifier for the PCU group. + */ + @JsonProperty("uuid") + private UUID id; + + /** + * Associated PCU Types + */ + @JsonProperty("pcuType") + private PCUType pcuType; + + @JsonProperty("name") + private String name; + + /** + * Organization identifier. + */ + private String orgId; + + /** + * Human-readable title for the PCU group. + */ + private String title; + + /** + * Description of the PCU group. + */ + private String description; + + /** + * Cloud provider where the PCU group is deployed. + */ + private CloudProviderType cloudProvider; + + /** + * Cloud region where the PCU group is deployed. + */ + private String region; + + /** + * Instance type for the PCU group. + */ + private String instanceType; + + /** + * Provisioning type (e.g., on-demand, reserved). + */ + private String provisionType; + + /** + * Minimum number of PCUs. + */ + private int min; + + /** + * Maximum number of PCUs. + */ + private int max; + + /** + * Number of reserved PCUs. + */ + private int reserved; + + /** + * Timestamp when the PCU group was created. + */ + private Instant createdAt; + + /** + * Timestamp when the PCU group was last updated. + */ + private Instant updatedAt; + + /** + * User identifier who created the PCU group. + * This is an Astra user identifier, not a standard UUID. + */ + private String createdBy; + + /** + * User identifier who last updated the PCU group. + * This is an Astra user identifier, not a standard UUID. + */ + private String updatedBy; + + /** + * Current status of the PCU group. + */ + private String status; + + /** + * Default constructor. + */ + public PCUGroup() { + } + + /** + * Gets id + * + * @return value of id + */ + public UUID getId() { + return id; + } + + /** + * Set value for id + * + * @param id new value for id + */ + public void setId(UUID id) { + this.id = id; + } + + /** + * Gets pcuType + * + * @return value of pcuType + */ + public PCUType getPcuType() { + return pcuType; + } + + /** + * Set value for pcuType + * + * @param pcuType new value for pcuType + */ + public void setPcuType(PCUType pcuType) { + this.pcuType = pcuType; + } + + /** + * Gets name + * + * @return value of name + */ + public String getName() { + return name; + } + + /** + * Set value for name + * + * @param name new value for name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets orgId + * + * @return value of orgId + */ + public String getOrgId() { + return orgId; + } + + /** + * Set value for orgId + * + * @param orgId new value for orgId + */ + public void setOrgId(String orgId) { + this.orgId = orgId; + } + + /** + * Gets title + * + * @return value of title + */ + public String getTitle() { + return title; + } + + /** + * Set value for title + * + * @param title new value for title + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Gets description + * + * @return value of description + */ + public String getDescription() { + return description; + } + + /** + * Set value for description + * + * @param description new value for description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Gets cloudProvider + * + * @return value of cloudProvider + */ + public CloudProviderType getCloudProvider() { + return cloudProvider; + } + + /** + * Set value for cloudProvider + * + * @param cloudProvider new value for cloudProvider + */ + public void setCloudProvider(CloudProviderType cloudProvider) { + this.cloudProvider = cloudProvider; + } + + /** + * Gets region + * + * @return value of region + */ + public String getRegion() { + return region; + } + + /** + * Set value for region + * + * @param region new value for region + */ + public void setRegion(String region) { + this.region = region; + } + + /** + * Gets instanceType + * + * @return value of instanceType + */ + public String getInstanceType() { + return instanceType; + } + + /** + * Set value for instanceType + * + * @param instanceType new value for instanceType + */ + public void setInstanceType(String instanceType) { + this.instanceType = instanceType; + } + + /** + * Gets provisionType + * + * @return value of provisionType + */ + public String getProvisionType() { + return provisionType; + } + + /** + * Set value for provisionType + * + * @param provisionType new value for provisionType + */ + public void setProvisionType(String provisionType) { + this.provisionType = provisionType; + } + + /** + * Gets min + * + * @return value of min + */ + public int getMin() { + return min; + } + + /** + * Set value for min + * + * @param min new value for min + */ + public void setMin(int min) { + this.min = min; + } + + /** + * Gets max + * + * @return value of max + */ + public int getMax() { + return max; + } + + /** + * Set value for max + * + * @param max new value for max + */ + public void setMax(int max) { + this.max = max; + } + + /** + * Gets reserved + * + * @return value of reserved + */ + public int getReserved() { + return reserved; + } + + /** + * Set value for reserved + * + * @param reserved new value for reserved + */ + public void setReserved(int reserved) { + this.reserved = reserved; + } + + /** + * Gets createdAt + * + * @return value of createdAt + */ + public Instant getCreatedAt() { + return createdAt; + } + + /** + * Set value for createdAt + * + * @param createdAt new value for createdAt + */ + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + /** + * Gets updatedAt + * + * @return value of updatedAt + */ + public Instant getUpdatedAt() { + return updatedAt; + } + + /** + * Set value for updatedAt + * + * @param updatedAt new value for updatedAt + */ + public void setUpdatedAt(Instant updatedAt) { + this.updatedAt = updatedAt; + } + + /** + * Gets createdBy + * + * @return value of createdBy + */ + public String getCreatedBy() { + return createdBy; + } + + /** + * Set value for createdBy + * + * @param createdBy new value for createdBy + */ + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + /** + * Gets updatedBy + * + * @return value of updatedBy + */ + public String getUpdatedBy() { + return updatedBy; + } + + /** + * Set value for updatedBy + * + * @param updatedBy new value for updatedBy + */ + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } + + /** + * Gets status + * + * @return value of status + */ + public String getStatus() { + return status; + } + + /** + * Set value for status + * + * @param status new value for status + */ + public void setStatus(String status) { + this.status = 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 similarity index 61% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreateUpdateRequest.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUGroupCreateUpdateRequest.java index 98ce90f1..e9c48273 100644 --- 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 @@ -1,6 +1,7 @@ package com.dtsx.astra.sdk.pcu.domain; import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -14,7 +15,7 @@ @Setter @SuperBuilder @NoArgsConstructor -public abstract class PcuGroupCreateUpdateRequest { +public abstract class PCUGroupCreateUpdateRequest { /** * Human-readable title for the PCU group. @@ -36,16 +37,45 @@ public abstract class PcuGroupCreateUpdateRequest { */ protected String region; + // -------------------------------------- + // Capacity Details + // -------------------------------------- + + /** + * Committed vs Flexible Workload. + *

+ * Commited: Committed capacity workloads include continuously-provisioned resources, and they can never scale to zero. + * Committed capacity workloads are intended for any database in any environment that requires long-term, continuous + * availability, such as multi-region databases and latency sensitive workloads. + *

+ * Flexible capacity workloads: PCU groups for flexible capacity workloads are billed entirely at the HCU rate, and they + * have the option to manually scale to zero. With flexible capacity workloads, you are billed for continuous HCU usage + * based on the group’s minimum capacity. While flexible capacity workloads don’t require a commitment to reserved capacity, + * they don’t offer cost savings for continuous usage that can be realized at the RCU rate. + */ + protected String workloadType; + + /** + * Provisioning type for the PCU group. + */ + @Setter(AccessLevel.NONE) + protected String provisionType; + + public PCUGroupCreateUpdateRequest setProvisionType(String provisionType) { + this.provisionType = provisionType; + return this; + } + /** * Minimum number of PCUs (must be greater or equals to 1). */ protected Integer min; // Integers so they're nullable - + /** * Maximum number of PCUs (must be greater or equals to min). */ protected Integer max; - + /** * Number of reserved PCUs (must be non-negative and lower or equals to 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 similarity index 68% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupCreationRequest.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUGroupCreationRequest.java index 2b155551..7e5ee9b9 100644 --- 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 @@ -11,16 +11,12 @@ @Getter @Setter @SuperBuilder -public final class PcuGroupCreationRequest extends PcuGroupCreateUpdateRequest { +public final class PCUGroupCreationRequest extends PCUGroupCreateUpdateRequest { + /** * Instance type for the PCU group (e.g., "standard"). */ private String instanceType; - - /** - * Provisioning type for the PCU group. - */ - private PcuProvisionType provisionType; /** * Applies default values and validates the request before creation. @@ -29,20 +25,24 @@ public final class PcuGroupCreationRequest extends PcuGroupCreateUpdateRequest { * @return * this request with defaults applied */ - public PcuGroupCreationRequest withDefaultsAndValidations() { + public PCUGroupCreationRequest withDefaultsAndValidations() { if (this.provisionType == null) { - this.provisionType = PcuProvisionType.SHARED; + this.provisionType = PCUProvisionType.shared.name(); } - // TODO do we really want a default for this? (since pcu instance types are changing) + // De if (this.instanceType == null || this.instanceType.isBlank()) { - this.instanceType = "standard"; + this.instanceType = PCUInstanceType.small.toString(); } if (this.reserved == null) { this.reserved = 0; + this.min = 1; + this.max = 1; } 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 similarity index 93% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupDatacenterAssociation.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUGroupDatacenterAssociation.java index 4e0481d9..cb1d0121 100644 --- 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 @@ -10,7 +10,7 @@ // 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 { +public class PCUGroupDatacenterAssociation { /** * PCU group unique identifier. */ 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 similarity index 96% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupStatusType.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUGroupStatusType.java index 1b0227c8..363deca1 100644 --- 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 @@ -4,7 +4,7 @@ * Enumeration of PCU (Processing Capacity Units) Group status types. * Represents the various lifecycle states of a PCU group. */ -public enum PcuGroupStatusType { +public enum PCUGroupStatusType { /** * PCU group has been created but not yet placed. */ 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 similarity index 83% rename from astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroupUpdateRequest.java rename to astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUGroupUpdateRequest.java index e97dd5c8..f2ecd646 100644 --- 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 @@ -12,7 +12,7 @@ */ @SuperBuilder @NoArgsConstructor -public class PcuGroupUpdateRequest extends PcuGroupCreateUpdateRequest { +public class PCUGroupUpdateRequest extends PCUGroupCreateUpdateRequest { /** * Applies defaults from the existing PCU group and validates the update request. * Fields not specified in the update request will retain their current values from the base PCU group. @@ -23,7 +23,7 @@ public class PcuGroupUpdateRequest extends PcuGroupCreateUpdateRequest { * internal representation with defaults applied and validation performed */ // 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) { + public PCUGroupCreateUpdateRequest withDefaultsAndValidations(PCUGroup base) { InternalRep internalRep = new InternalRep(); internalRep.setTitle(this.title == null ? base.getTitle() : this.title); internalRep.setDescription(this.description == null ? base.getDescription() : this.description); @@ -32,11 +32,10 @@ public PcuGroupCreateUpdateRequest withDefaultsAndValidations(PcuGroup base) { internalRep.setMin(this.min == null ? base.getMin() : this.min); internalRep.setMax(this.max == null ? base.getMax() : this.max); internalRep.setReserved(this.reserved == null ? base.getReserved() : this.reserved); - internalRep.validate(); return internalRep - .setPcuGroupUUID(base.getId()) + .setPcuGroupUUID(base.getId().toString()) .setInstanceType(base.getInstanceType()) .setProvisionType(base.getProvisionType()); } @@ -47,13 +46,17 @@ public PcuGroupCreateUpdateRequest withDefaultsAndValidations(PcuGroup base) { @Setter @Getter @Accessors(chain = true) - static class InternalRep extends PcuGroupUpdateRequest { + static class InternalRep extends PCUGroupUpdateRequest { private String pcuGroupUUID; private String instanceType; - private PcuProvisionType provisionType; InternalRep() { super(); } + + public InternalRep setProvisionType(String provisionType) { + super.setProvisionType(provisionType); + return this; + } } } diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUInstanceType.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUInstanceType.java new file mode 100644 index 00000000..b7dc30b9 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUInstanceType.java @@ -0,0 +1,16 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import lombok.Getter; + +@Getter +public enum PCUInstanceType { + small, + medium, + generalPurpose, + cacheOptimized, + + // Legacy + standard, + storageOptimized; + +} 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 00000000..9ddca76c --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUProvisionType.java @@ -0,0 +1,28 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; + +/** + * Enumeration of PCU (Processing Capacity Units) provisioning types. + * Defines how compute resources are allocated for a PCU group. + */ +@Getter +@Accessors(fluent = true) +@RequiredArgsConstructor +public enum PCUProvisionType { + + /** + * Shared provisioning - resources are shared across multiple tenants. + * More cost-effective but with potential resource contention. + */ + shared, + + /** + * Dedicated provisioning - resources are exclusively allocated. + * Higher cost but guaranteed performance and isolation. + */ + dedicated; +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUType.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUType.java new file mode 100644 index 00000000..ba2d8469 --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUType.java @@ -0,0 +1,18 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +@Data +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PCUType { + String type; + String region; + String provider; + Map details; + boolean enabled; +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeLocationFilter.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeLocationFilter.java new file mode 100644 index 00000000..9e27babb --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeLocationFilter.java @@ -0,0 +1,15 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class PCUTypeLocationFilter { + String provider; + String region; +} diff --git a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeResolver.java b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeResolver.java new file mode 100644 index 00000000..e393c61e --- /dev/null +++ b/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PCUTypeResolver.java @@ -0,0 +1,58 @@ +package com.dtsx.astra.sdk.pcu.domain; + +import java.util.List; + +/** + * Utility class for resolving default PCU types based on availability and configuration. + */ +public class PCUTypeResolver { + + /** + * Resolves the default PCU type based on available types and mini PCU configuration. + * + *

Selection priority when mini PCU is enabled: + * SMALL > MEDIUM > GENERAL_PURPOSE > CACHE_OPTIMIZED + * + *

Selection priority when mini PCU is disabled: + * GENERAL_PURPOSE > CACHE_OPTIMIZED (SMALL and MEDIUM are ignored) + * + * @param availableTypes list of available PCU family types + * @param miniPcuEnabled whether mini PCU is enabled + * @return the selected PCU family, or null if no suitable type is available + */ + public static PCUInstanceType resolveDefaultPcuType(List availableTypes, boolean miniPcuEnabled) { + if (availableTypes == null || availableTypes.isEmpty()) { + return null; + } + + if (miniPcuEnabled) { + // Priority: SMALL > MEDIUM > GENERAL_PURPOSE > CACHE_OPTIMIZED + if (availableTypes.contains(PCUInstanceType.small)) { + return PCUInstanceType.small; + } + if (availableTypes.contains(PCUInstanceType.medium)) { + return PCUInstanceType.medium; + } + if (availableTypes.contains(PCUInstanceType.generalPurpose)) { + return PCUInstanceType.generalPurpose; + } + if (availableTypes.contains(PCUInstanceType.cacheOptimized)) { + return PCUInstanceType.cacheOptimized; + } + } else { + // Priority: GENERAL_PURPOSE > CACHE_OPTIMIZED (ignore SMALL and MEDIUM) + if (availableTypes.contains(PCUInstanceType.generalPurpose)) { + return PCUInstanceType.generalPurpose; + } + if (availableTypes.contains(PCUInstanceType.cacheOptimized)) { + return PCUInstanceType.cacheOptimized; + } + } + + return null; + } + + private PCUTypeResolver() { + // Utility class - prevent instantiation + } +} 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 deleted file mode 100644 index 0e6d47e8..00000000 --- a/astra-sdk-devops/src/main/java/com/dtsx/astra/sdk/pcu/domain/PcuGroup.java +++ /dev/null @@ -1,97 +0,0 @@ -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; - -/** - * Represents a PCU (Processing Capacity Units) Group in Astra. - * A PCU group manages compute resources for databases across cloud providers and regions. - */ -@Data -@NoArgsConstructor -@JsonIgnoreProperties(ignoreUnknown = true) -public class PcuGroup { - /** - * Unique identifier for the PCU group. - */ - @JsonProperty("uuid") - private String id; - - /** - * Organization identifier. - */ - private String orgId; - - /** - * Human-readable title for the PCU group. - */ - private String title; - - /** - * Description of the PCU group. - */ - private String description; - - /** - * Cloud provider where the PCU group is deployed. - */ - private CloudProviderType cloudProvider; - - /** - * Cloud region where the PCU group is deployed. - */ - private String region; - - /** - * Instance type for the PCU group. - */ - private String instanceType; - - /** - * Provisioning type (e.g., on-demand, reserved). - */ - private PcuProvisionType provisionType; - - /** - * Minimum number of PCUs. - */ - private int min; - - /** - * Maximum number of PCUs. - */ - private int max; - - /** - * Number of reserved PCUs. - */ - private int reserved; - - /** - * Timestamp when the PCU group was created. - */ - private String createdAt; - - /** - * Timestamp when the PCU group was last updated. - */ - private String updatedAt; - - /** - * User who created the PCU group. - */ - private String createdBy; - - /** - * User who last updated the PCU group. - */ - private String updatedBy; - - /** - * Current status of the PCU group. - */ - private PcuGroupStatusType status; -} 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 index 8f0fbfb4..ee1833b4 100644 --- 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 @@ -2,6 +2,8 @@ import lombok.Getter; +import java.util.UUID; + /** * Exception thrown when a datacenter association with a PCU (Processing Capacity Units) Group cannot be found. * This occurs when attempting to access or modify an association that doesn't exist. @@ -11,7 +13,7 @@ public class PcuGroupDbAssociationNotFound extends RuntimeException { * PCU group unique identifier. */ @Getter - private final String pcuGroupId; + private final UUID pcuGroupId; /** * Datacenter unique identifier. @@ -27,7 +29,7 @@ public class PcuGroupDbAssociationNotFound extends RuntimeException { * @param datacenterId * the datacenter ID */ - public PcuGroupDbAssociationNotFound(String pcuGroupId, String datacenterId) { + public PcuGroupDbAssociationNotFound(UUID 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 index 920d3e4c..04ccb451 100644 --- 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 @@ -3,6 +3,7 @@ import lombok.Getter; import java.util.Optional; +import java.util.UUID; /** * Exception thrown when a PCU (Processing Capacity Units) Group cannot be found. @@ -20,7 +21,7 @@ public class PcuGroupNotFoundException extends RuntimeException { * Optional ID of the PCU group that was not found. */ @Getter - private final Optional id; + private final Optional id; /** * Private constructor for creating the exception. @@ -30,12 +31,28 @@ public class PcuGroupNotFoundException extends RuntimeException { * @param id * optional ID of the PCU group */ - private PcuGroupNotFoundException(Optional title, Optional id) { - super("PCU group " + title.or(() -> id).map(s -> "'" + s + "' ").orElse("") + "has not been found."); + private PcuGroupNotFoundException(Optional title, Optional id) { + super(buildErrorMessage(title, id)); this.title = title; this.id = id; } + /** + * Build Error Message. + * @param title + * title + * @param id + * PUC group identifier + * @return + * exception + */ + private static String buildErrorMessage(Optional title, Optional id) { + String identifier = title + .map(t -> "'" + t + "'") + .orElseGet(() -> id.map(uuid -> "'" + uuid + "'").orElse("")); + return "PCU group " + identifier + " has not been found."; + } + /** * Creates an exception for a PCU group not found by title. * @@ -56,7 +73,7 @@ public static PcuGroupNotFoundException forTitle(String title) { * @return * exception instance */ - public static PcuGroupNotFoundException forId(String id) { + public static PcuGroupNotFoundException forId(UUID id) { return new PcuGroupNotFoundException(Optional.empty(), Optional.of(id)); } } diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java index 64607136..4dcccfd7 100644 --- a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java +++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/AbstractDevopsApiTest.java @@ -4,6 +4,7 @@ import com.dtsx.astra.sdk.db.AstraDBOpsClient; import com.dtsx.astra.sdk.db.domain.DatabaseCreationRequest; import com.dtsx.astra.sdk.streaming.AstraStreamingClient; +import com.dtsx.astra.sdk.utils.AstraEnvironment; import com.dtsx.astra.sdk.utils.AstraRc; import com.dtsx.astra.sdk.utils.Utils; import org.junit.jupiter.api.Assertions; @@ -33,25 +34,30 @@ public abstract class AbstractDevopsApiTest { */ private static String token; + /** + * Hold reference to token + */ + private static AstraEnvironment env; + /** * Reference to Databases Client. */ - private static AstraDBOpsClient databasesClient; + protected static AstraDBOpsClient databasesClient; /** * Reference to organization client. */ - private static AstraOpsClient apiDevopsClient; + protected static AstraOpsClient apiDevopsClient; /** * Working db. */ - private static DbOpsClient dbClient; + protected static DbOpsClient dbClient; /** * Reference to Databases Client. */ - private static AstraStreamingClient streamingClient; + protected static AstraStreamingClient streamingClient; /** * Access DB client. @@ -61,7 +67,7 @@ public abstract class AbstractDevopsApiTest { */ protected AstraOpsClient getApiDevopsClient() { if (apiDevopsClient == null) { - apiDevopsClient = new AstraOpsClient(getToken()); + apiDevopsClient = new AstraOpsClient(getToken(), getEnvironment()); } return apiDevopsClient; } @@ -74,7 +80,7 @@ protected AstraOpsClient getApiDevopsClient() { */ protected AstraDBOpsClient getDatabasesClient() { if (databasesClient == null) { - databasesClient = new AstraDBOpsClient(getToken()); + databasesClient = new AstraDBOpsClient(getToken(), getEnvironment()); } return databasesClient; } @@ -87,7 +93,7 @@ protected AstraDBOpsClient getDatabasesClient() { */ protected AstraStreamingClient getStreamingClient() { if (streamingClient == null) { - streamingClient = new AstraStreamingClient(getToken()); + streamingClient = new AstraStreamingClient(getToken(), getEnvironment()); } return streamingClient; } @@ -110,6 +116,21 @@ protected String getToken() { return token; } + protected AstraEnvironment getEnvironment() { + String envStr = null; + if (env == null) { + if (AstraRc.isDefaultConfigFileExists()) { + envStr = new AstraRc() + .getSectionKey(AstraRc.ASTRARC_DEFAULT, AstraRc.ASTRA_ENV) + .orElse(null); + env = AstraEnvironment.valueOf(envStr); + } + envStr = Utils.readEnvVariable(AstraRc.ASTRA_ENV).orElse(envStr); + env = AstraEnvironment.valueOf(envStr); + } + return env; + } + /** * Create DB if not exist * diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUGroupClientDevTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUGroupClientDevTest.java new file mode 100644 index 00000000..9e9ddcf1 --- /dev/null +++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUGroupClientDevTest.java @@ -0,0 +1,187 @@ +package com.dtsx.astra.sdk.pcu; + +import com.dtsx.astra.sdk.AbstractDevopsApiTest; +import com.dtsx.astra.sdk.AstraOpsClient; +import com.dtsx.astra.sdk.db.domain.CloudProviderType; +import com.dtsx.astra.sdk.db.domain.DatabaseCreationRequest; +import com.dtsx.astra.sdk.db.domain.DatabaseStatusType; +import com.dtsx.astra.sdk.org.domain.Organization; +import com.dtsx.astra.sdk.pcu.domain.PCUCapacityWorkloadType; +import com.dtsx.astra.sdk.pcu.domain.PCUGroup; +import com.dtsx.astra.sdk.pcu.domain.PCUGroupCreationRequest; +import com.dtsx.astra.sdk.pcu.domain.PCUInstanceType; +import com.dtsx.astra.sdk.pcu.domain.PCUProvisionType; +import com.dtsx.astra.sdk.pcu.domain.PCUType; +import com.dtsx.astra.sdk.pcu.domain.PCUTypeLocationFilter; +import com.dtsx.astra.sdk.utils.AstraEnvironment; +import com.dtsx.astra.sdk.utils.JsonUtils; +import com.dtsx.astra.sdk.utils.TestUtils; +import com.dtsx.astra.sdk.utils.Utils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +// Unpredictable so re;oving fro; automated tests +//@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class PCUGroupClientDevTest extends AbstractDevopsApiTest { + + public static final String DEV_REGION = "us-west-2"; + public static final CloudProviderType DEV_PROVIDER = CloudProviderType.AWS; + + public static final String DEV_TOKEN = Utils + .readEnvVariable("ASTRA_DB_APPLICATION_TOKEN_DEV") + .orElseThrow(() -> new IllegalStateException("Please set env var 'ASTRA_DB_APPLICATION_TOKEN_DEV' with a dev token")); + + protected static PCUGroupsOpsClient PCUGroupsOpsClient; + + @BeforeAll + public static void beforeAll() { + apiDevopsClient = new AstraOpsClient(DEV_TOKEN, AstraEnvironment.DEV); + PCUGroupsOpsClient = apiDevopsClient.pcus(); + } + + @Test + @Order(1) + public void shouldAccessOrganizationInDev() { + Organization org = getApiDevopsClient().getOrganization(); + Assertions.assertNotNull(org); + Assertions.assertNotNull(org.getId()); + Assertions.assertNotNull(org.getName()); + System.out.println("[test-pcu] - You are connected to organization: " + org.getName() + "(" + org.getId() + ")"); + } + + @Test + @Order(2) + public void shouldListPcuGroupsType() { + // Full List + System.out.println("[test-pcu] - Listing PCU Types (no filters)"); + Stream types = PCUGroupsOpsClient.listPcuTypes(); + System.out.println("[test-pcu] - Items " + types.toList().size()); + + // Filtered by DC + PCUTypeLocationFilter location = new PCUTypeLocationFilter(DEV_PROVIDER.getCode().toLowerCase(), DEV_REGION); + System.out.println("[test-pcu] - Listing PCU Types on " + JsonUtils.marshall(location)); + List filtered = getApiDevopsClient().pcus().listPcuTypes(location).toList(); + Assertions.assertNotNull(filtered); + System.out.println("[test-pcu] - Items " + filtered.size()); + System.out.println("[test-pcu] - Items " + JsonUtils.marshall(filtered)); + Assertions.assertTrue(filtered.stream().anyMatch(pcu -> pcu.getType().equals("small"))); + } + + @Test + @Order(2) + public void shouldListPcuGroups() { + List allGroups = PCUGroupsOpsClient.findAll().toList(); + System.out.println("[test-pcu] - Listing pcu groups " + JsonUtils.marshall(allGroups)); + System.out.println("[test-pcu] - Items " + allGroups.size()); + } + + @Test + @Order(3) + public void shouldCreatePcuGroupStandard() { + + UUID pcuGroupStandardId = null; + try { + PCUGroupCreationRequest createPcu = PCUGroupCreationRequest + .builder() + .title("pcu_group_from_java") + .description("my first PCU group") + .instanceType("standard") + .cloudProvider(CloudProviderType.AWS) + .region(DEV_REGION) + .workloadType(PCUCapacityWorkloadType.flexible.name()) + .provisionType(PCUProvisionType.shared.name()) + .reserved(1) + .min(1) + .max(1) + .build(); + + // Creating standard pcu Group + PCUGroup group = PCUGroupsOpsClient.create(createPcu); + Assertions.assertNotNull(group); + pcuGroupStandardId = pcuGroupStandardId = group.getId(); + System.out.println("[test-pcu] - Created PCU group: " + group.getId()); + Assertions.assertTrue(getApiDevopsClient().pcus().findById(group.getId()).isPresent()); + + // Create DB + DatabaseCreationRequest dbCreation = DatabaseCreationRequest + .builder() + .name("vector_db_in_standard_pcu") + .keyspace(SDK_TEST_KEYSPACE) + .cloudProvider(CloudProviderType.AWS) + .cloudRegion(DEV_REGION) + .withVector() + .assignToPCUGroup(pcuGroupStandardId) + .build(); + + String dbId = getApiDevopsClient().db().create(dbCreation); + TestUtils.waitForDbStatus(getDatabasesClient().database(dbId), DatabaseStatusType.ACTIVE, 500); + Assertions.assertTrue(getDatabasesClient().findById(dbId).isPresent()); + Assertions.assertNotNull(getDatabasesClient().database(dbId).get()); + Assertions.assertTrue(getDatabasesClient().findByName("vector_db_in_standard_pcu").findAny().isPresent()); + + } catch (RuntimeException e) { + + } finally { + if (pcuGroupStandardId != null) { + PCUGroupsOpsClient.group(pcuGroupStandardId).delete(); + } + } + } + + @Test + @Order(2) + public void shouldCreateMiniPcu() { + PCUGroupCreationRequest createPcu = PCUGroupCreationRequest + .builder() + .title("java_client_mini_pcu") + .description("java_client_mini_pcu") + .instanceType(PCUInstanceType.small.name()) + .cloudProvider(CloudProviderType.AWS) + .region(DEV_REGION) + .workloadType(PCUCapacityWorkloadType.flexible.name()) + .provisionType(PCUProvisionType.shared.name()) + .reserved(1) + .min(1) + .max(1) + .build(); + PCUGroup group = getApiDevopsClient().pcus().create(createPcu); + System.out.println(group); + } + + @Test + @Order(2) + public void shouldDeletePcuGroup() { + PCUGroupsOpsClient.group(UUID.fromString("0c1f67a8-1fd6-4f52-ae2c-ea1483718a11")).delete(); + } + + @Test + @Order(3) + public void should_create_db_and_assign_pcu() throws InterruptedException { + UUID miniPcuUUID = UUID.fromString("57dde257-86c6-4646-9247-670cd8a4d360"); + DatabaseCreationRequest dbCreation = DatabaseCreationRequest + .builder() + .name("vector_db_in_mini_pcu") + .keyspace(SDK_TEST_KEYSPACE) + .cloudProvider(CloudProviderType.AWS) + .cloudRegion(DEV_REGION) + .withVector() + .assignToPCUGroup(miniPcuUUID) + .build(); + String dbId = getApiDevopsClient().db().create(dbCreation); + Thread.sleep(10000); + Assertions.assertTrue(getDatabasesClient().findById(dbId).isPresent()); + Assertions.assertNotNull(getDatabasesClient().database(dbId).get()); + Assertions.assertTrue(getDatabasesClient().findByName("vector_db_in_mini_pcu").count() > 0); + // When + TestUtils.waitForDbStatus(getDatabasesClient().database(dbId), DatabaseStatusType.ACTIVE, 500); + } + +} diff --git a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUTypeResolverTest.java b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUTypeResolverTest.java new file mode 100644 index 00000000..ab38ecc9 --- /dev/null +++ b/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/PCUTypeResolverTest.java @@ -0,0 +1,132 @@ +package com.dtsx.astra.sdk.pcu; + +import com.dtsx.astra.sdk.pcu.domain.PCUInstanceType; +import com.dtsx.astra.sdk.pcu.domain.PCUTypeResolver; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Test class for PcuTypeResolver. + */ +@DisplayName("PcuTypeResolver Tests") +class PCUTypeResolverTest { + + @Nested + @DisplayName("Mini PCU Enabled") + class MiniPcuEnabled { + + @Test + @DisplayName("Selects SMALL when SMALL is available") + void selectsSmallWhenSmallIsAvailable() { + List availableTypes = Arrays.asList( + PCUInstanceType.small, + PCUInstanceType.medium, + PCUInstanceType.generalPurpose, + PCUInstanceType.cacheOptimized + ); + + assertEquals( + PCUInstanceType.small, + PCUTypeResolver.resolveDefaultPcuType(availableTypes, true) + ); + } + + @Test + @DisplayName("Selects MEDIUM when SMALL is unavailable but MEDIUM exists") + void selectsMediumWhenSmallUnavailableButMediumExists() { + List availableTypes = Arrays.asList( + PCUInstanceType.medium, + PCUInstanceType.generalPurpose + ); + + assertEquals( + PCUInstanceType.medium, + PCUTypeResolver.resolveDefaultPcuType(availableTypes, true) + ); + } + + @Test + @DisplayName("Selects GENERAL_PURPOSE when only GENERAL_PURPOSE and CACHE_OPTIMIZED exist") + void selectsGeneralPurposeWhenOnlyGeneralPurposeAndCacheOptimizedExist() { + List availableTypes = Arrays.asList( + PCUInstanceType.generalPurpose, + PCUInstanceType.cacheOptimized + ); + + assertEquals( + PCUInstanceType.generalPurpose, + PCUTypeResolver.resolveDefaultPcuType(availableTypes, true) + ); + } + + @Test + @DisplayName("Selects CACHE_OPTIMIZED when it is the only available type") + void selectsCacheOptimizedWhenItIsTheOnlyAvailableType() { + List availableTypes = Collections.singletonList( + PCUInstanceType.cacheOptimized + ); + + assertEquals( + PCUInstanceType.cacheOptimized, + PCUTypeResolver.resolveDefaultPcuType(availableTypes, true) + ); + } + + @Test + @DisplayName("Returns null when no types are available") + void returnsNullWhenNoTypesAreAvailable() { + assertNull(PCUTypeResolver.resolveDefaultPcuType(Collections.emptyList(), true)); + } + } + + @Nested + @DisplayName("Mini PCU Disabled") + class MiniPcuDisabled { + + @Test + @DisplayName("Selects GENERAL_PURPOSE when GENERAL_PURPOSE is available") + void selectsGeneralPurposeWhenGeneralPurposeIsAvailable() { + List availableTypes = Arrays.asList( + PCUInstanceType.generalPurpose, + PCUInstanceType.cacheOptimized + ); + + assertEquals( + PCUInstanceType.generalPurpose, + PCUTypeResolver.resolveDefaultPcuType(availableTypes, false) + ); + } + + @Test + @DisplayName("Selects CACHE_OPTIMIZED when it is the only available type") + void selectsCacheOptimizedWhenItIsTheOnlyAvailableType() { + List availableTypes = Collections.singletonList( + PCUInstanceType.cacheOptimized + ); + + assertEquals( + PCUInstanceType.cacheOptimized, + PCUTypeResolver.resolveDefaultPcuType(availableTypes, false) + ); + } + + @Test + @DisplayName("Returns null when only SMALL or MEDIUM are available") + void returnsNullWhenOnlySmallOrMediumAreAvailable() { + List availableTypes = Arrays.asList( + PCUInstanceType.small, + PCUInstanceType.medium + ); + + assertNull(PCUTypeResolver.resolveDefaultPcuType(availableTypes, false)); + } + } +} 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 deleted file mode 100644 index 0d1b53e4..00000000 --- a/astra-sdk-devops/src/test/java/com/dtsx/astra/sdk/pcu/ParkingTest.java +++ /dev/null @@ -1,10 +0,0 @@ -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 { - -} diff --git a/integrations/skills/spring-boot-data-api/README.md b/integrations/skills/spring-boot-data-api/README.md index a290b4a0..04fc6257 100644 --- a/integrations/skills/spring-boot-data-api/README.md +++ b/integrations/skills/spring-boot-data-api/README.md @@ -5,8 +5,8 @@ A comprehensive AI-powered skill for building production-ready Spring Boot appli ## Quick Links - πŸ“š **[Main Skill Document](SKILL.md)** - Complete step-by-step guide -- πŸ“ **[Code Templates](templates/)** - Reusable code templates -- πŸ’‘ **[Basic Example](examples/basic/)** - Minimal working example +- πŸ“ **[Code Templates](assets/templates/)** - Reusable code templates +- πŸ’‘ **[Basic Example](assets/examples/basic/)** - Minimal working example ## What You'll Learn @@ -29,6 +29,26 @@ A comprehensive AI-powered skill for building production-ready Spring Boot appli 30-45 minutes +## Folder Structure + +``` +spring-boot-data-api/ +β”œβ”€β”€ README.md # This file +β”œβ”€β”€ SKILL.md # Main skill guide +β”œβ”€β”€ references/ # Additional reference docs (future) +β”œβ”€β”€ scripts/ # Automation scripts (future) +└── assets/ # Reusable assets + β”œβ”€β”€ templates/ # Code templates + β”‚ β”œβ”€β”€ Document.java.template + β”‚ β”œβ”€β”€ Repository.java.template + β”‚ β”œβ”€β”€ Service.java.template + β”‚ β”œβ”€β”€ Controller.java.template + β”‚ └── application.yml.template + └── examples/ # Working examples + └── basic/ # Basic example + └── README.md +``` + ## How to Use This Skill ### With Claude @@ -70,7 +90,7 @@ integrations/skills/spring-boot-data-api/SKILL.md ``` 2. **Use templates to generate code:** - - Copy templates from `templates/` directory + - Copy templates from `assets/templates/` directory - Replace placeholders ({{CLASS_NAME}}, {{COLLECTION_NAME}}, etc.) - Customize for your use case diff --git a/integrations/skills/spring-boot-data-api/examples/basic/README.md b/integrations/skills/spring-boot-data-api/assets/examples/basic/README.md similarity index 100% rename from integrations/skills/spring-boot-data-api/examples/basic/README.md rename to integrations/skills/spring-boot-data-api/assets/examples/basic/README.md diff --git a/integrations/skills/spring-boot-data-api/templates/Controller.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template similarity index 100% rename from integrations/skills/spring-boot-data-api/templates/Controller.java.template rename to integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template diff --git a/integrations/skills/spring-boot-data-api/templates/Document.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Document.java.template similarity index 100% rename from integrations/skills/spring-boot-data-api/templates/Document.java.template rename to integrations/skills/spring-boot-data-api/assets/templates/Document.java.template diff --git a/integrations/skills/spring-boot-data-api/templates/Repository.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template similarity index 100% rename from integrations/skills/spring-boot-data-api/templates/Repository.java.template rename to integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template diff --git a/integrations/skills/spring-boot-data-api/templates/Service.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Service.java.template similarity index 100% rename from integrations/skills/spring-boot-data-api/templates/Service.java.template rename to integrations/skills/spring-boot-data-api/assets/templates/Service.java.template diff --git a/integrations/skills/spring-boot-data-api/templates/application.yml.template b/integrations/skills/spring-boot-data-api/assets/templates/application.yml.template similarity index 100% rename from integrations/skills/spring-boot-data-api/templates/application.yml.template rename to integrations/skills/spring-boot-data-api/assets/templates/application.yml.template diff --git a/tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md b/tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md new file mode 100644 index 00000000..a6b24de8 --- /dev/null +++ b/tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md @@ -0,0 +1,86 @@ +# CollectionCloner Integration Tests + +This directory contains integration tests for the `CollectionCloner` utility. + +## Test Coverage + +The `CollectionClonerIT` test suite covers: + +1. **Basic Cloning** - Small collection (50 documents) with default settings +2. **Large Collection Cloning** - 2500+ documents with parallel reading (tests estimatedDocumentCount) +3. **Document Transformation** - Using DocumentMapper to transform documents during cloning +4. **Custom Thread Pools** - Testing different read/insert thread pool configurations +5. **Empty Collection** - Handling edge case of empty source collection +6. **Duplicate Prevention** - Verifying no duplicates are created on repeated cloning + +## Running the Tests + +### Prerequisites + +- Local HCD/DSE instance running on `http://localhost:8181` (default) +- OR Astra database with proper credentials configured + +### Run Tests Locally (HCD/DSE) + +```bash +# From project root +mvn test -pl tools/data-api-tools + +# Or from tools/data-api-tools directory +mvn test +``` + +### Run Tests Against Astra + +```bash +# Set environment variables +export ASTRA_DB_APPLICATION_TOKEN= +export ASTRA_DB_API_ENDPOINT= + +# Run tests +mvn test -pl tools/data-api-tools -Dtest.environment=astra_prod +``` + +## Test Configuration + +Tests use configuration from: +- `src/test/resources/test-config.properties` - Default local settings +- Environment variables can override config file settings +- Inherits from `AbstractDataAPITest` in astra-db-java module + +## Performance Expectations + +With default settings (5 read threads, 10 insert threads): +- Small collections (< 100 docs): < 5 seconds +- Medium collections (500 docs): < 15 seconds +- Large collections (2500+ docs): < 60 seconds + +Actual performance depends on: +- Network latency +- Database load +- Document size and complexity +- Available system resources + +## Troubleshooting + +### Test Failures + +1. **Connection refused**: Ensure HCD/DSE is running on localhost:8181 +2. **Timeout errors**: Increase timeout in test configuration +3. **Duplicate key errors**: Expected behavior when cloning to non-empty target + +### Logging + +Adjust log levels in `src/test/resources/logback-test.xml`: +```xml + +``` + +## Adding New Tests + +When adding new test cases: +1. Extend `CollectionClonerIT` class +2. Use `@Order` annotation to control execution sequence +3. Clean up collections in `@BeforeAll` and `@AfterAll` +4. Use descriptive test method names: `should__()` +5. Add assertions to verify expected behavior diff --git a/tools/data-api-tools/src/test/resources/logback-test.xml b/tools/data-api-tools/src/test/resources/logback-test.xml new file mode 100644 index 00000000..5cf5eec7 --- /dev/null +++ b/tools/data-api-tools/src/test/resources/logback-test.xml @@ -0,0 +1,15 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + diff --git a/tools/data-api-tools/src/test/resources/test-config.properties b/tools/data-api-tools/src/test/resources/test-config.properties new file mode 100644 index 00000000..252cf7cf --- /dev/null +++ b/tools/data-api-tools/src/test/resources/test-config.properties @@ -0,0 +1,49 @@ +# Test Configuration for Data API Tools Integration Tests +# This file is used by CollectionClonerIT and other integration tests + +# Default test environment - Astra Production +test.environment=astra_prod + +# ======================================== +# Astra Configuration +# ======================================== +# You can configure Astra connection in two ways: +# +# Option 1: Environment Variables (recommended for CI/CD) +# ASTRA_DB_APPLICATION_TOKEN - Your Astra DB token +# +# Option 2: Properties file (convenient for local development) +# Uncomment and set the property below: +# astra.token=AstraCS:...your-token-here... + +# Astra settings +astra.keyspace=default_keyspace +astra.cloud.provider=AWS +astra.cloud.region=us-east-2 + +# Test settings +test.timeout.seconds=300 +test.log.progress=true +test.vectorize=true +test.reranking=true + +# ======================================== +# Local HCD/DSE Configuration (commented out) +# Uncomment these lines and set test.environment=local to run against local HCD/DSE +# ======================================== +# test.environment=local +# local.endpoint=http://localhost:8181 +# local.keyspace=default_keyspace +# local.username=cassandra +# local.password=cassandra +# test.vectorize=false +# test.reranking=false + +# ======================================== +# Notes: +# ======================================== +# - For Astra tests, the framework automatically creates/finds a database +# based on cloud provider and region settings above +# - You don't need to specify ASTRA_DB_API_ENDPOINT - it's derived from +# the database name, cloud provider, and region +# - Priority: Environment Variables > System Properties > Config File