generator-jhipster-cassandra
v1.0.17
Published
Description for Cassandra Composite Primary Keys
Maintainers
Readme
generator-jhipster-cassandra
A JHipster blueprint for Cassandra with advanced support for composite primary keys, sets, and maps. Compatible with JHipster v9.0.0.
Introduction
This is a JHipster blueprint designed to extend JHipster’s capabilities to support Apache Cassandra, particularly with composite primary keys, SET, and MAP types.
The generator-jhipster-cassandra blueprint provides powerful schema modeling tools tailored for Cassandra, including fine-grained control over partition and clustering keys, along with native support for complex Cassandra collections like sets and maps.
🔑 Key Features
Composite Primary Key Support
- Define entities with multiple primary key fields.
- Use
@customAnnotation("PrimaryKeyType.PARTITIONED")and@customAnnotation("PrimaryKeyType.CLUSTERED")to clearly specify partition and clustering columns. - Auto-generate query methods for equality, range, and filtering on clustering columns.
Set & Map Field Support
- Native Cassandra collection types are now fully supported:
CassandraType.Name.SET- Supports
TEXT CassandraType.Name.MAP- Supports various key-value types:
TEXT,BOOLEAN,DECIMAL, andBIGINT.
- Supports various key-value types:
- Native Cassandra collection types are now fully supported:
AI-Powered Semantic Vector Search
- Define vector embedding fields using
@customAnnotation("VECTOR")with configurable dimensions. - Automatic AI search bar on entity list pages with semantic similarity search.
- Checkbox selection to search across one or more vector fields when an entity has multiple embeddings.
- Uses Cassandra 5.0+ SAI (Storage Attached Indexes) with ANN (Approximate Nearest Neighbor) queries.
- Powered by Spring AI with OpenAI embeddings (text-embedding-3-small model, 1536 dimensions).
- Auto-generates
EmbeddingService,EmbeddingConfiguration, repository ANN query methods, and REST endpoints.
- Define vector embedding fields using
Custom Annotations
- Annotations like
@customAnnotation("UTC_DATE")or@customAnnotation("TIMEUUID")support consistent metadata across entity fields.
- Annotations like
Optimized for Microservices
- Seamless integration with the JHipster microservice architecture.
- Supports JDL files with complex schemas.
Improvements Since v1.0.15
The following improvements have been made since the last open-source tagged release (v1.0.15):
Cassandra Pagination Overhaul
- Replaced page-number-based pagination with native Cassandra Slice pagination using paging state tokens, which is the correct approach for Cassandra's distributed architecture.
- Added a dedicated
/sliceendpoint for backward-compatible paginated queries. - Replaced automatic infinite scroll with a Load More button for better UX control.
- Fixed paging state extraction to use
CassandraPageRequestwith the correct public API. - Fixed infinite "Load More" loop by properly checking for empty results.
Composite Key Search Widget
- Added findBy search methods for Cassandra composite keys with a full search widget on entity list pages.
- Added date pickers and comparison operators (equals, greater than, less than, etc.) for clustering key fields.
- Clustering key fields are automatically disabled when an inequality operator is selected on a preceding clustering column (respecting Cassandra's query restrictions).
- Navbar entities are now sorted alphabetically for easier navigation.
UTC Date Handling
- Added
UTC_DATEdisplay support that prevents timezone shifting -- dates are rendered exactly as stored. - Added a custom
FormatUtcDatePipefor consistent date-only formatting in Angular templates. - Fixed UTC_DATE form handling to use dayjs throughout the stack (create, update, and search forms).
- Configured
DayjsDateAdapterfor Material Datepicker integration.
Composite Key Sorting
- Added column sorting for Cassandra entities, with proper data array clearing when sort changes.
- Sort by composite key fields is now fully supported in the Angular list view.
Backend Fixes
- Fixed malformed CQL
@Querygeneration infindLatestByrepository methods. - Removed expensive
count()queries from Cassandra resource templates (not supported efficiently by Cassandra). - Fixed Cassandra pagination to use an explicit
pagingStatequery parameter for clean API design. - Improved translation key generation for Cassandra entities using i18n entity labels.
🧑💻 Example Use Cases
- Model a
Postentity using a composite primary key (createdDate,addedDateTime,postId) and additional attributes liketitleandcontent. - Use
SETto tag entities with multiple string values. - Use
MAPto represent dynamic metadata or key-value configurations (e.g.,addOnDetailsBoolean,addOnDetailsDecimal). - Define a
Tagentity with AI-powered semantic search acrossnameanddescriptionfields using vector embeddings.
🧪 JDL Examples
Composite Key Example
Below are various examples of defining JDL entities using the @customAnnotation methodology to specify the details of the Cassandra composite primary key. Also, below is an example of a single-value primary key entity. Some example entities are of composite primary keys using Map fields and some are using a Set field. There are also examples of single-value primary key entities using Maps and Set data structures.
// Composite Primary Key Example:
entity Post {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") @customAnnotation("0") createdDate Long
// Do not name composite primary key fields as 'id' as it conflicts with the 'id' field in the JHipster entity.
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") @customAnnotation("1") addedDateTime Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") @customAnnotation("2") postId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") title String required
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") content String required
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") @customAnnotation("") publishedDateTime Long
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") @customAnnotation("") sentDate Long
}
// Single-value Primary Key Example:
entity Product {
// Primary Key field can be named 'id'. JHipster natively supports single-value primary keys. This blueprint also supports single-value primary keys.
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") @customAnnotation("") id UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") title String required
@customAnnotation("") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") @customAnnotation("") price BigDecimal required min(0)
@customAnnotation("") @customAnnotation("CassandraType.Name.BLOB") @customAnnotation("image") @customAnnotation("") image ImageBlob
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") @customAnnotation("") addedDate Long required
}
// Composite Primary Key Example with TIMEUUID clustered key, multiple partitioned keys, with multiple clustered keys.
entity SaathratriEntity2 {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") entityTypeId UUID
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("") yearOfDateAdded Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") arrivalDate Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TIMEUUID") @customAnnotation("TIMEUUID") blogId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityDescription String
@customAnnotation("") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") entityCost BigDecimal
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") departureDate Long
}
// Example showing a text/string set.
entity SaathratriEntity3 {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityType String
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TIMEUUID") @customAnnotation("") createdTimeId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityDescription String
@customAnnotation("") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") entityCost BigDecimal
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") departureDate Long
@customAnnotation("CassandraType.Name.SET") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") tags String,
}
// Example showing key-value data structure.
entity SaathratriEntity4 {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") attributeKey String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") attributeValue String
}
// Example showing text/string, boolean, numeric and date-time maps.
entity AddOnsAvailableByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityType String
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") entityId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") addOnId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnType String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnDetailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") addOnDetailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") addOnDetailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") addOnDetailsBigInt Long
}
// Another example showing text/string, boolean, numeric and date-time maps.
entity AddOnsSelectedByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") arrivalDate Long
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") accountNumber String
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.TIMEUUID") @customAnnotation("") createdTimeId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATE") departureDate Long
@customAnnotation("") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") customerId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerFirstName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerLastName String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerUpdatedEmail String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerUpdatedPhoneNumber String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") customerEstimatedArrivalTime String
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") tinyUrlShortCode String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnDetailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") addOnDetailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") addOnDetailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") addOnDetailsBigInt Long
}
// Single-value Primary Key with Maps
entity LandingPageByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") detailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") detailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") detailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") detailsBigInt Long
}
// Single-value Primary Key with Set
entity SetEntityByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("CassandraType.Name.SET") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") tags String
}
// AI Semantic Search Example with Vector Embeddings:
// The Tag entity demonstrates AI-powered semantic search with multiple vector fields.
// Each VECTOR field stores an embedding generated from a source text field.
// The field name convention is: <sourceFieldName>Embedding (e.g., nameEmbedding derives from name).
entity Tag {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") @customAnnotation("") id UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") name String required
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") @customAnnotation("") description String
@customAnnotation("VECTOR") @customAnnotation("1536") @customAnnotation("") @customAnnotation("") nameEmbedding Blob
@customAnnotation("VECTOR") @customAnnotation("1536") @customAnnotation("") @customAnnotation("") descriptionEmbedding Blob
}Vector Field Annotation Format:
- First annotation:
"VECTOR"— marks the field as a vector embedding column - Second annotation:
"1536"— the vector dimension (1536 for OpenAI text-embedding-3-small) - Third/fourth annotations: unused (leave as
"")
The field name must end with Embedding (e.g., nameEmbedding). The source field is derived by stripping the Embedding suffix (e.g., name).
When an entity has vector fields, the blueprint automatically generates:
- A
GET /api/<entity>/ai-search?query=...&limit=...&fields=...REST endpoint - Cassandra ANN (Approximate Nearest Neighbor) query methods in the repository
- An AI search bar on the Angular list page with checkbox selection for choosing which vector fields to search
EmbeddingServiceandEmbeddingConfigurationfor OpenAI integration
MAP Data Type UI Components
The blueprint generates custom Angular UI components for each Cassandra MAP value type. The following screenshots demonstrate the AddOnsAvailableByOrganization entity, which uses all four supported MAP types.
JDL Definition
entity AddOnsAvailableByOrganization {
@Id @customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") organizationId UUID
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") entityType String
@customAnnotation("PrimaryKeyType.PARTITIONED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") entityId UUID
@customAnnotation("PrimaryKeyType.CLUSTERED") @customAnnotation("CassandraType.Name.UUID") @customAnnotation("") addOnId UUID
@customAnnotation("") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnType String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.TEXT") @customAnnotation("") addOnDetailsText String
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.DECIMAL") @customAnnotation("") addOnDetailsDecimal BigDecimal
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BOOLEAN") @customAnnotation("") addOnDetailsBoolean Boolean
@customAnnotation("CassandraType.Name.MAP") @customAnnotation("CassandraType.Name.BIGINT") @customAnnotation("UTC_DATETIME") addOnDetailsBigInt Long
}MAP<TEXT, TEXT> — String Key-Value Pairs
Edit string-to-string map entries with inline key and value fields. Each entry can be added, edited, or removed.

MAP<TEXT, DECIMAL> — Numeric Values
Edit string-to-decimal map entries for numeric data such as mileage, cost, or quantity.

MAP<TEXT, BOOLEAN> — Boolean Toggle Values
Edit string-to-boolean map entries using toggle switches for true/false values.

MAP<TEXT, BIGINT> with UTC_DATETIME — Date-Time Values
Edit string-to-datetime map entries with a full date and time picker (date, hours, minutes, AM/PM).

List Page — All MAP Types Displayed
The list page renders all MAP columns with their key-value pairs displayed inline.

Detail Page — All MAP Types Displayed
The detail/view page renders all MAP fields with their key-value pairs.

E2E Testing with Cypress
Every custom Angular widget the blueprint ships exposes data-cy hooks so the
generated Cypress specs can drive them without DOM gymnastics:
| Widget | Add-row hooks | Per-row hooks | Dialog hooks |
| ----------------------- | --------------------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------ |
| set-string-component | <field>-add-value, <field>-add-button | <field>-row-<i>-edit, <field>-row-<i>-delete | dialog-edit-value, dialog-save-button |
| map-string-component | <field>-add-key, -add-value, -add-button | <field>-row-<key>-edit, -delete | dialog-edit-value, dialog-save-button |
| map-number-component | same as map-string | same as map-string | same |
| map-boolean-component | <field>-add-key, -add-toggle, -add-button | <field>-row-<i>-edit, -delete | dialog-edit-toggle, dialog-save-button |
| map-dayjs-component | <field>-add-key, -add-datetime-{date,hours,minutes,ampm}, -add-button | <field>-row-<key>-edit, -delete | dialog-save-button |
| app-date-time | <field>-{date,hours,minutes,ampm} | n/a | n/a |
The cypress generator (generators/cypress/generator.js) post-processes the
generated entity specs in POST_WRITING_ENTITIES and emits:
- Smoke tests that drive each widget's Add-row inputs.
- Round-trip tests that fill every MAP/SET widget (incl. MAP via
the nested
<app-date-time>sub-inputs), Save, and assert the POST response body contains the keys/values. - Edit-dialog tests that add a row, open its edit dialog, modify the value (or toggle), Save the dialog, and assert it closes.
- Delete-row tests that add a row and assert its per-row hook disappears after delete.
For UTC_DATETIME scalar fields the patch swaps the upstream .type(…) form-fill
for a click on the entity-update's "Generate" button (which fills date/time
sub-inputs via Angular's reactive form). For MAP/SET scalar fills it strips the
upstream cy.get('[data-cy="<field>"]') lines that target non-existent inputs.
See TESTING.md §5.2 for the full pass-by-pass catalogue (a → c.11 → d) and run instructions.
AI Search Setup
To enable AI-powered semantic search, set your OpenAI API key:
export OPENAI_API_KEY=sk-your-api-key-hereOr add to application-dev.yml:
openai:
api-key: sk-your-api-key-hereRequirements: Cassandra 5.0+ (for SAI vector index support).
🚀 Quick Start
Prerequisites
- Java 21+
- Node.js 20+
- Docker Desktop
- JHipster 9.0.0
Installation
npm install -g generator-jhipster-cassandraUsage
jhipster --blueprints cassandra🎡 Example Project
- Full example repo with Cassandra composite key entities and JDL: 👉 https://github.com/amarpatel-xx/jhipster-cassandra-example
Generate Code
git clone https://github.com/amarpatel-xx/jhipster-cassandra-example.git
cd jhipster-cassandra-example
sh saathratri-generate-code-dev-cassandra.sh🧪 Testing
Two layers of tests: the generator's own unit tests, and the generated application's tests (backend + frontend). This blueprint is Cassandra-only and targets composite primary keys, so the sample covers composite keys, single-value keys, multiple partitioned/clustered keys, TIMEUUID, BLOB, and Set/Map data structures.
Generator unit tests
Lint, format check, and Vitest snapshot tests (each sub-generator is run and its output
asserted). This is what the generator.yml GitHub workflow runs on every push:
npm testExpected: Prettier prints All matched files use Prettier code style!, ESLint reports
0 problems, and all 12 generator spec suites pass (Test Files 12 passed).
Generate a sample application
The samples.yml GitHub workflow generates the bundled README example entities and
builds the backend. To do the same locally (Node 22+; Java 21 + Docker for the build):
mkdir sample-app && cd sample-app
node /path/to/generator-jhipster-cassandra/cli/cli.cjs \
generate-sample sample --skip-jhipster-dependencies --skip-installExpected: Congratulations, JHipster execution is complete! — a full Spring Boot 4 /
Java 21 microservice (≈100 Java + ≈245 TypeScript files) covering all the example
entities.
Backend
From the generated app directory:
# Compile + package the backend only — no Docker needed (this is what CI runs)
./mvnw -ntp -DskipTests -Dskip.npm package
# Unit + integration tests. Integration tests (*IT) use Testcontainers, so a running
# Docker daemon is required — a Cassandra container is started automatically.
./mvnw -ntp -Dskip.npm verifyExpected: package produces target/*.jar. verify starts a Cassandra Testcontainer and
runs the full test suite green — the domain, DTO, security, exception-translator and
structural tests, plus the composite-key entity REST CRUD integration tests
(*ResourceIT): create / get-one / get-all / update (PUT) / partial update (PATCH) /
delete and their negative cases, for both single-value and composite primary keys
(including auto-generated TIMEUUID clustering keys, and Set / Map columns).
The samples.yml GitHub workflow runs exactly this verify on every push (the runner
provides Docker), so the composite-key CRUD contract is continuously regression-tested.
Frontend
From the generated app directory (Node 22+):
npm install
npm test # = pretest (eslint .) THEN ng test (Vitest) — runs once and exits
npm run test:watch # same, but keeps Vitest in watch mode for iterative TDDnpm test runs eslint . first — if lint fails, the Vitest run never starts — then
the Angular unit tests on Vitest, one-shot. (The blueprint patches the generated
package.json to add --watch=false; upstream JHipster's ng test --coverage script
defaults to watch mode after the Karma→Vitest switch and never exits.) The lint gate
fails only on errors (not warnings). To run just one half:
npx eslint . (lint only) or npx ng test --watch=false (Vitest only — npx ng test
alone re-enters watch mode because Angular's unit-test builder defaults to watch).
Expected: ESLint reports 0 problems and Vitest passes all ~407 specs — the
generated services, list/detail/update components, routing resolvers and the
Cassandra-specific Set/Map/date-time field components, for both single-value and
composite primary keys.
Debugging test failures
The golden rule for this blueprint is fix the .ejs templates / generator.js, never
the generated app (it is overwritten on every regeneration). The full runbook for
diagnosing and fixing failures at every layer — the generate-sample tight loop, backend
composite-key REST CRUD bug patterns, and the Angular frontend (compile / runtime / lint)
bug catalogue — is in TESTING.md.
🔐 Identity Providers
This blueprint supports Keycloak by default. You can switch to Okta using:
okta apps create jhipster🧐 Learn More
👏 Acknowledgements
Huge thanks to:
- yelhouti
- Jeremy Artero
- Matt Raible
- Gaël Marziou
- Cedrick Lunven
- Christophe Borne
- Disha Patel
- Catherine Guevara
