Skip to content

DomainTemplate API Reference

The API manages DomainTemplate business object. Its singular base path is /api/v1/domaintemplate. Swagger UI is published at /swagger-ui.html.

All business endpoints require JWT authentication. Obtain a token with POST /api/v1/auth/login, then send it as Authorization: Bearer <token>. The forced administrator account is configured with APP_ADMIN_USERNAME and APP_ADMIN_PASSWORD at API startup.

Resource Schema

Field JSON type Required Rules
id number Response only Server-generated Java Long.
code string Yes Unique among active rows and immutable.
label string Yes Non-blank and mutable.
description string or null No Mutable.
createdDate string Response only Server-owned Instant in ISO-8601 format.
lastModifiedDate string Response only Server-owned Instant in ISO-8601 format.
createdBy string Response only Authenticated username at creation.
lastModifiedBy string Response only Authenticated username at the last persisted change.

Endpoints

Authentication and Administration

Method Path Description
POST /api/v1/auth/login Exchange username/password for a JWT.
GET /api/v1/auth/me Read the authenticated user.
GET /api/v1/auth/users List users. Requires ADMIN.
POST /api/v1/auth/users Create a user with a BCrypt-stored password. Requires ADMIN.
GET /api/v1/auth/users/{id} Read a user. Requires ADMIN.
PUT /api/v1/auth/users/{id} Update username, enabled flag, roles, and optionally password. Requires ADMIN.
DELETE /api/v1/auth/users/{id} Delete a user. Requires ADMIN.
POST /api/v1/auth/users/delete-selected Delete the users listed in {"ids":[...]}. Requires ADMIN.
GET /api/v1/auth/roles List roles. Requires ADMIN.
POST /api/v1/auth/roles Create a role. Requires ADMIN.
GET /api/v1/auth/roles/{id} Read a role. Requires ADMIN.
PUT /api/v1/auth/roles/{id} Update a role label. Requires ADMIN.
DELETE /api/v1/auth/roles/{id} Delete a non-system role. Requires ADMIN.
POST /api/v1/auth/roles/delete-selected Delete the roles listed in {"ids":[...]}; system roles are skipped. Requires ADMIN.

The ANONYMOUS, ADMIN, and USER roles are seeded at first startup and cannot be deleted. Bulk delete endpoints respond 200 OK with {"deletedIds":[...],"skippedIds":[...]}; unknown IDs and protected rows are reported as skipped. User responses never include passwords or password hashes.

User Settings

User settings are implemented in the fr.tiogars.domaintemplate.settings.user package. The first setting is viewMode (light, dark, or system; default system).

Method Path Description
GET /api/v1/settings/user/me Read the authenticated user's settings. Requires USER or ADMIN.
PUT /api/v1/settings/user/me Update the authenticated user's settings. Requires USER or ADMIN.
GET /api/v1/settings/user/users/{userId} Read the settings of a user with the USER role. Requires ADMIN; returns 404 for other users.
PUT /api/v1/settings/user/users/{userId} Update the settings of a user with the USER role. Requires ADMIN; returns 404 for other users.
GET /api/v1/settings/user/anonymous Read the ANONYMOUS role settings. Public.
PUT /api/v1/settings/user/anonymous Update the ANONYMOUS role settings. Requires ADMIN.

Settings that were never saved are returned with default values; they are stored on first update and deleted with their user.

DomainTemplate

Method Path Description
GET /api/v1/domaintemplate List all objects, ordered by code.
GET /api/v1/domaintemplate/{id} Read one object by server ID.
POST /api/v1/domaintemplate Create an object.
PUT /api/v1/domaintemplate/{id} Update its mutable fields.
DELETE /api/v1/domaintemplate/{id} Archive one object without removing its database row.
POST /api/v1/domaintemplate/delete-selected Archive the objects listed in {"ids":[...]}. Requires ADMIN.
POST /api/v1/domaintemplate/seed Upsert an array by code.
POST /api/v1/domaintemplate/import.json Import and upsert a JSON array by code.
POST /api/v1/domaintemplate/import.csv Import and upsert CSV rows by code.
GET /api/v1/domaintemplate/export.json Export all objects as JSON.
GET /api/v1/domaintemplate/export.csv Export all objects as CSV.
GET /api/v1/domaintemplate/{id}/print.pdf Print one active object as PDF.
GET /api/v1/domaintemplate/print.pdf Print all active objects as PDF, ordered by code.
POST /api/v1/domaintemplate/print-selected.pdf Print the active objects listed in {"ids":[...]} as PDF, ordered by code.

PDF Printing

PDF printing uses Apache PDFBox 3.0.7 and three dedicated services for one, all, and selected DomainTemplates. All three endpoints require the same JWT authentication and ADMIN or USER role as reading DomainTemplates and return application/pdf, inline Content-Disposition, and Cache-Control: no-store.

Each record starts on a new A4 page and includes its ID, code, label, description, and audit fields. Long fields wrap and continue onto additional numbered pages. The embedded Liberation Sans font supplied by PDFBox supports accented Latin, Greek, and Cyrillic text; unsupported characters return 422 Unprocessable Content instead of silently changing the content. Printing an empty collection returns a PDF stating that there are no DomainTemplates.

The selected request accepts 1 to 1000 positive IDs. Duplicate IDs print only once. Invalid selections return 400 Bad Request; an unknown or archived ID returns 404 Not Found for the entire request, so a PDF never silently omits selected records. Archived records are excluded from all PDF queries.

In the React backend console, Print all prints every active record, independently of grid pagination or filtering. Print selected prints only the checkbox-selected grid rows, including selections across pages, and is disabled when nothing is selected. Print on the detail page prints the current saved record. Each action opens the generated PDF in a new browser tab; use the PDF viewer's native Print control to choose a printer. Popups must be allowed for the site. Failed requests close the blank viewer and display an error in the console.

The React client's print(id), printAll(), and printSelected(ids) methods return Promise<Blob>. Binary requests use the configured API URL, fetcher, and token provider directly, outside the JSON-oriented RTK Query cache, avoiding non-serializable PDF objects in Redux state.

Create

POST /api/v1/domaintemplate accepts only business fields:

{
    "code": "CUSTOMER",
    "label": "Customer",
    "description": "Customer master data"
}

The response is 201 Created, includes the complete resource, and sets Location to /api/v1/domaintemplate/{id}. A duplicate code returns 409 Conflict. Blank required fields return 400 Bad Request.

Read

GET /api/v1/domaintemplate returns a JSON array. GET /api/v1/domaintemplate/{id} returns one object or 404 Not Found.

Update

PUT /api/v1/domaintemplate/{id} accepts the mutable fields only:

{
    "label": "Customer record",
    "description": null
}

The server preserves id, code, and createdDate, and replaces lastModifiedDate. The response is 200 OK, or 404 Not Found for an unknown ID.

Delete

DELETE /api/v1/domaintemplate/{id} returns 204 No Content, or 404 Not Found for an unknown or already archived ID.

POST /api/v1/domaintemplate/delete-selected archives several objects in one transaction. The body is {"ids":[1,2,3]} and the response is {"deletedIds":[1,2],"skippedIds":[3]}, where skipped IDs were unknown or already archived. The admin console uses it for the grid's "Delete selected" action.

Deletion sets deleted_date and deleted_by; the original ID, code, business fields, and creation metadata remain in PostgreSQL. Archived rows are excluded from all normal JPA queries, including read, update, list, export, duplicate checks, and seed/import lookups. They cannot be updated or restored through the API.

After deletion, creating the same code produces a new row with a new ID. Seed/import also creates a new row rather than reviving the archived row. Multiple archived rows may share a code, but a PostgreSQL partial unique index permits at most one active row per code. Optimistic versioning prevents stale writers from overwriting a deletion or another update.

Concurrent writes that lose an optimistic-lock race, or violate active-code uniqueness, return 409 Conflict; other database integrity errors are not treated as duplicate-code conflicts. Refresh the resource before retrying.

Persistence Auditing and Required Migration

Auditing and soft deletion apply to DomainTemplateEntity only; users, roles, and user settings retain their existing behavior. Spring Data JPA supplies creation and modification timestamps, truncated to PostgreSQL microsecond precision, and records the authenticated username:

Database column Meaning
created_by Username at creation; never changed.
last_modified_by Username at the last persisted change, including deletion.
deleted_by Username that archived the row, or NULL while active.
deleted_date Archival timestamp, or NULL while active.
version Optimistic concurrency token managed by JPA.

Actors are username snapshots, not foreign keys: changing or deleting a user does not erase the recorded name. Writes without an authenticated security context fail; scheduled jobs must establish an explicit service identity. JSON responses (including JSON exports) expose createdBy and lastModifiedBy as read-only fields, displayed alongside their timestamps in the backend console detail view. Historical actors backfilled by the migration appear as unknown. Deletion metadata and the optimistic-lock version remain persistence-only; the CSV contract remains unchanged. Spring Data auditing records the creator and most recent editor, not a full revision history.

Before deploying this API version, stop API writers, back up the database, and run the manual PostgreSQL migration. Run it for both existing and new databases, using the same schema/search path as the API. For example, with PostgreSQL connection variables configured:

psql -v ON_ERROR_STOP=1 -f java\api\src\main\resources\db\manual\domain-template-auditing-soft-delete.sql

The migration is transactional and rerunnable. It retains all rows and existing timestamps, backfills unavailable historical actors as unknown, adds the audit/deletion/version columns, removes the old Hibernate-generated unique constraint on code, and creates the active-only unique index. It can also create the DomainTemplate table in an empty database. ddl-auto: update alone does not remove the old constraint or create the partial index; the script is deliberately not executed automatically.

Inspect retained history with a privileged SQL query (it is not exposed by normal API endpoints):

SELECT id, code, created_by, created_date, last_modified_by,
       last_modified_date, deleted_by, deleted_date
FROM domain_templates
WHERE code = 'CUSTOMER'
ORDER BY id;

The PostgreSQL lifecycle tests require Docker and run with the normal API test suite:

mvn -B -f java\pom.xml -pl api test

Seed and Import

Seed and both import formats use upsert semantics keyed by code: a missing code creates an object and an existing code updates label and description. Client-supplied id, createdDate, lastModifiedDate, createdBy, and lastModifiedBy values are ignored. The server keeps ownership of identifiers, timestamps, and audit actors.

POST /api/v1/domaintemplate/seed and POST /api/v1/domaintemplate/import.json accept an array. A minimal document is:

[
    {
        "code": "CUSTOMER",
        "label": "Customer",
        "description": null
    }
]

Both return 201 Created with the stored resources.

Import and Export Formats

JSON export returns the complete resource schema. The same exported JSON can be imported: server-owned fields are accepted only as transport data and ignored during the upsert.

[
    {
        "id": 42,
        "code": "CUSTOMER",
        "label": "Customer",
        "description": "Customer master data",
        "createdDate": "2026-08-30T17:00:00Z",
        "lastModifiedDate": "2026-08-30T17:00:00Z",
        "createdBy": "admin",
        "lastModifiedBy": "admin"
    }
]

CSV import and export use this header:

id,code,label,description,createdDate,lastModifiedDate

All six columns are required in the CSV header so an export can round-trip. On import, id, createdDate, and lastModifiedDate are ignored; empty description becomes null.

id,code,label,description,createdDate,lastModifiedDate
42,CUSTOMER,Customer,Customer master data,2026-08-30T17:00:00Z,2026-08-30T17:00:00Z

Response

{
    "id": 42,
    "code": "CUSTOMER",
    "label": "Customer",
    "description": "Customer master data",
    "createdDate": "2026-08-30T17:00:00Z",
    "lastModifiedDate": "2026-08-30T17:05:00Z",
    "createdBy": "admin",
    "lastModifiedBy": "editor"
}

Clients

The java-client, react-client, and dart-client modules provide typed Java, TypeScript, and Dart access to CRUD, seed, import, and export operations. Construct each client with the service origin; the client appends /api/v1/domaintemplate.

The react-client also generates RTK Query endpoints and exports an AuthClient for login, current-user lookup, and user and role administration under /api/v1/auth, and a UserSettingsClient for user settings under /api/v1/settings/user.