Skip to content

Commit 065ae2f

Browse files
authored
feat: add Spring Boot Data API skill templates and tools test resources (#97)
1 parent 5c7f0cd commit 065ae2f

9 files changed

Lines changed: 556 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Basic Spring Boot with Astra DB Example
2+
3+
This is a minimal working example demonstrating Spring Boot integration with Astra DB.
4+
5+
## What's Included
6+
7+
- Simple Product entity with basic CRUD operations
8+
- Spring Data repository
9+
- REST API endpoints
10+
- Configuration examples
11+
12+
## Quick Start
13+
14+
1. **Set environment variables:**
15+
```bash
16+
export ASTRA_DB_TOKEN="AstraCS:..."
17+
export ASTRA_DB_ENDPOINT="https://your-db-id-region.apps.astra.datastax.com"
18+
```
19+
20+
2. **Run the application:**
21+
```bash
22+
mvn spring-boot:run
23+
```
24+
25+
3. **Test the API:**
26+
```bash
27+
# Create a product
28+
curl -X POST http://localhost:8080/api/products \
29+
-H "Content-Type: application/json" \
30+
-d '{"name":"Laptop","category":"Electronics","price":999.99,"stock":10}'
31+
32+
# List all products
33+
curl http://localhost:8080/api/products
34+
35+
# Get by ID
36+
curl http://localhost:8080/api/products/{id}
37+
```
38+
39+
## Project Structure
40+
41+
```
42+
src/
43+
├── main/
44+
│ ├── java/
45+
│ │ └── com/example/demo/
46+
│ │ ├── DemoApplication.java
47+
│ │ ├── model/
48+
│ │ │ └── Product.java
49+
│ │ ├── repository/
50+
│ │ │ └── ProductRepository.java
51+
│ │ ├── service/
52+
│ │ │ └── ProductService.java
53+
│ │ └── controller/
54+
│ │ └── ProductController.java
55+
│ └── resources/
56+
│ └── application.yml
57+
└── test/
58+
└── java/
59+
└── com/example/demo/
60+
└── ProductServiceTest.java
61+
```
62+
63+
## Next Steps
64+
65+
- Add vector search capabilities
66+
- Implement pagination
67+
- Add validation
68+
- Create integration tests
69+
- Deploy to production
70+
71+
See the main [SKILL.md](../../SKILL.md) for detailed guidance.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.example.demo.controller;
2+
3+
import com.example.demo.model.{{CLASS_NAME}};
4+
import com.example.demo.service.{{CLASS_NAME}}Service;
5+
import jakarta.validation.Valid;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.data.domain.Page;
8+
import org.springframework.http.HttpStatus;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
import java.util.List;
12+
13+
/**
14+
* REST controller for {{CLASS_NAME}} endpoints.
15+
*/
16+
@RestController
17+
@RequestMapping("/api/{{RESOURCE_PATH}}")
18+
@RequiredArgsConstructor
19+
public class {{CLASS_NAME}}Controller {
20+
21+
private final {{CLASS_NAME}}Service service;
22+
23+
/**
24+
* List all entities.
25+
*/
26+
@GetMapping
27+
public List<{{CLASS_NAME}}> list() {
28+
return service.findAll();
29+
}
30+
31+
/**
32+
* List entities with pagination.
33+
*/
34+
@GetMapping("/paginated")
35+
public Page<{{CLASS_NAME}}> listPaginated(
36+
@RequestParam(defaultValue = "0") int page,
37+
@RequestParam(defaultValue = "20") int size,
38+
@RequestParam(defaultValue = "id") String sortBy) {
39+
return service.findAllPaginated(page, size, sortBy);
40+
}
41+
42+
/**
43+
* Get entity by ID.
44+
*/
45+
@GetMapping("/{id}")
46+
public {{CLASS_NAME}} get(@PathVariable String id) {
47+
return service.findById(id)
48+
.orElseThrow(() -> new ResourceNotFoundException("Entity not found"));
49+
}
50+
51+
/**
52+
* Create new entity.
53+
*/
54+
@PostMapping
55+
@ResponseStatus(HttpStatus.CREATED)
56+
public {{CLASS_NAME}} create(@Valid @RequestBody {{CLASS_NAME}} entity) {
57+
return service.create(entity);
58+
}
59+
60+
/**
61+
* Update existing entity.
62+
*/
63+
@PutMapping("/{id}")
64+
public {{CLASS_NAME}} update(
65+
@PathVariable String id,
66+
@Valid @RequestBody {{CLASS_NAME}} entity) {
67+
return service.update(id, entity);
68+
}
69+
70+
/**
71+
* Delete entity.
72+
*/
73+
@DeleteMapping("/{id}")
74+
@ResponseStatus(HttpStatus.NO_CONTENT)
75+
public void delete(@PathVariable String id) {
76+
service.delete(id);
77+
}
78+
79+
/**
80+
* Search entities.
81+
*/
82+
@GetMapping("/search")
83+
public List<{{CLASS_NAME}}> search(
84+
@RequestParam String field,
85+
@RequestParam String value) {
86+
return service.search(field, value);
87+
}
88+
}
89+
90+
class ResourceNotFoundException extends RuntimeException {
91+
public ResourceNotFoundException(String message) {
92+
super(message);
93+
}
94+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.example.demo.model;
2+
3+
import com.datastax.astra.client.collections.mapping.*;
4+
import com.datastax.astra.client.core.vector.SimilarityMetric;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import jakarta.validation.constraints.*;
7+
import lombok.Data;
8+
import lombok.experimental.Accessors;
9+
10+
import java.time.Instant;
11+
12+
/**
13+
* Document class template for Astra DB collections.
14+
*
15+
* Replace {{COLLECTION_NAME}} with your collection name.
16+
* Customize fields, validation, and indexing as needed.
17+
*/
18+
@Data
19+
@Accessors(chain = true)
20+
@DataApiCollection(
21+
name = "{{COLLECTION_NAME}}",
22+
// Optional: Vector search configuration
23+
// vectorDimension = 1536,
24+
// vectorSimilarity = SimilarityMetric.COSINE,
25+
// vectorizeProvider = "openai",
26+
// vectorizeModel = "text-embedding-ada-002",
27+
// vectorizeSharedSecret = "OPENAI_API_KEY",
28+
29+
// Optional: Full-text search
30+
// lexicalEnabled = true,
31+
// lexicalAnalyzer = AnalyzerTypes.ENGLISH,
32+
33+
// Optional: Indexing optimization
34+
indexingDeny = {"internal_field"}
35+
)
36+
public class {{CLASS_NAME}} {
37+
38+
@DocumentId
39+
private String id;
40+
41+
@NotBlank(message = "Field is required")
42+
private String field1;
43+
44+
@NotNull(message = "Field is required")
45+
private String field2;
46+
47+
// Optional: Auto-vectorization
48+
// @Vectorize
49+
// private String vectorizeField;
50+
51+
// Optional: Full-text search
52+
// @Lexical
53+
// private String searchableText;
54+
55+
@JsonProperty("created_at")
56+
private Instant createdAt;
57+
58+
@JsonProperty("updated_at")
59+
private Instant updatedAt;
60+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.example.demo.repository;
2+
3+
import com.datastax.astra.spring.DataApiCollectionCrudRepository;
4+
import com.example.demo.model.{{CLASS_NAME}};
5+
import org.springframework.stereotype.Repository;
6+
7+
/**
8+
* Spring Data repository for {{CLASS_NAME}}.
9+
*
10+
* Provides CRUD operations and query capabilities:
11+
* - save(entity)
12+
* - findById(id)
13+
* - findAll()
14+
* - deleteById(id)
15+
* - count()
16+
* - Query by Example
17+
* - Data API filters
18+
*/
19+
@Repository
20+
public interface {{CLASS_NAME}}Repository
21+
extends DataApiCollectionCrudRepository<{{CLASS_NAME}}, String> {
22+
23+
// Custom query methods can be added here
24+
// Spring Data will automatically implement them
25+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.example.demo.service;
2+
3+
import com.datastax.astra.client.core.query.Filter;
4+
import com.datastax.astra.client.core.query.Filters;
5+
import com.example.demo.model.{{CLASS_NAME}};
6+
import com.example.demo.repository.{{CLASS_NAME}}Repository;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import org.springframework.data.domain.*;
10+
import org.springframework.stereotype.Service;
11+
12+
import java.time.Instant;
13+
import java.util.List;
14+
import java.util.Optional;
15+
16+
/**
17+
* Service layer for {{CLASS_NAME}} business logic.
18+
*/
19+
@Slf4j
20+
@Service
21+
@RequiredArgsConstructor
22+
public class {{CLASS_NAME}}Service {
23+
24+
private final {{CLASS_NAME}}Repository repository;
25+
26+
/**
27+
* Create a new entity.
28+
*/
29+
public {{CLASS_NAME}} create({{CLASS_NAME}} entity) {
30+
entity.setCreatedAt(Instant.now());
31+
entity.setUpdatedAt(Instant.now());
32+
log.info("Creating entity: {}", entity);
33+
return repository.save(entity);
34+
}
35+
36+
/**
37+
* Find entity by ID.
38+
*/
39+
public Optional<{{CLASS_NAME}}> findById(String id) {
40+
return repository.findById(id);
41+
}
42+
43+
/**
44+
* Find all entities.
45+
*/
46+
public List<{{CLASS_NAME}}> findAll() {
47+
return (List<{{CLASS_NAME}}>) repository.findAll();
48+
}
49+
50+
/**
51+
* Update an existing entity.
52+
*/
53+
public {{CLASS_NAME}} update(String id, {{CLASS_NAME}} updates) {
54+
{{CLASS_NAME}} entity = repository.findById(id)
55+
.orElseThrow(() -> new ResourceNotFoundException("Entity not found: " + id));
56+
57+
// Update fields
58+
// entity.setField1(updates.getField1());
59+
// entity.setField2(updates.getField2());
60+
entity.setUpdatedAt(Instant.now());
61+
62+
return repository.save(entity);
63+
}
64+
65+
/**
66+
* Delete entity by ID.
67+
*/
68+
public void delete(String id) {
69+
repository.deleteById(id);
70+
}
71+
72+
/**
73+
* Find entities with pagination.
74+
*/
75+
public Page<{{CLASS_NAME}}> findAllPaginated(int page, int size, String sortBy) {
76+
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
77+
return repository.findAll(Example.of(new {{CLASS_NAME}}()), pageable);
78+
}
79+
80+
/**
81+
* Search entities using Data API filters.
82+
*/
83+
public List<{{CLASS_NAME}}> search(String field, String value) {
84+
Filter filter = Filters.eq(field, value);
85+
return (List<{{CLASS_NAME}}>) repository.findAll(filter);
86+
}
87+
}
88+
89+
class ResourceNotFoundException extends RuntimeException {
90+
public ResourceNotFoundException(String message) {
91+
super(message);
92+
}
93+
}

0 commit comments

Comments
 (0)