[
https://issues.apache.org/jira/browse/UNOMI-963?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Serge Huber updated UNOMI-963:
------------------------------
Description:
h2. Summary
Enrich Apache Unomi's *live* OpenAPI document (CXF {{OpenApiFeature}}) with
high-quality documentation derived from *existing* Java + Javadoc—without
scraping HTML, without duplicating prose into Swagger
{{@Schema(description=...)}} annotations, and without losing
OSGi/dynamic-deployment accuracy.
Primary consumers: human developers (Swagger UI, manuals), {{unomi-ui}} /
{{inoyu-ui}}, OpenAPI-driven clients, and AI agents that need typed, linked
contracts (not empty {{application/json: {}}}/{{default}} responses).
Related (not a duplicate): [UNOMI-899] (replace jaxrs-analyzer / CXF OpenAPI
generation). That work covers *how* the live spec is generated; this ticket
covers *documentation completeness* of that generation.
----
h2. Problem
The live spec ({{GET /cxs/openapi.json}}) and committed snapshots under
{{unomi-rest-api-tests}} are path-complete but documentation-poor:
* Methods returning {{javax.ws.rs.core.Response}} often show empty response
bodies even when the wire JSON is a typed DTO (e.g. {{TenantUsage}},
{{TenantEventPurgeResult}}).
* Responses are overwhelmingly a single {{default}} entry; named
{{200}}/{{400}}/{{401}}/{{404}}/{{500}} are missing.
* Schemas that *are* emitted from concrete return types have property
names/types but **no** field descriptions (Javadoc is not available at runtime
in CXF/swagger-core).
* Stock CXF {{JavaDocProvider}} scrapes *generated Javadoc HTML*—brittle across
JDK versions and unsuitable as a long-term approach.
* Mass {{@Operation}}/{{@Schema(description=...)}} would *duplicate* existing
Javadoc and drift.
Historical note: Miredot previously gave build-time, Javadoc-based docs but did
not model Unomi's *dynamically deployed* JAX-RS surface well. CXF is correct
for "what this Karaf instance registered"; build-time tools are correct for
"what the sources say." We need both.
----
h2. Inventory baseline (server/unomi, JAX-RS)
Static scan of {{src/main/java}} ({{@Path}} + HTTP verbs; abstract router CRUD
expanded):
|| Metric || Count ||
| HTTP operations | *181* (180 unique method+path; {{GET /profiles/export}}
overloaded twice) |
| Returning {{Response}} | *31* (highest schema risk) |
| JAX-RS resource classes | ~28 |
| Maven REST-ish modules | 8 |
|| Path prefix || Ops || Module ||
| {{/profiles}} (+ personas, sessions, properties, export) | 37 | {{rest}} |
| {{/definitions}} | 16 | {{rest}} |
| {{/campaigns}}, {{/rules}}, {{/segments}}, {{/privacy}} | 10 each | {{rest}}
/ privacy |
| {{/tenants}} | 9 | {{rest}} (3.1) |
| {{/scoring}} | 8 | {{rest}} |
| {{/goals}} | 7 | {{rest}} |
| {{/jsonSchema}} | 6 | json-schema ext |
| {{/lists}} + {{/userList}} | 5+1 | lists-ext + {{rest}} |
| {{/tasks}} | 5 | {{rest}} (3.1) |
| {{/exportConfiguration}}, {{/importConfiguration}} | 5 each | router-rest |
| {{/geonames}} | 5 | geonames |
| {{/events}}, {{/query}}, {{/scopes}} | 4 each | {{rest}} |
| {{/context.json}}, {{/context.js}}, {{/eventcollector}}, {{/cluster}} | 3
each | {{rest}} |
| {{/client}}, {{/groovyActions}}, {{/sfdc}} | 2 each | rest / groovy /
salesforce |
| {{/patches}}, {{/test/ping}} | 1 each | {{rest}} |
|| Maven module || Ops ||
| {{rest}} | 141 |
| {{extensions/privacy-extension/rest}} | 10 |
| {{extensions/router/router-rest}} | 10 |
| {{extensions/json-schema/rest}} | 6 |
| {{extensions/geonames/rest}} | 5 |
| {{extensions/lists-extension/rest}} | 5 |
| {{extensions/groovy-actions/rest}} | 2 |
| {{extensions/salesforce-connector/rest}} | 2 |
Coverage denominator for *JAX-RS*: inventory rows / 181. Progress = enrichment
packs covering those ops.
Separate *servlet* denominator (see below): at least {{GET /health/check}};
optional {{/graphql}}. Do *not* double-count {{web-servlets}}
context/eventcollector/client forwards already covered under {{/cxs}} JAX-RS.
----
h2. Goals
# Live OpenAPI remains *deployment-accurate* (CXF reflects OSGi-registered
resources only).
# Enrichment packs are generated at *build time* from Java + Javadoc (and
JAX-RS annotations)—*no HTML scrape*.
# Prose is *not* duplicated into Swagger description annotations; Javadoc stays
the human source of truth.
# OpenAPI becomes useful for humans *and* agents: summaries, param docs, named
statuses, {{$ref}} schemas, property descriptions, stable operation keys.
# Per-bundle packs so optional extensions contribute docs only when installed.
----
h2. Non-goals
* Mass {{@Schema(description=...)}} / {{@Operation(summary=...)}} copying
Javadoc.
* Using CXF {{JavaDocProvider}} HTML scraping in production.
* Replacing CXF {{OpenApiFeature}} with a build-time-only OpenAPI as the sole
live API.
* Breaking HTTP wire contracts (paths, methods, status semantics, JSON shapes).
* Expecting CXF to *discover* HttpService / whiteboard servlets automatically
(it cannot).
* Turning GraphQL into a full REST OpenAPI in Phase 0 (optional later pack or
link to GraphQL schema).
* Re-documenting {{web-servlets}} {{/context.json}}, {{/context.js}},
{{/eventcollector}}, {{/client/*}} as separate APIs when the same contracts
already exist under {{/cxs}} JAX-RS (optional note about forward/alias only).
----
h2. Architecture
{code}
Maven (per JAX-RS module)
→ JavaParser (or equivalent) on .java sources
→ META-INF/unomi/openapi-doc.json (enrichment pack)
packaged in OSGi bundle
Runtime
→ Discover packs from *installed* bundles (BundleTracker /
ClassLoader.getResources)
→ UnomiJsonDocumentationProvider implements
org.apache.cxf.jaxrs.model.doc.DocumentationProvider
→ OpenApiCustomizer.setJavadocProvider(provider)
→ Subclass/customize OpenApiCustomizer for response $refs + schema property
docs
→ OpenApiFeature serves complete /cxs/openapi.json
{code}
*CXF {{DocumentationProvider}} SPI* (already on {{OpenApiCustomizer}}):
|| SPI || OpenAPI field ||
| {{getMethodDoc}} | {{operation.summary}} / description |
| {{getMethodParameterDoc}} | {{parameter.description}} |
| {{getMethodResponseDoc}} | success response description |
| {{getClassDoc}} | tag description (if {{replaceTags}}) |
That SPI alone does *not* fix empty {{Response}} schemas or DTO property
docs—those need the customizer merge from the same JSON.
----
h2. Servlets and other non-JAX-RS HTTP surfaces
CXF OpenAPI only sees OSGi JAX-RS resources. Unomi also exposes (or forwards)
HTTP via servlets:
|| Path || Implementation || OpenAPI approach ||
| {{GET /health/check}} | {{HealthCheckServlet}} via OSGi {{HttpService}}
({{extensions/healthcheck}}) | *In scope*: servlet enrichment pack + customizer
merge |
| {{/graphql}} | {{GraphQLServlet}} | Optional later; prefer GraphQL schema or
a small servlet pack |
| {{/context.json}}, {{/context.js}}, {{/eventcollector}}, {{/client/*}} |
{{web-servlets}} whiteboard → forward to CXF | Already documented as JAX-RS
under {{/cxs}}; no duplicate paths unless documenting the servlet alias |
*Health contract today (from implementation):*
* Auth: OSGi role {{health}} (missing → *403*).
* Body: JSON array of {{HealthCheckResponse}}.
* Status: *200* if all checks live; *206 Partial Content* otherwise.
*How to include them in the live OpenAPI (recommended):*
# Build-time (or curated) pack, e.g. {{META-INF/unomi/openapi-servlet.json}} or
a {{type: servlet}} section in the same enrichment format—absolute paths, not
under {{/cxs}}.
# Same runtime discovery as JAX-RS packs (only if the health bundle is
installed).
# {{OpenApiCustomizer}} *merges* servlet paths/schemas into the CXF {{OpenAPI}}
model (tags: {{Health}}, etc.).
# Document security explicitly (health role ≠ tenant API key).
# OpenAPI {{servers}}: account for root vs {{/cxs}} (e.g. server URL {{/}} with
absolute {{/health/check}}, or dual servers). Agents care that documented URLs
are reachable as written.
*Alternatives considered (not preferred for 3.1 spike):*
* Thin JAX-RS façade {{GET /cxs/health}} duplicating the servlet (second URL;
keep servlet canonical for ops).
* Completely separate {{/health/openapi.json}} (extra consumer wiring).
----
h2. Build-time extraction (high quality)
*Dual extract* from the same sources:
# *Structure*: {{@Path}}, HTTP verb, {{@*Param}}, return types, generics
({{PartialList<T>}}, etc.), Jackson visibility.
# *Meaning*: class/method/field Javadoc, {{@param}} (match by *name*),
{{@return}}, {{@throws}}.
Optional thin machine tags *only* where inference is ambiguous (do not restate
essays), e.g.:
{code}
@unomi.status 200 {@link TenantUsage}
@unomi.status 404 empty
@unomi.status 400 unsupported period
{code}
Especially required for multi-status bodies (e.g. {{purgeTenantEvents}}: 200
and 500 share {{TenantEventPurgeResult}}).
JSON packs should carry OpenAPI-like fragments (operations + schemas with
property descriptions), keyed by stable {{GET /path}} and/or
{{className#methodName}}.
----
h2. Compatibility
Documentation-only (plus optional signature cleanups that preserve wire
behavior). Especially: do *not* collapse purge {{200}}/{{500}} into a single
success code.
----
h2. Phased delivery
|| Phase || Scope || Notes ||
| *0 - Spike* | {{TenantEndpoint}}, {{TaskEndpoint}}, {{TenantUsage}} / purge
DTOs | Must prove JAX-RS pipeline; early 3.1 candidate |
| *0b - Servlet pack* | {{GET /health/check}} + {{HealthCheckResponse}} schema
| Prove servlet merge + dual path prefix ({{/}} vs {{/cxs}}) |
| *1 - Core* | Remaining {{rest}} module (~141 ops) | Largest surface |
| *2 - Common extensions* | privacy, lists, json-schema | Often installed |
| *3 - Optional extensions* | geonames, groovy, router, sfdc; optional
{{/graphql}} pack | Validates dynamic discovery |
| *4 - Hardening* | CI gates, snapshot regen, UI consumer notes | Release
hygiene |
*Release intent:* Phase 0 should try to land in *3.1* if the spike succeeds;
Phases 1-4 may complete in 3.1 or slip to *3.2* without blocking spike
learnings. Update fixVersion(s) when decided.
----
h2. Acceptance criteria / definition of done
h3. Pipeline
* [ ] Maven Mojo (or equivalent) extracts JAX-RS + Javadoc into a versioned
JSON pack schema.
* [ ] Packs emitted as {{META-INF/unomi/openapi-doc.json}} (name TBD) inside
REST OSGi bundles.
* [ ] Runtime discovery loads packs only from *installed* bundles.
* [ ] {{DocumentationProvider}} implementation fills operation/param/response
*prose* from JSON.
* [ ] {{OpenApiCustomizer}} extension applies response {{$ref}}s and schema
property docs from the same JSON.
* [ ] Wired from {{RestServer}} (existing customizer).
h3. Coverage
* [ ] Inventory of public JAX-RS ops tracked (denominator ≈ 181); enrichment
coverage measurable per module.
* [ ] All {{Response}}-returning methods (31) have status + schema metadata in
packs.
* [ ] DTOs referenced by enriched responses appear under schemas with property
descriptions where Javadoc exists.
* [ ] {{GET /health/check}} present in live OpenAPI when health bundle is
installed (servlet pack merge).
* [ ] {{HealthCheckResponse}} (and array response) documented with statuses 200
/ 206 / 403 and health-role security.
* [ ] No duplicate OpenAPI paths for {{web-servlets}} forwards that already
exist under {{/cxs}} JAX-RS (unless explicitly documenting aliases).
h3. Quality / CI
* [ ] Fail (or stage-warn→fail) if public {{@Path}} method lacks Javadoc
summary.
* [ ] Fail if {{@*Param}} lacks matching {{@param}}.
* [ ] Fail if {{Response}} method lacks status/schema map.
* [ ] Test/fixture: OpenAPI contains {{TenantUsage}} {{$ref}} and named
statuses for purge.
* [ ] Procedure to regenerate committed OpenAPI snapshot for
{{unomi-rest-api-tests}} / UI docs.
h3. Compatibility tests
* [ ] Existing REST integration tests pass (no wire regressions).
* [ ] Swagger UI usable on a full distribution for spike endpoints.
----
h2. Spike (Phase 0) - immediate implementation target
# Hand-written or Mojo-generated JSON for tenants/tasks.
# {{UnomiJsonDocumentationProvider}} + customizer schema merge.
# Verify live {{GET /cxs/openapi.json}}: summaries, param docs, {{TenantUsage}}
{{$ref}}, purge 200/400/404/500.
# Write short effort estimate for rolling Phases 1-4 into 3.1 vs 3.2.
# Follow with Phase 0b: health servlet pack merged into the same live OpenAPI.
----
h2. References
* {{RestServer.java}} — {{OpenApiFeature}} / {{OpenApiCustomizer}}
* CXF {{OpenApiCustomizer}} + {{DocumentationProvider}} — use SPI, *not* HTML
{{JavaDocProvider}}:
https://github.com/apache/cxf/blob/3.6.x-fixes/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizer.java
* {{TenantEndpoint.java}}, {{TaskEndpoint.java}}, tenant DTOs under
{{unomi-api}}
* {{HealthCheckServlet.java}}, {{HealthCheckService.java}},
{{HealthCheckResponse.java}} — {{/health/check}}
* {{web-servlets}} {{ContextServlet}} / {{EventsCollectorServlet}} /
{{ClientServlet}} — forwards to CXF
* {{GraphQLServlet}} / activator — {{/graphql}} (optional)
* {{server/unomi-rest-api-tests/unomi-openapi-3.1.0.json}}
* Endpoint inventory (attach): method, path, class, java_method, return,
module, file — ~181 rows
* {{rest/README.md}} — historical Miredot note on {{rest-documentation}} branch
----
h2. Sub-tasks (suggested)
# Define JSON pack schema + conventions ({{@unomi.status}}; {{type:
jaxrs|servlet}})
# Implement / evaluate JavaParser-based extraction Mojo
# Runtime {{DocumentationProvider}} + pack discovery
# {{OpenApiCustomizer}} schema/status merge (+ servlet path merge)
# Phase 0 spike (tenants/tasks)
# Phase 0b health servlet pack
# Phase 1 core {{rest}} coverage
# Phase 2-3 extensions (optional graphql pack)
# CI gates + snapshot regen + consumer docs
was:
h2. Summary
Enrich Apache Unomi's *live* OpenAPI document (CXF {{OpenApiFeature}}) with
high-quality documentation derived from *existing* Java + Javadoc—without
scraping HTML, without duplicating prose into Swagger
{{@Schema(description=...)}} annotations, and without losing
OSGi/dynamic-deployment accuracy.
Primary consumers: human developers (Swagger UI, manuals), {{unomi-ui}} /
{{inoyu-ui}}, OpenAPI-driven clients, and AI agents that need typed, linked
contracts (not empty {{application/json: {}}}/{{default}} responses).
Related (not a duplicate): [UNOMI-899] (replace jaxrs-analyzer / CXF OpenAPI
generation). That work covers *how* the live spec is generated; this ticket
covers *documentation completeness* of that generation.
----
h2. Problem
The live spec ({{GET /cxs/openapi.json}}) and committed snapshots under
{{unomi-rest-api-tests}} are path-complete but documentation-poor:
* Methods returning {{javax.ws.rs.core.Response}} often show empty response
bodies even when the wire JSON is a typed DTO (e.g. {{TenantUsage}},
{{TenantEventPurgeResult}}).
* Responses are overwhelmingly a single {{default}} entry; named
{{200}}/{{400}}/{{401}}/{{404}}/{{500}} are missing.
* Schemas that *are* emitted from concrete return types have property
names/types but **no** field descriptions (Javadoc is not available at runtime
in CXF/swagger-core).
* Stock CXF {{JavaDocProvider}} scrapes *generated Javadoc HTML*—brittle across
JDK versions and unsuitable as a long-term approach.
* Mass {{@Operation}}/{{@Schema(description=...)}} would *duplicate* existing
Javadoc and drift.
Historical note: Miredot previously gave build-time, Javadoc-based docs but did
not model Unomi's *dynamically deployed* JAX-RS surface well. CXF is correct
for "what this Karaf instance registered"; build-time tools are correct for
"what the sources say." We need both.
----
h2. Inventory baseline (server/unomi, JAX-RS)
Static scan of {{src/main/java}} ({{@Path}} + HTTP verbs; abstract router CRUD
expanded):
|| Metric || Count ||
| HTTP operations | *181* (180 unique method+path; {{GET /profiles/export}}
overloaded twice) |
| Returning {{Response}} | *31* (highest schema risk) |
| JAX-RS resource classes | ~28 |
| Maven REST-ish modules | 8 |
|| Path prefix || Ops || Module ||
| {{/profiles}} (+ personas, sessions, properties, export) | 37 | {{rest}} |
| {{/definitions}} | 16 | {{rest}} |
| {{/campaigns}}, {{/rules}}, {{/segments}}, {{/privacy}} | 10 each | {{rest}}
/ privacy |
| {{/tenants}} | 9 | {{rest}} (3.1) |
| {{/scoring}} | 8 | {{rest}} |
| {{/goals}} | 7 | {{rest}} |
| {{/jsonSchema}} | 6 | json-schema ext |
| {{/lists}} + {{/userList}} | 5+1 | lists-ext + {{rest}} |
| {{/tasks}} | 5 | {{rest}} (3.1) |
| {{/exportConfiguration}}, {{/importConfiguration}} | 5 each | router-rest |
| {{/geonames}} | 5 | geonames |
| {{/events}}, {{/query}}, {{/scopes}} | 4 each | {{rest}} |
| {{/context.json}}, {{/context.js}}, {{/eventcollector}}, {{/cluster}} | 3
each | {{rest}} |
| {{/client}}, {{/groovyActions}}, {{/sfdc}} | 2 each | rest / groovy /
salesforce |
| {{/patches}}, {{/test/ping}} | 1 each | {{rest}} |
|| Maven module || Ops ||
| {{rest}} | 141 |
| {{extensions/privacy-extension/rest}} | 10 |
| {{extensions/router/router-rest}} | 10 |
| {{extensions/json-schema/rest}} | 6 |
| {{extensions/geonames/rest}} | 5 |
| {{extensions/lists-extension/rest}} | 5 |
| {{extensions/groovy-actions/rest}} | 2 |
| {{extensions/salesforce-connector/rest}} | 2 |
*Not* JAX-RS (document separately): servlet {{GET /health/check}}
({{extensions/healthcheck}}).
Coverage denominator for this ticket: inventory rows matching JAX-RS ops / 181.
Progress = enrichment packs covering those ops.
----
h2. Goals
# Live OpenAPI remains *deployment-accurate* (CXF reflects OSGi-registered
resources only).
# Enrichment packs are generated at *build time* from Java + Javadoc (and
JAX-RS annotations)—*no HTML scrape*.
# Prose is *not* duplicated into Swagger description annotations; Javadoc stays
the human source of truth.
# OpenAPI becomes useful for humans *and* agents: summaries, param docs, named
statuses, {{$ref}} schemas, property descriptions, stable operation keys.
# Per-bundle packs so optional extensions contribute docs only when installed.
----
h2. Non-goals
* Mass {{@Schema(description=...)}} / {{@Operation(summary=...)}} copying
Javadoc.
* Using CXF {{JavaDocProvider}} HTML scraping in production.
* Replacing CXF {{OpenApiFeature}} with a build-time-only OpenAPI as the sole
live API.
* Breaking HTTP wire contracts (paths, methods, status semantics, JSON shapes).
* Forcing {{/health/check}} into CXF JAX-RS OpenAPI.
* Documenting GraphQL or non-REST surfaces in this ticket.
----
h2. Architecture
{code}
Maven (per JAX-RS module)
→ JavaParser (or equivalent) on .java sources
→ META-INF/unomi/openapi-doc.json (enrichment pack)
packaged in OSGi bundle
Runtime
→ Discover packs from *installed* bundles (BundleTracker /
ClassLoader.getResources)
→ UnomiJsonDocumentationProvider implements
org.apache.cxf.jaxrs.model.doc.DocumentationProvider
→ OpenApiCustomizer.setJavadocProvider(provider)
→ Subclass/customize OpenApiCustomizer for response $refs + schema property
docs
→ OpenApiFeature serves complete /cxs/openapi.json
{code}
*CXF {{DocumentationProvider}} SPI* (already on {{OpenApiCustomizer}}):
|| SPI || OpenAPI field ||
| {{getMethodDoc}} | {{operation.summary}} / description |
| {{getMethodParameterDoc}} | {{parameter.description}} |
| {{getMethodResponseDoc}} | success response description |
| {{getClassDoc}} | tag description (if {{replaceTags}}) |
That SPI alone does *not* fix empty {{Response}} schemas or DTO property
docs—those need the customizer merge from the same JSON.
----
h2. Build-time extraction (high quality)
*Dual extract* from the same sources:
# *Structure*: {{@Path}}, HTTP verb, {{@*Param}}, return types, generics
({{PartialList<T>}}, etc.), Jackson visibility.
# *Meaning*: class/method/field Javadoc, {{@param}} (match by *name*),
{{@return}}, {{@throws}}.
Optional thin machine tags *only* where inference is ambiguous (do not restate
essays), e.g.:
{code}
@unomi.status 200 {@link TenantUsage}
@unomi.status 404 empty
@unomi.status 400 unsupported period
{code}
Especially required for multi-status bodies (e.g. {{purgeTenantEvents}}: 200
and 500 share {{TenantEventPurgeResult}}).
JSON packs should carry OpenAPI-like fragments (operations + schemas with
property descriptions), keyed by stable {{GET /path}} and/or
{{className#methodName}}.
----
h2. Compatibility
Documentation-only (plus optional signature cleanups that preserve wire
behavior). Especially: do *not* collapse purge {{200}}/{{500}} into a single
success code.
----
h2. Phased delivery
|| Phase || Scope || Notes ||
| *0 - Spike* | {{TenantEndpoint}}, {{TaskEndpoint}}, {{TenantUsage}} / purge
DTOs | Must prove pipeline; early 3.1 candidate |
| *1 - Core* | Remaining {{rest}} module (~141 ops) | Largest surface |
| *2 - Common extensions* | privacy, lists, json-schema | Often installed |
| *3 - Optional extensions* | geonames, groovy, router, sfdc | Validates
dynamic discovery |
| *4 - Hardening* | CI gates, snapshot regen, UI consumer notes | Release
hygiene |
*Release intent:* Phase 0 should try to land in *3.1* if the spike succeeds;
Phases 1-4 may complete in 3.1 or slip to *3.2* without blocking spike
learnings. Update fixVersion(s) when decided.
----
h2. Acceptance criteria / definition of done
h3. Pipeline
* [ ] Maven Mojo (or equivalent) extracts JAX-RS + Javadoc into a versioned
JSON pack schema.
* [ ] Packs emitted as {{META-INF/unomi/openapi-doc.json}} (name TBD) inside
REST OSGi bundles.
* [ ] Runtime discovery loads packs only from *installed* bundles.
* [ ] {{DocumentationProvider}} implementation fills operation/param/response
*prose* from JSON.
* [ ] {{OpenApiCustomizer}} extension applies response {{$ref}}s and schema
property docs from the same JSON.
* [ ] Wired from {{RestServer}} (existing customizer).
h3. Coverage
* [ ] Inventory of public JAX-RS ops tracked (denominator ≈ 181); enrichment
coverage measurable per module.
* [ ] All {{Response}}-returning methods (31) have status + schema metadata in
packs.
* [ ] DTOs referenced by enriched responses appear under schemas with property
descriptions where Javadoc exists.
* [ ] {{/health/check}} documented outside CXF OpenAPI (or explicitly waived).
h3. Quality / CI
* [ ] Fail (or stage-warn→fail) if public {{@Path}} method lacks Javadoc
summary.
* [ ] Fail if {{@*Param}} lacks matching {{@param}}.
* [ ] Fail if {{Response}} method lacks status/schema map.
* [ ] Test/fixture: OpenAPI contains {{TenantUsage}} {{$ref}} and named
statuses for purge.
* [ ] Procedure to regenerate committed OpenAPI snapshot for
{{unomi-rest-api-tests}} / UI docs.
h3. Compatibility tests
* [ ] Existing REST integration tests pass (no wire regressions).
* [ ] Swagger UI usable on a full distribution for spike endpoints.
----
h2. Spike (Phase 0) - immediate implementation target
# Hand-written or Mojo-generated JSON for tenants/tasks.
# {{UnomiJsonDocumentationProvider}} + customizer schema merge.
# Verify live {{GET /cxs/openapi.json}}: summaries, param docs, {{TenantUsage}}
{{$ref}}, purge 200/400/404/500.
# Write short effort estimate for rolling Phases 1-4 into 3.1 vs 3.2.
----
h2. References
* {{RestServer.java}} — {{OpenApiFeature}} / {{OpenApiCustomizer}}
* CXF {{OpenApiCustomizer}} + {{DocumentationProvider}} — use SPI, *not* HTML
{{JavaDocProvider}}:
https://github.com/apache/cxf/blob/3.6.x-fixes/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizer.java
* {{TenantEndpoint.java}}, {{TaskEndpoint.java}}, tenant DTOs under
{{unomi-api}}
* {{server/unomi-rest-api-tests/unomi-openapi-3.1.0.json}}
* Endpoint inventory (attach): method, path, class, java_method, return,
module, file — ~181 rows
* {{rest/README.md}} — historical Miredot note on {{rest-documentation}} branch
----
h2. Sub-tasks (suggested)
# Define JSON pack schema + conventions ({{@unomi.status}})
# Implement / evaluate JavaParser-based extraction Mojo
# Runtime {{DocumentationProvider}} + pack discovery
# {{OpenApiCustomizer}} schema/status merge
# Phase 0 spike (tenants/tasks)
# Phase 1 core {{rest}} coverage
# Phase 2-3 extensions
# CI gates + snapshot regen + consumer docs
> Improve OpenAPI completeness with Swagger annotations
> -----------------------------------------------------
>
> Key: UNOMI-963
> URL: https://issues.apache.org/jira/browse/UNOMI-963
> Project: Apache Unomi
> Issue Type: Improvement
> Components: unomi(-core)
> Affects Versions: unomi-3.0.0, unomi-2.7.0, unomi-3.1.0
> Reporter: Serge Huber
> Assignee: Serge Huber
> Priority: Major
> Fix For: unomi-3.1.0
>
>
> h2. Summary
> Enrich Apache Unomi's *live* OpenAPI document (CXF {{OpenApiFeature}}) with
> high-quality documentation derived from *existing* Java + Javadoc—without
> scraping HTML, without duplicating prose into Swagger
> {{@Schema(description=...)}} annotations, and without losing
> OSGi/dynamic-deployment accuracy.
> Primary consumers: human developers (Swagger UI, manuals), {{unomi-ui}} /
> {{inoyu-ui}}, OpenAPI-driven clients, and AI agents that need typed, linked
> contracts (not empty {{application/json: {}}}/{{default}} responses).
> Related (not a duplicate): [UNOMI-899] (replace jaxrs-analyzer / CXF OpenAPI
> generation). That work covers *how* the live spec is generated; this ticket
> covers *documentation completeness* of that generation.
> ----
> h2. Problem
> The live spec ({{GET /cxs/openapi.json}}) and committed snapshots under
> {{unomi-rest-api-tests}} are path-complete but documentation-poor:
> * Methods returning {{javax.ws.rs.core.Response}} often show empty response
> bodies even when the wire JSON is a typed DTO (e.g. {{TenantUsage}},
> {{TenantEventPurgeResult}}).
> * Responses are overwhelmingly a single {{default}} entry; named
> {{200}}/{{400}}/{{401}}/{{404}}/{{500}} are missing.
> * Schemas that *are* emitted from concrete return types have property
> names/types but **no** field descriptions (Javadoc is not available at
> runtime in CXF/swagger-core).
> * Stock CXF {{JavaDocProvider}} scrapes *generated Javadoc HTML*—brittle
> across JDK versions and unsuitable as a long-term approach.
> * Mass {{@Operation}}/{{@Schema(description=...)}} would *duplicate* existing
> Javadoc and drift.
> Historical note: Miredot previously gave build-time, Javadoc-based docs but
> did not model Unomi's *dynamically deployed* JAX-RS surface well. CXF is
> correct for "what this Karaf instance registered"; build-time tools are
> correct for "what the sources say." We need both.
> ----
> h2. Inventory baseline (server/unomi, JAX-RS)
> Static scan of {{src/main/java}} ({{@Path}} + HTTP verbs; abstract router
> CRUD expanded):
> || Metric || Count ||
> | HTTP operations | *181* (180 unique method+path; {{GET /profiles/export}}
> overloaded twice) |
> | Returning {{Response}} | *31* (highest schema risk) |
> | JAX-RS resource classes | ~28 |
> | Maven REST-ish modules | 8 |
> || Path prefix || Ops || Module ||
> | {{/profiles}} (+ personas, sessions, properties, export) | 37 | {{rest}} |
> | {{/definitions}} | 16 | {{rest}} |
> | {{/campaigns}}, {{/rules}}, {{/segments}}, {{/privacy}} | 10 each |
> {{rest}} / privacy |
> | {{/tenants}} | 9 | {{rest}} (3.1) |
> | {{/scoring}} | 8 | {{rest}} |
> | {{/goals}} | 7 | {{rest}} |
> | {{/jsonSchema}} | 6 | json-schema ext |
> | {{/lists}} + {{/userList}} | 5+1 | lists-ext + {{rest}} |
> | {{/tasks}} | 5 | {{rest}} (3.1) |
> | {{/exportConfiguration}}, {{/importConfiguration}} | 5 each | router-rest |
> | {{/geonames}} | 5 | geonames |
> | {{/events}}, {{/query}}, {{/scopes}} | 4 each | {{rest}} |
> | {{/context.json}}, {{/context.js}}, {{/eventcollector}}, {{/cluster}} | 3
> each | {{rest}} |
> | {{/client}}, {{/groovyActions}}, {{/sfdc}} | 2 each | rest / groovy /
> salesforce |
> | {{/patches}}, {{/test/ping}} | 1 each | {{rest}} |
> || Maven module || Ops ||
> | {{rest}} | 141 |
> | {{extensions/privacy-extension/rest}} | 10 |
> | {{extensions/router/router-rest}} | 10 |
> | {{extensions/json-schema/rest}} | 6 |
> | {{extensions/geonames/rest}} | 5 |
> | {{extensions/lists-extension/rest}} | 5 |
> | {{extensions/groovy-actions/rest}} | 2 |
> | {{extensions/salesforce-connector/rest}} | 2 |
> Coverage denominator for *JAX-RS*: inventory rows / 181. Progress =
> enrichment packs covering those ops.
> Separate *servlet* denominator (see below): at least {{GET /health/check}};
> optional {{/graphql}}. Do *not* double-count {{web-servlets}}
> context/eventcollector/client forwards already covered under {{/cxs}} JAX-RS.
> ----
> h2. Goals
> # Live OpenAPI remains *deployment-accurate* (CXF reflects OSGi-registered
> resources only).
> # Enrichment packs are generated at *build time* from Java + Javadoc (and
> JAX-RS annotations)—*no HTML scrape*.
> # Prose is *not* duplicated into Swagger description annotations; Javadoc
> stays the human source of truth.
> # OpenAPI becomes useful for humans *and* agents: summaries, param docs,
> named statuses, {{$ref}} schemas, property descriptions, stable operation
> keys.
> # Per-bundle packs so optional extensions contribute docs only when installed.
> ----
> h2. Non-goals
> * Mass {{@Schema(description=...)}} / {{@Operation(summary=...)}} copying
> Javadoc.
> * Using CXF {{JavaDocProvider}} HTML scraping in production.
> * Replacing CXF {{OpenApiFeature}} with a build-time-only OpenAPI as the sole
> live API.
> * Breaking HTTP wire contracts (paths, methods, status semantics, JSON
> shapes).
> * Expecting CXF to *discover* HttpService / whiteboard servlets automatically
> (it cannot).
> * Turning GraphQL into a full REST OpenAPI in Phase 0 (optional later pack or
> link to GraphQL schema).
> * Re-documenting {{web-servlets}} {{/context.json}}, {{/context.js}},
> {{/eventcollector}}, {{/client/*}} as separate APIs when the same contracts
> already exist under {{/cxs}} JAX-RS (optional note about forward/alias only).
> ----
> h2. Architecture
> {code}
> Maven (per JAX-RS module)
> → JavaParser (or equivalent) on .java sources
> → META-INF/unomi/openapi-doc.json (enrichment pack)
> packaged in OSGi bundle
> Runtime
> → Discover packs from *installed* bundles (BundleTracker /
> ClassLoader.getResources)
> → UnomiJsonDocumentationProvider implements
> org.apache.cxf.jaxrs.model.doc.DocumentationProvider
> → OpenApiCustomizer.setJavadocProvider(provider)
> → Subclass/customize OpenApiCustomizer for response $refs + schema property
> docs
> → OpenApiFeature serves complete /cxs/openapi.json
> {code}
> *CXF {{DocumentationProvider}} SPI* (already on {{OpenApiCustomizer}}):
> || SPI || OpenAPI field ||
> | {{getMethodDoc}} | {{operation.summary}} / description |
> | {{getMethodParameterDoc}} | {{parameter.description}} |
> | {{getMethodResponseDoc}} | success response description |
> | {{getClassDoc}} | tag description (if {{replaceTags}}) |
> That SPI alone does *not* fix empty {{Response}} schemas or DTO property
> docs—those need the customizer merge from the same JSON.
> ----
> h2. Servlets and other non-JAX-RS HTTP surfaces
> CXF OpenAPI only sees OSGi JAX-RS resources. Unomi also exposes (or forwards)
> HTTP via servlets:
> || Path || Implementation || OpenAPI approach ||
> | {{GET /health/check}} | {{HealthCheckServlet}} via OSGi {{HttpService}}
> ({{extensions/healthcheck}}) | *In scope*: servlet enrichment pack +
> customizer merge |
> | {{/graphql}} | {{GraphQLServlet}} | Optional later; prefer GraphQL schema
> or a small servlet pack |
> | {{/context.json}}, {{/context.js}}, {{/eventcollector}}, {{/client/*}} |
> {{web-servlets}} whiteboard → forward to CXF | Already documented as JAX-RS
> under {{/cxs}}; no duplicate paths unless documenting the servlet alias |
> *Health contract today (from implementation):*
> * Auth: OSGi role {{health}} (missing → *403*).
> * Body: JSON array of {{HealthCheckResponse}}.
> * Status: *200* if all checks live; *206 Partial Content* otherwise.
> *How to include them in the live OpenAPI (recommended):*
> # Build-time (or curated) pack, e.g. {{META-INF/unomi/openapi-servlet.json}}
> or a {{type: servlet}} section in the same enrichment format—absolute paths,
> not under {{/cxs}}.
> # Same runtime discovery as JAX-RS packs (only if the health bundle is
> installed).
> # {{OpenApiCustomizer}} *merges* servlet paths/schemas into the CXF
> {{OpenAPI}} model (tags: {{Health}}, etc.).
> # Document security explicitly (health role ≠ tenant API key).
> # OpenAPI {{servers}}: account for root vs {{/cxs}} (e.g. server URL {{/}}
> with absolute {{/health/check}}, or dual servers). Agents care that
> documented URLs are reachable as written.
> *Alternatives considered (not preferred for 3.1 spike):*
> * Thin JAX-RS façade {{GET /cxs/health}} duplicating the servlet (second URL;
> keep servlet canonical for ops).
> * Completely separate {{/health/openapi.json}} (extra consumer wiring).
> ----
> h2. Build-time extraction (high quality)
> *Dual extract* from the same sources:
> # *Structure*: {{@Path}}, HTTP verb, {{@*Param}}, return types, generics
> ({{PartialList<T>}}, etc.), Jackson visibility.
> # *Meaning*: class/method/field Javadoc, {{@param}} (match by *name*),
> {{@return}}, {{@throws}}.
> Optional thin machine tags *only* where inference is ambiguous (do not
> restate essays), e.g.:
> {code}
> @unomi.status 200 {@link TenantUsage}
> @unomi.status 404 empty
> @unomi.status 400 unsupported period
> {code}
> Especially required for multi-status bodies (e.g. {{purgeTenantEvents}}: 200
> and 500 share {{TenantEventPurgeResult}}).
> JSON packs should carry OpenAPI-like fragments (operations + schemas with
> property descriptions), keyed by stable {{GET /path}} and/or
> {{className#methodName}}.
> ----
> h2. Compatibility
> Documentation-only (plus optional signature cleanups that preserve wire
> behavior). Especially: do *not* collapse purge {{200}}/{{500}} into a single
> success code.
> ----
> h2. Phased delivery
> || Phase || Scope || Notes ||
> | *0 - Spike* | {{TenantEndpoint}}, {{TaskEndpoint}}, {{TenantUsage}} / purge
> DTOs | Must prove JAX-RS pipeline; early 3.1 candidate |
> | *0b - Servlet pack* | {{GET /health/check}} + {{HealthCheckResponse}}
> schema | Prove servlet merge + dual path prefix ({{/}} vs {{/cxs}}) |
> | *1 - Core* | Remaining {{rest}} module (~141 ops) | Largest surface |
> | *2 - Common extensions* | privacy, lists, json-schema | Often installed |
> | *3 - Optional extensions* | geonames, groovy, router, sfdc; optional
> {{/graphql}} pack | Validates dynamic discovery |
> | *4 - Hardening* | CI gates, snapshot regen, UI consumer notes | Release
> hygiene |
> *Release intent:* Phase 0 should try to land in *3.1* if the spike succeeds;
> Phases 1-4 may complete in 3.1 or slip to *3.2* without blocking spike
> learnings. Update fixVersion(s) when decided.
> ----
> h2. Acceptance criteria / definition of done
> h3. Pipeline
> * [ ] Maven Mojo (or equivalent) extracts JAX-RS + Javadoc into a versioned
> JSON pack schema.
> * [ ] Packs emitted as {{META-INF/unomi/openapi-doc.json}} (name TBD) inside
> REST OSGi bundles.
> * [ ] Runtime discovery loads packs only from *installed* bundles.
> * [ ] {{DocumentationProvider}} implementation fills operation/param/response
> *prose* from JSON.
> * [ ] {{OpenApiCustomizer}} extension applies response {{$ref}}s and schema
> property docs from the same JSON.
> * [ ] Wired from {{RestServer}} (existing customizer).
> h3. Coverage
> * [ ] Inventory of public JAX-RS ops tracked (denominator ≈ 181); enrichment
> coverage measurable per module.
> * [ ] All {{Response}}-returning methods (31) have status + schema metadata
> in packs.
> * [ ] DTOs referenced by enriched responses appear under schemas with
> property descriptions where Javadoc exists.
> * [ ] {{GET /health/check}} present in live OpenAPI when health bundle is
> installed (servlet pack merge).
> * [ ] {{HealthCheckResponse}} (and array response) documented with statuses
> 200 / 206 / 403 and health-role security.
> * [ ] No duplicate OpenAPI paths for {{web-servlets}} forwards that already
> exist under {{/cxs}} JAX-RS (unless explicitly documenting aliases).
> h3. Quality / CI
> * [ ] Fail (or stage-warn→fail) if public {{@Path}} method lacks Javadoc
> summary.
> * [ ] Fail if {{@*Param}} lacks matching {{@param}}.
> * [ ] Fail if {{Response}} method lacks status/schema map.
> * [ ] Test/fixture: OpenAPI contains {{TenantUsage}} {{$ref}} and named
> statuses for purge.
> * [ ] Procedure to regenerate committed OpenAPI snapshot for
> {{unomi-rest-api-tests}} / UI docs.
> h3. Compatibility tests
> * [ ] Existing REST integration tests pass (no wire regressions).
> * [ ] Swagger UI usable on a full distribution for spike endpoints.
> ----
> h2. Spike (Phase 0) - immediate implementation target
> # Hand-written or Mojo-generated JSON for tenants/tasks.
> # {{UnomiJsonDocumentationProvider}} + customizer schema merge.
> # Verify live {{GET /cxs/openapi.json}}: summaries, param docs,
> {{TenantUsage}} {{$ref}}, purge 200/400/404/500.
> # Write short effort estimate for rolling Phases 1-4 into 3.1 vs 3.2.
> # Follow with Phase 0b: health servlet pack merged into the same live OpenAPI.
> ----
> h2. References
> * {{RestServer.java}} — {{OpenApiFeature}} / {{OpenApiCustomizer}}
> * CXF {{OpenApiCustomizer}} + {{DocumentationProvider}} — use SPI, *not* HTML
> {{JavaDocProvider}}:
> https://github.com/apache/cxf/blob/3.6.x-fixes/rt/rs/description-openapi-v3/src/main/java/org/apache/cxf/jaxrs/openapi/OpenApiCustomizer.java
> * {{TenantEndpoint.java}}, {{TaskEndpoint.java}}, tenant DTOs under
> {{unomi-api}}
> * {{HealthCheckServlet.java}}, {{HealthCheckService.java}},
> {{HealthCheckResponse.java}} — {{/health/check}}
> * {{web-servlets}} {{ContextServlet}} / {{EventsCollectorServlet}} /
> {{ClientServlet}} — forwards to CXF
> * {{GraphQLServlet}} / activator — {{/graphql}} (optional)
> * {{server/unomi-rest-api-tests/unomi-openapi-3.1.0.json}}
> * Endpoint inventory (attach): method, path, class, java_method, return,
> module, file — ~181 rows
> * {{rest/README.md}} — historical Miredot note on {{rest-documentation}}
> branch
> ----
> h2. Sub-tasks (suggested)
> # Define JSON pack schema + conventions ({{@unomi.status}}; {{type:
> jaxrs|servlet}})
> # Implement / evaluate JavaParser-based extraction Mojo
> # Runtime {{DocumentationProvider}} + pack discovery
> # {{OpenApiCustomizer}} schema/status merge (+ servlet path merge)
> # Phase 0 spike (tenants/tasks)
> # Phase 0b health servlet pack
> # Phase 1 core {{rest}} coverage
> # Phase 2-3 extensions (optional graphql pack)
> # CI gates + snapshot regen + consumer docs
--
This message was sent by Atlassian Jira
(v8.20.10#820010)