[
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}} / {{GET
/cxs/openapi.json}} + Swagger UI) so developers get usable statuses, examples,
and *wire-accurate* schemas—without changing REST behavior.
*Approach (decided):* treat *Javadoc* as the source of truth. At build time,
the external toolkit
*[https://repo1.maven.org/maven2/dev/inoyu/openapi/|dev.inoyu.openapi]
openapi-enrich {{0.1.0}}* (Maven Central) scans sources into enrichment packs;
at runtime CXF merges them. Optional thin machine tags {{@api.status}} /
{{@api.example}} where inference needs help.
*Toolkit provenance:* *openapi-enrich* was *designed and developed for this
Unomi need* (Javadoc-driven enrichment of a CXF/OSGi live OpenAPI, without mass
Swagger annotations) and *open-sourced by [https://inoyu.dev|Inoyu]* under
Apache-2.0 on Maven Central under the {{dev.inoyu.openapi}} coordinates. Unomi
depends on the published artifacts as a normal third-party library—it does
*not* vendor the toolkit sources.
*Toolkit links:*
* GitHub: [https://github.com/inoyu-dev/openapi-enrich]
* README (usage, {{@api.status}} / {{@api.example}} grammar, pack format):
[https://github.com/inoyu-dev/openapi-enrich/blob/main/README.md]
* Maven Central: [https://repo1.maven.org/maven2/dev/inoyu/openapi/]
*Explicitly not used:* Swagger/OpenAPI annotations such as {{@Operation}},
{{@ApiResponse}}, {{@Schema(description=...)}}. Those would *duplicate*
existing Javadoc, drift from code, and fight Unomi's OSGi/dynamic surface. (The
issue *summary* previously mentioned Swagger annotations; that path was
rejected.)
*Primary consumers:* integration engineers (Swagger UI, curl/Postman), OpenAPI
codegen / typed clients, {{unomi-ui}} / tooling, AI agents that need a real
contract (not empty {{application/json: {}}}/{{default}} responses).
*Related (not a duplicate):* [UNOMI-899] (replace jaxrs-analyzer / CXF OpenAPI
*generation*). That covers *how* the live spec is produced; this ticket covers
*documentation completeness* of that generation.
*PR:* [https://github.com/apache/unomi/pull/826|#826] — branch
{{UNOMI-963-openapi-javadoc-enrichment}}
----
h2. Problem
The live spec was path-complete but documentation-poor:
* Methods returning {{javax.ws.rs.core.Response}} often showed empty bodies
even when the wire JSON is a typed DTO.
* Responses were overwhelmingly a single {{default}} entry; named
{{200}}/{{204}}/{{400}}/{{403}}/{{404}}… were missing.
* Schemas often lacked field descriptions, and sometimes leaked Java/transient
names instead of *wire* JSON (e.g. Condition {{type}} + {{parameterValues}} vs
internal ids).
* Stock CXF {{JavaDocProvider}} scrapes *generated Javadoc HTML*—brittle across
JDKs.
* Mass Swagger annotations would duplicate Javadoc and drift.
----
h2. Goals
# Live OpenAPI remains *deployment-accurate* (CXF reflects OSGi-registered
resources only).
# Enrichment packs generated at *build time* from Java + Javadoc (+ JAX-RS
annotations)—*no HTML scrape*, *no Swagger annotation dual-maint*.
# Prose stays in Javadoc; thin {{@api.*}} tags only where status/example/schema
binding needs an explicit signal.
# OpenAPI useful for humans *and* agents: summaries, params, named statuses,
examples, {{$ref}} schemas, stable operation keys, wire names.
# Per-bundle packs so optional extensions contribute docs only when installed.
----
h2. Non-goals
* Mass {{@Schema(description=...)}} / {{@Operation(summary=...)}} /
{{@ApiResponse}} copying Javadoc (*rejected*).
* 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).
* Enriching {{GET /api-docs/{resource}}} (Swagger helper, not a product API).
----
h2. Architecture (as implemented)
{code}
Maven (rest + extension REST modules)
→ openapi-enrich-maven-plugin (dev.inoyu.openapi:0.1.0)
→ META-INF/openapi-enrich/openapi-doc.json (pack)
packaged in OSGi bundle
Runtime
→ BundleContextDocPackLoader discovers packs from *installed* bundles
→ EnrichingOpenApiCustomizer merges ops/schemas/examples into CXF OpenAPI
→ OpenApiFeature serves complete /cxs/openapi.json
{code}
Unomi *wires* the toolkit and supplies Javadoc content; correctness (aliases,
{{@XmlTransient}} suppress, inherited JAX-RS methods, path-param regex
normalize, subclass override docs) lives in the Central library maintained by
Inoyu.
Servlet surface in scope: {{GET /health/check}} via servlet pack merge
(healthcheck module).
----
----
h2. Gold coverage (what it means + how much work)
h3. What "gold" means
We score each HTTP operation in the live {{/cxs/openapi.json}} as:
|| Tier || Criteria ||
| *Gold* | Has a *named* success/error status (e.g. {{200}}/{{204}}/{{400}})
whose description is *not* empty/{{default response}}, *and* the operation
carries at least one response *example* |
| *Partial* | Has statuses *or* an example, but not both to gold standard |
| *Weak* | Neither usable statuses nor examples |
That bar is what makes Swagger UI and codegen useful: developers see *what can
happen* and *what a valid body looks like*, not a CXF skeleton.
h3. Result on this branch
* *179 / 179* operations *gold* (excluding {{GET /api-docs/{resource}}},
intentionally skipped — Swagger helper, not a product API).
* *0* partial, *0* weak on the product surface after the final example pass.
* Critical schemas verified: Condition/Action expose wire {{type}} +
{{parameterValues}}; Event exposes {{profileId}}/{{sessionId}} (not leaked
nested/transient fields).
h3. Work that got us there
Not a thin status pass — end-to-end enrichment of the JAX-RS inventory (~180
ops) plus schema quality:
# *Toolkit (Inoyu, open-sourced):* openapi-enrich {{0.1.0}} — scan/Javadoc
tags, schema inheritance, wire aliases, {{@XmlTransient}} suppress, HTTP
overloads, inherited JAX-RS methods, path-param regex normalize, subclass
override docs, servlet merge — published to Maven Central so Unomi (and CI)
resolve a release, not a SNAPSHOT.
# *Exemplar content:* Tenant + Health to prove the pipeline and tag grammar.
# *Hot paths:* context, eventcollector, profiles (36 ops), rules, segments,
privacy — statuses, examples, shared models (Query, PartialList,
Condition/Action, …).
# *Mid + thin REST + extensions:* definitions, campaigns, scoring, goals,
events, tasks, scopes, query, cluster, client, patch, userList, test/ping, plus
json-schema, lists, geonames, router import/export, groovy, SFDC, healthcheck.
# *Gold completion pass:* every remaining partial (void DELETE/OPTIONS/thin
GETs, geonames regex-path ops, query metrics) got *context-specific*
{{@api.example}} values (entity illustrations, JS for {{context.js}}, CSV for
export, {{true}}/maps where appropriate)—so "examples everywhere" is
intentional and domain-relevant, not filler.
*Effort order of magnitude (tracker planning bands):* toolkit MVP roughly *4–8*
person-days; Unomi hot-path content roughly *4–7* person-days; full
Tenant-quality inventory across mid/thin/extensions a further multi-day content
push on top—plus the final gold example sweep after live scorecards.
----
h2. Delivery status (2026-07-15)
|| Area || Status ||
| Toolkit published Central {{0.1.0}} | Done |
| Unomi pin {{${openapi.enrich.version}=0.1.0}} | Done (PR #826) |
| Hot paths (context, events, profiles, rules, segments, privacy) | Done |
| Mid / thin REST + extensions (incl. geonames, lists, json-schema, router,
groovy, SFDC, health) | Done |
| Live verify | *179/179* **gold** excl. {{/api-docs}} — see *Gold coverage*
above for definition + work scope |
| ASF review | PR [https://github.com/apache/unomi/pull/826|#826] open |
----
h2. Acceptance criteria
h3. Pipeline
* [x] Maven Mojo extracts JAX-RS + Javadoc into versioned JSON packs (format
{{1.1}}).
* [x] Packs at {{META-INF/openapi-enrich/openapi-doc.json}} in REST OSGi
bundles.
* [x] Runtime discovery loads packs only from *installed* bundles.
* [x] {{OpenApiCustomizer}} merge for statuses, examples, schema property docs,
response {{$ref}}s.
* [x] Wired from {{RestServer}}; dependency on Central {{0.1.0}} (not SNAPSHOT).
h3. Coverage / quality
* [x] Core + common/optional extension REST inventory enriched (excl.
{{/api-docs}}).
* [x] {{Response}}/multi-status methods have named statuses where realistic.
* [x] Key DTOs (Profile, Event, Condition/Action, Query, PartialList, Tenant,
…) documented; wire aliases / transient suppress.
* [x] {{GET /health/check}} documented when health bundle installed.
* [x] No Swagger annotation dual-maint; Javadoc + {{@api.status}} /
{{@api.example}} only.
h3. Compatibility
* [x] Documentation-only (no intentional wire/behavior change).
* [ ] CI green on PR #826; REST ITs unchanged aside from build resolving
Central artifacts.
----
h2. References
* PR: [https://github.com/apache/unomi/pull/826]
* Toolkit:
[https://repo1.maven.org/maven2/dev/inoyu/openapi/cxf-openapi-enricher/0.1.0/]
* {{RestServer.java}} — OpenApiFeature / EnrichingOpenApiCustomizer
* Exemplars: {{TenantEndpoint}}, {{ContextJsonEndpoint}},
{{RulesServiceEndPoint}}, {{HealthCheckServlet}}
* Related: [UNOMI-899]
* Toolkit GitHub: [https://github.com/inoyu-dev/openapi-enrich]
* Toolkit README:
[https://github.com/inoyu-dev/openapi-enrich/blob/main/README.md]
* Toolkit on Central:
[https://repo1.maven.org/maven2/dev/inoyu/openapi/cxf-openapi-enricher/0.1.0/]
* Toolkit author/publisher: [https://inoyu.dev|Inoyu] (Apache-2.0)
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 |
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
> Improve OpenAPI completeness from Javadoc (openapi-enrich)
> ----------------------------------------------------------
>
> 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
>
> Time Spent: 20m
> Remaining Estimate: 0h
>
> h2. Summary
> Enrich Apache Unomi's *live* OpenAPI document (CXF {{OpenApiFeature}} / {{GET
> /cxs/openapi.json}} + Swagger UI) so developers get usable statuses,
> examples, and *wire-accurate* schemas—without changing REST behavior.
> *Approach (decided):* treat *Javadoc* as the source of truth. At build time,
> the external toolkit
> *[https://repo1.maven.org/maven2/dev/inoyu/openapi/|dev.inoyu.openapi]
> openapi-enrich {{0.1.0}}* (Maven Central) scans sources into enrichment
> packs; at runtime CXF merges them. Optional thin machine tags {{@api.status}}
> / {{@api.example}} where inference needs help.
> *Toolkit provenance:* *openapi-enrich* was *designed and developed for this
> Unomi need* (Javadoc-driven enrichment of a CXF/OSGi live OpenAPI, without
> mass Swagger annotations) and *open-sourced by [https://inoyu.dev|Inoyu]*
> under Apache-2.0 on Maven Central under the {{dev.inoyu.openapi}}
> coordinates. Unomi depends on the published artifacts as a normal third-party
> library—it does *not* vendor the toolkit sources.
> *Toolkit links:*
> * GitHub: [https://github.com/inoyu-dev/openapi-enrich]
> * README (usage, {{@api.status}} / {{@api.example}} grammar, pack format):
> [https://github.com/inoyu-dev/openapi-enrich/blob/main/README.md]
> * Maven Central: [https://repo1.maven.org/maven2/dev/inoyu/openapi/]
> *Explicitly not used:* Swagger/OpenAPI annotations such as {{@Operation}},
> {{@ApiResponse}}, {{@Schema(description=...)}}. Those would *duplicate*
> existing Javadoc, drift from code, and fight Unomi's OSGi/dynamic surface.
> (The issue *summary* previously mentioned Swagger annotations; that path was
> rejected.)
> *Primary consumers:* integration engineers (Swagger UI, curl/Postman),
> OpenAPI codegen / typed clients, {{unomi-ui}} / tooling, AI agents that need
> a real contract (not empty {{application/json: {}}}/{{default}} responses).
> *Related (not a duplicate):* [UNOMI-899] (replace jaxrs-analyzer / CXF
> OpenAPI *generation*). That covers *how* the live spec is produced; this
> ticket covers *documentation completeness* of that generation.
> *PR:* [https://github.com/apache/unomi/pull/826|#826] — branch
> {{UNOMI-963-openapi-javadoc-enrichment}}
> ----
> h2. Problem
> The live spec was path-complete but documentation-poor:
> * Methods returning {{javax.ws.rs.core.Response}} often showed empty bodies
> even when the wire JSON is a typed DTO.
> * Responses were overwhelmingly a single {{default}} entry; named
> {{200}}/{{204}}/{{400}}/{{403}}/{{404}}… were missing.
> * Schemas often lacked field descriptions, and sometimes leaked
> Java/transient names instead of *wire* JSON (e.g. Condition {{type}} +
> {{parameterValues}} vs internal ids).
> * Stock CXF {{JavaDocProvider}} scrapes *generated Javadoc HTML*—brittle
> across JDKs.
> * Mass Swagger annotations would duplicate Javadoc and drift.
> ----
> h2. Goals
> # Live OpenAPI remains *deployment-accurate* (CXF reflects OSGi-registered
> resources only).
> # Enrichment packs generated at *build time* from Java + Javadoc (+ JAX-RS
> annotations)—*no HTML scrape*, *no Swagger annotation dual-maint*.
> # Prose stays in Javadoc; thin {{@api.*}} tags only where
> status/example/schema binding needs an explicit signal.
> # OpenAPI useful for humans *and* agents: summaries, params, named statuses,
> examples, {{$ref}} schemas, stable operation keys, wire names.
> # Per-bundle packs so optional extensions contribute docs only when installed.
> ----
> h2. Non-goals
> * Mass {{@Schema(description=...)}} / {{@Operation(summary=...)}} /
> {{@ApiResponse}} copying Javadoc (*rejected*).
> * 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).
> * Enriching {{GET /api-docs/{resource}}} (Swagger helper, not a product API).
> ----
> h2. Architecture (as implemented)
> {code}
> Maven (rest + extension REST modules)
> → openapi-enrich-maven-plugin (dev.inoyu.openapi:0.1.0)
> → META-INF/openapi-enrich/openapi-doc.json (pack)
> packaged in OSGi bundle
> Runtime
> → BundleContextDocPackLoader discovers packs from *installed* bundles
> → EnrichingOpenApiCustomizer merges ops/schemas/examples into CXF OpenAPI
> → OpenApiFeature serves complete /cxs/openapi.json
> {code}
> Unomi *wires* the toolkit and supplies Javadoc content; correctness (aliases,
> {{@XmlTransient}} suppress, inherited JAX-RS methods, path-param regex
> normalize, subclass override docs) lives in the Central library maintained by
> Inoyu.
> Servlet surface in scope: {{GET /health/check}} via servlet pack merge
> (healthcheck module).
> ----
> ----
> h2. Gold coverage (what it means + how much work)
> h3. What "gold" means
> We score each HTTP operation in the live {{/cxs/openapi.json}} as:
> || Tier || Criteria ||
> | *Gold* | Has a *named* success/error status (e.g. {{200}}/{{204}}/{{400}})
> whose description is *not* empty/{{default response}}, *and* the operation
> carries at least one response *example* |
> | *Partial* | Has statuses *or* an example, but not both to gold standard |
> | *Weak* | Neither usable statuses nor examples |
> That bar is what makes Swagger UI and codegen useful: developers see *what
> can happen* and *what a valid body looks like*, not a CXF skeleton.
> h3. Result on this branch
> * *179 / 179* operations *gold* (excluding {{GET /api-docs/{resource}}},
> intentionally skipped — Swagger helper, not a product API).
> * *0* partial, *0* weak on the product surface after the final example pass.
> * Critical schemas verified: Condition/Action expose wire {{type}} +
> {{parameterValues}}; Event exposes {{profileId}}/{{sessionId}} (not leaked
> nested/transient fields).
> h3. Work that got us there
> Not a thin status pass — end-to-end enrichment of the JAX-RS inventory (~180
> ops) plus schema quality:
> # *Toolkit (Inoyu, open-sourced):* openapi-enrich {{0.1.0}} — scan/Javadoc
> tags, schema inheritance, wire aliases, {{@XmlTransient}} suppress, HTTP
> overloads, inherited JAX-RS methods, path-param regex normalize, subclass
> override docs, servlet merge — published to Maven Central so Unomi (and CI)
> resolve a release, not a SNAPSHOT.
> # *Exemplar content:* Tenant + Health to prove the pipeline and tag grammar.
> # *Hot paths:* context, eventcollector, profiles (36 ops), rules, segments,
> privacy — statuses, examples, shared models (Query, PartialList,
> Condition/Action, …).
> # *Mid + thin REST + extensions:* definitions, campaigns, scoring, goals,
> events, tasks, scopes, query, cluster, client, patch, userList, test/ping,
> plus json-schema, lists, geonames, router import/export, groovy, SFDC,
> healthcheck.
> # *Gold completion pass:* every remaining partial (void DELETE/OPTIONS/thin
> GETs, geonames regex-path ops, query metrics) got *context-specific*
> {{@api.example}} values (entity illustrations, JS for {{context.js}}, CSV for
> export, {{true}}/maps where appropriate)—so "examples everywhere" is
> intentional and domain-relevant, not filler.
> *Effort order of magnitude (tracker planning bands):* toolkit MVP roughly
> *4–8* person-days; Unomi hot-path content roughly *4–7* person-days; full
> Tenant-quality inventory across mid/thin/extensions a further multi-day
> content push on top—plus the final gold example sweep after live scorecards.
> ----
> h2. Delivery status (2026-07-15)
> || Area || Status ||
> | Toolkit published Central {{0.1.0}} | Done |
> | Unomi pin {{${openapi.enrich.version}=0.1.0}} | Done (PR #826) |
> | Hot paths (context, events, profiles, rules, segments, privacy) | Done |
> | Mid / thin REST + extensions (incl. geonames, lists, json-schema, router,
> groovy, SFDC, health) | Done |
> | Live verify | *179/179* **gold** excl. {{/api-docs}} — see *Gold coverage*
> above for definition + work scope |
> | ASF review | PR [https://github.com/apache/unomi/pull/826|#826] open |
> ----
> h2. Acceptance criteria
> h3. Pipeline
> * [x] Maven Mojo extracts JAX-RS + Javadoc into versioned JSON packs (format
> {{1.1}}).
> * [x] Packs at {{META-INF/openapi-enrich/openapi-doc.json}} in REST OSGi
> bundles.
> * [x] Runtime discovery loads packs only from *installed* bundles.
> * [x] {{OpenApiCustomizer}} merge for statuses, examples, schema property
> docs, response {{$ref}}s.
> * [x] Wired from {{RestServer}}; dependency on Central {{0.1.0}} (not
> SNAPSHOT).
> h3. Coverage / quality
> * [x] Core + common/optional extension REST inventory enriched (excl.
> {{/api-docs}}).
> * [x] {{Response}}/multi-status methods have named statuses where realistic.
> * [x] Key DTOs (Profile, Event, Condition/Action, Query, PartialList, Tenant,
> …) documented; wire aliases / transient suppress.
> * [x] {{GET /health/check}} documented when health bundle installed.
> * [x] No Swagger annotation dual-maint; Javadoc + {{@api.status}} /
> {{@api.example}} only.
> h3. Compatibility
> * [x] Documentation-only (no intentional wire/behavior change).
> * [ ] CI green on PR #826; REST ITs unchanged aside from build resolving
> Central artifacts.
> ----
> h2. References
> * PR: [https://github.com/apache/unomi/pull/826]
> * Toolkit:
> [https://repo1.maven.org/maven2/dev/inoyu/openapi/cxf-openapi-enricher/0.1.0/]
> * {{RestServer.java}} — OpenApiFeature / EnrichingOpenApiCustomizer
> * Exemplars: {{TenantEndpoint}}, {{ContextJsonEndpoint}},
> {{RulesServiceEndPoint}}, {{HealthCheckServlet}}
> * Related: [UNOMI-899]
> * Toolkit GitHub: [https://github.com/inoyu-dev/openapi-enrich]
> * Toolkit README:
> [https://github.com/inoyu-dev/openapi-enrich/blob/main/README.md]
> * Toolkit on Central:
> [https://repo1.maven.org/maven2/dev/inoyu/openapi/cxf-openapi-enricher/0.1.0/]
> * Toolkit author/publisher: [https://inoyu.dev|Inoyu] (Apache-2.0)
--
This message was sent by Atlassian Jira
(v8.20.10#820010)