From 1fe6ac62a0a43cf3f5f596ac7a2a5f338c7fa6f8 Mon Sep 17 00:00:00 2001 From: Cedrick Lunven Date: Mon, 29 Jun 2026 14:44:00 +0200 Subject: [PATCH] feat: add Spring Boot Data API skill templates and tools test resources --- .../skills/spring-boot-data-api/README.md | 26 ++++- .../assets/examples/basic/README.md | 71 ++++++++++++++ .../assets/templates/Controller.java.template | 94 +++++++++++++++++++ .../assets/templates/Document.java.template | 60 ++++++++++++ .../assets/templates/Repository.java.template | 25 +++++ .../assets/templates/Service.java.template | 93 ++++++++++++++++++ .../assets/templates/application.yml.template | 63 +++++++++++++ .../com/datastax/astra/tool/copy/README.md | 86 +++++++++++++++++ .../src/test/resources/logback-test.xml | 15 +++ .../src/test/resources/test-config.properties | 49 ++++++++++ 10 files changed, 579 insertions(+), 3 deletions(-) create mode 100644 integrations/skills/spring-boot-data-api/assets/examples/basic/README.md create mode 100644 integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template create mode 100644 integrations/skills/spring-boot-data-api/assets/templates/Document.java.template create mode 100644 integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template create mode 100644 integrations/skills/spring-boot-data-api/assets/templates/Service.java.template create mode 100644 integrations/skills/spring-boot-data-api/assets/templates/application.yml.template create mode 100644 tools/data-api-tools/src/test/java/com/datastax/astra/tool/copy/README.md create mode 100644 tools/data-api-tools/src/test/resources/logback-test.xml create mode 100644 tools/data-api-tools/src/test/resources/test-config.properties diff --git a/integrations/skills/spring-boot-data-api/README.md b/integrations/skills/spring-boot-data-api/README.md index a290b4a..04fc625 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/assets/examples/basic/README.md b/integrations/skills/spring-boot-data-api/assets/examples/basic/README.md new file mode 100644 index 0000000..99b6a40 --- /dev/null +++ b/integrations/skills/spring-boot-data-api/assets/examples/basic/README.md @@ -0,0 +1,71 @@ +# Basic Spring Boot with Astra DB Example + +This is a minimal working example demonstrating Spring Boot integration with Astra DB. + +## What's Included + +- Simple Product entity with basic CRUD operations +- Spring Data repository +- REST API endpoints +- Configuration examples + +## Quick Start + +1. **Set environment variables:** + ```bash + export ASTRA_DB_TOKEN="AstraCS:..." + export ASTRA_DB_ENDPOINT="https://your-db-id-region.apps.astra.datastax.com" + ``` + +2. **Run the application:** + ```bash + mvn spring-boot:run + ``` + +3. **Test the API:** + ```bash + # Create a product + curl -X POST http://localhost:8080/api/products \ + -H "Content-Type: application/json" \ + -d '{"name":"Laptop","category":"Electronics","price":999.99,"stock":10}' + + # List all products + curl http://localhost:8080/api/products + + # Get by ID + curl http://localhost:8080/api/products/{id} + ``` + +## Project Structure + +``` +src/ +├── main/ +│ ├── java/ +│ │ └── com/example/demo/ +│ │ ├── DemoApplication.java +│ │ ├── model/ +│ │ │ └── Product.java +│ │ ├── repository/ +│ │ │ └── ProductRepository.java +│ │ ├── service/ +│ │ │ └── ProductService.java +│ │ └── controller/ +│ │ └── ProductController.java +│ └── resources/ +│ └── application.yml +└── test/ + └── java/ + └── com/example/demo/ + └── ProductServiceTest.java +``` + +## Next Steps + +- Add vector search capabilities +- Implement pagination +- Add validation +- Create integration tests +- Deploy to production + +See the main [SKILL.md](../../SKILL.md) for detailed guidance. diff --git a/integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template new file mode 100644 index 0000000..20311ef --- /dev/null +++ b/integrations/skills/spring-boot-data-api/assets/templates/Controller.java.template @@ -0,0 +1,94 @@ +package com.example.demo.controller; + +import com.example.demo.model.{{CLASS_NAME}}; +import com.example.demo.service.{{CLASS_NAME}}Service; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * REST controller for {{CLASS_NAME}} endpoints. + */ +@RestController +@RequestMapping("/api/{{RESOURCE_PATH}}") +@RequiredArgsConstructor +public class {{CLASS_NAME}}Controller { + + private final {{CLASS_NAME}}Service service; + + /** + * List all entities. + */ + @GetMapping + public List<{{CLASS_NAME}}> list() { + return service.findAll(); + } + + /** + * List entities with pagination. + */ + @GetMapping("/paginated") + public Page<{{CLASS_NAME}}> listPaginated( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int size, + @RequestParam(defaultValue = "id") String sortBy) { + return service.findAllPaginated(page, size, sortBy); + } + + /** + * Get entity by ID. + */ + @GetMapping("/{id}") + public {{CLASS_NAME}} get(@PathVariable String id) { + return service.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Entity not found")); + } + + /** + * Create new entity. + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public {{CLASS_NAME}} create(@Valid @RequestBody {{CLASS_NAME}} entity) { + return service.create(entity); + } + + /** + * Update existing entity. + */ + @PutMapping("/{id}") + public {{CLASS_NAME}} update( + @PathVariable String id, + @Valid @RequestBody {{CLASS_NAME}} entity) { + return service.update(id, entity); + } + + /** + * Delete entity. + */ + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable String id) { + service.delete(id); + } + + /** + * Search entities. + */ + @GetMapping("/search") + public List<{{CLASS_NAME}}> search( + @RequestParam String field, + @RequestParam String value) { + return service.search(field, value); + } +} + +class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/integrations/skills/spring-boot-data-api/assets/templates/Document.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Document.java.template new file mode 100644 index 0000000..1ce2b92 --- /dev/null +++ b/integrations/skills/spring-boot-data-api/assets/templates/Document.java.template @@ -0,0 +1,60 @@ +package com.example.demo.model; + +import com.datastax.astra.client.collections.mapping.*; +import com.datastax.astra.client.core.vector.SimilarityMetric; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.*; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.Instant; + +/** + * Document class template for Astra DB collections. + * + * Replace {{COLLECTION_NAME}} with your collection name. + * Customize fields, validation, and indexing as needed. + */ +@Data +@Accessors(chain = true) +@DataApiCollection( + name = "{{COLLECTION_NAME}}", + // Optional: Vector search configuration + // vectorDimension = 1536, + // vectorSimilarity = SimilarityMetric.COSINE, + // vectorizeProvider = "openai", + // vectorizeModel = "text-embedding-ada-002", + // vectorizeSharedSecret = "OPENAI_API_KEY", + + // Optional: Full-text search + // lexicalEnabled = true, + // lexicalAnalyzer = AnalyzerTypes.ENGLISH, + + // Optional: Indexing optimization + indexingDeny = {"internal_field"} +) +public class {{CLASS_NAME}} { + + @DocumentId + private String id; + + @NotBlank(message = "Field is required") + private String field1; + + @NotNull(message = "Field is required") + private String field2; + + // Optional: Auto-vectorization + // @Vectorize + // private String vectorizeField; + + // Optional: Full-text search + // @Lexical + // private String searchableText; + + @JsonProperty("created_at") + private Instant createdAt; + + @JsonProperty("updated_at") + private Instant updatedAt; +} diff --git a/integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template new file mode 100644 index 0000000..58571d2 --- /dev/null +++ b/integrations/skills/spring-boot-data-api/assets/templates/Repository.java.template @@ -0,0 +1,25 @@ +package com.example.demo.repository; + +import com.datastax.astra.spring.DataApiCollectionCrudRepository; +import com.example.demo.model.{{CLASS_NAME}}; +import org.springframework.stereotype.Repository; + +/** + * Spring Data repository for {{CLASS_NAME}}. + * + * Provides CRUD operations and query capabilities: + * - save(entity) + * - findById(id) + * - findAll() + * - deleteById(id) + * - count() + * - Query by Example + * - Data API filters + */ +@Repository +public interface {{CLASS_NAME}}Repository + extends DataApiCollectionCrudRepository<{{CLASS_NAME}}, String> { + + // Custom query methods can be added here + // Spring Data will automatically implement them +} diff --git a/integrations/skills/spring-boot-data-api/assets/templates/Service.java.template b/integrations/skills/spring-boot-data-api/assets/templates/Service.java.template new file mode 100644 index 0000000..c565884 --- /dev/null +++ b/integrations/skills/spring-boot-data-api/assets/templates/Service.java.template @@ -0,0 +1,93 @@ +package com.example.demo.service; + +import com.datastax.astra.client.core.query.Filter; +import com.datastax.astra.client.core.query.Filters; +import com.example.demo.model.{{CLASS_NAME}}; +import com.example.demo.repository.{{CLASS_NAME}}Repository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.*; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; + +/** + * Service layer for {{CLASS_NAME}} business logic. + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class {{CLASS_NAME}}Service { + + private final {{CLASS_NAME}}Repository repository; + + /** + * Create a new entity. + */ + public {{CLASS_NAME}} create({{CLASS_NAME}} entity) { + entity.setCreatedAt(Instant.now()); + entity.setUpdatedAt(Instant.now()); + log.info("Creating entity: {}", entity); + return repository.save(entity); + } + + /** + * Find entity by ID. + */ + public Optional<{{CLASS_NAME}}> findById(String id) { + return repository.findById(id); + } + + /** + * Find all entities. + */ + public List<{{CLASS_NAME}}> findAll() { + return (List<{{CLASS_NAME}}>) repository.findAll(); + } + + /** + * Update an existing entity. + */ + public {{CLASS_NAME}} update(String id, {{CLASS_NAME}} updates) { + {{CLASS_NAME}} entity = repository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Entity not found: " + id)); + + // Update fields + // entity.setField1(updates.getField1()); + // entity.setField2(updates.getField2()); + entity.setUpdatedAt(Instant.now()); + + return repository.save(entity); + } + + /** + * Delete entity by ID. + */ + public void delete(String id) { + repository.deleteById(id); + } + + /** + * Find entities with pagination. + */ + public Page<{{CLASS_NAME}}> findAllPaginated(int page, int size, String sortBy) { + Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy)); + return repository.findAll(Example.of(new {{CLASS_NAME}}()), pageable); + } + + /** + * Search entities using Data API filters. + */ + public List<{{CLASS_NAME}}> search(String field, String value) { + Filter filter = Filters.eq(field, value); + return (List<{{CLASS_NAME}}>) repository.findAll(filter); + } +} + +class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/integrations/skills/spring-boot-data-api/assets/templates/application.yml.template b/integrations/skills/spring-boot-data-api/assets/templates/application.yml.template new file mode 100644 index 0000000..af5b750 --- /dev/null +++ b/integrations/skills/spring-boot-data-api/assets/templates/application.yml.template @@ -0,0 +1,63 @@ +# Spring Boot Application Configuration for Astra DB + +spring: + application: + name: {{APPLICATION_NAME}} + +astra: + data-api: + # Database connection (required) + token: ${ASTRA_DB_TOKEN} + endpoint-url: ${ASTRA_DB_ENDPOINT} + keyspace: ${ASTRA_DB_KEYSPACE:default_keyspace} + + # Schema management + # Options: CREATE_IF_NOT_EXISTS, VALIDATE, NONE + schema-action: CREATE_IF_NOT_EXISTS + + # Request logging (development) + log-request: ${ASTRA_LOG_REQUESTS:false} + + # Advanced options + options: + # Timeouts (milliseconds) + timeout: + connect: 5000 + request: 10000 + general: 30000 + collection-admin: 60000 + + # HTTP configuration + http: + retry-count: 3 + retry-delay: 100 + version: HTTP_2 + redirect: NORMAL + + # Embedding API key for vector search (optional) + embedding-api-key: ${OPENAI_API_KEY:} + +# Logging configuration +logging: + level: + root: INFO + com.example.demo: DEBUG + com.datastax.astra: ${ASTRA_LOG_LEVEL:INFO} + com.datastax.astra.client.DataAPIClient: ${ASTRA_LOG_LEVEL:INFO} + +# Server configuration +server: + port: ${PORT:8080} + error: + include-message: always + include-binding-errors: always + +# Actuator endpoints (optional) +management: + endpoints: + web: + exposure: + include: health,info,metrics + metrics: + tags: + application: ${spring.application.name} 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 0000000..a6b24de --- /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 0000000..5cf5eec --- /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 0000000..252cf7c --- /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