You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2026/07/14 21:19:07 UTC
(unomi) 01/01: UNOMI-963: Enrich OpenAPI docs from Javadoc across REST endpoints
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch UNOMI-963-openapi-javadoc-enrichment
in repository https://gitbox.apache.org/repos/asf/unomi.git
commit d746396b61eb9f3a357c03b5ce9479740bae604a
Author: Serge Huber <sh...@apache.org>
AuthorDate: Tue Jul 14 23:18:55 2026 +0200
UNOMI-963: Enrich OpenAPI docs from Javadoc across REST endpoints
Wire openapi-enrich into rest/extensions and add @api.status / @api.example
plus schema field docs so /cxs/openapi.json is Client/Swagger-ready (live gold
excl. /api-docs).
---
.../java/org/apache/unomi/api/BatchUpdate.java | 23 +++++
.../main/java/org/apache/unomi/api/Consent.java | 21 +++++
.../java/org/apache/unomi/api/ContextRequest.java | 51 ++++++++++-
.../java/org/apache/unomi/api/ContextResponse.java | 55 +++++++++++
api/src/main/java/org/apache/unomi/api/Event.java | 34 +++++++
.../apache/unomi/api/EventsCollectorRequest.java | 15 ++-
api/src/main/java/org/apache/unomi/api/Item.java | 33 +++++++
.../main/java/org/apache/unomi/api/Metadata.java | 40 ++++++++
.../java/org/apache/unomi/api/PartialList.java | 27 ++++++
.../org/apache/unomi/api/PersonaWithSessions.java | 6 ++
.../apache/unomi/api/PersonalizationResult.java | 15 ++-
.../main/java/org/apache/unomi/api/Profile.java | 22 +++++
.../java/org/apache/unomi/api/ProfileAlias.java | 16 ++++
.../java/org/apache/unomi/api/PropertyType.java | 47 ++++++++++
.../main/java/org/apache/unomi/api/ServerInfo.java | 5 +
.../main/java/org/apache/unomi/api/Session.java | 42 +++++++++
.../java/org/apache/unomi/api/actions/Action.java | 13 +++
.../org/apache/unomi/api/actions/ActionType.java | 3 +
.../org/apache/unomi/api/campaigns/Campaign.java | 6 ++
.../org/apache/unomi/api/conditions/Condition.java | 13 +++
.../apache/unomi/api/conditions/ConditionType.java | 3 +
.../main/java/org/apache/unomi/api/goals/Goal.java | 12 +++
.../java/org/apache/unomi/api/query/Query.java | 31 +++++++
.../main/java/org/apache/unomi/api/rules/Rule.java | 28 ++++++
.../org/apache/unomi/api/rules/RuleStatistics.java | 28 ++++++
.../unomi/api/segments/DependentMetadata.java | 10 ++
.../org/apache/unomi/api/segments/Scoring.java | 6 ++
.../apache/unomi/api/segments/ScoringElement.java | 6 ++
.../org/apache/unomi/api/segments/Segment.java | 6 ++
.../unomi/api/services/PersonalizationService.java | 53 ++++++++++-
.../java/org/apache/unomi/api/tenants/ApiKey.java | 32 +++----
.../unomi/api/tenants/ApiKeyCreationResult.java | 8 ++
.../java/org/apache/unomi/api/tenants/Tenant.java | 35 ++++---
.../unomi/api/tenants/TenantEventPurgeResult.java | 19 +++-
.../apache/unomi/api/tenants/TenantScopeUsage.java | 6 ++
.../org/apache/unomi/api/tenants/TenantStatus.java | 24 ++---
.../org/apache/unomi/api/tenants/TenantUsage.java | 21 ++++-
extensions/geonames/rest/pom.xml | 14 +++
.../unomi/geonames/rest/GeonamesEndPoint.java | 50 ++++++++++
extensions/groovy-actions/rest/pom.xml | 9 ++
.../groovy/actions/rest/GroovyActionsEndPoint.java | 17 +++-
extensions/healthcheck/pom.xml | 12 +++
.../unomi/healthcheck/HealthCheckResponse.java | 49 +++++++++-
.../healthcheck/servlet/HealthCheckServlet.java | 12 ++-
extensions/json-schema/rest/pom.xml | 9 ++
.../unomi/schema/rest/JsonSchemaEndPoint.java | 52 +++++++----
extensions/lists-extension/rest/pom.xml | 14 +++
.../unomi/lists/rest/UserListServiceEndPoint.java | 41 ++++++++-
extensions/privacy-extension/rest/pom.xml | 14 +++
.../unomi/privacy/rest/PrivacyServiceEndPoint.java | 100 +++++++++++++++++++-
extensions/router/router-rest/pom.xml | 9 ++
.../rest/AbstractConfigurationServiceEndpoint.java | 29 ++++--
.../rest/ExportConfigurationServiceEndPoint.java | 49 ++++++++--
.../rest/ImportConfigurationServiceEndPoint.java | 56 ++++++++++--
extensions/salesforce-connector/rest/pom.xml | 9 ++
.../org/apache/unomi/sfdc/rest/SFDCEndPoint.java | 17 ++++
pom.xml | 27 ++++++
rest/pom.xml | 48 ++++++++++
.../rest/endpoints/CampaignsServiceEndPoint.java | 31 +++++++
.../unomi/rest/endpoints/ClientEndpoint.java | 17 +++-
.../rest/endpoints/ClusterServiceEndPoint.java | 13 ++-
.../unomi/rest/endpoints/ContextJsonEndpoint.java | 90 ++++++++++++------
.../rest/endpoints/DefinitionsServiceEndPoint.java | 86 +++++++++++++++---
.../unomi/rest/endpoints/EventServiceEndpoint.java | 13 ++-
.../rest/endpoints/EventsCollectorEndpoint.java | 31 +++++--
.../unomi/rest/endpoints/GoalsServiceEndPoint.java | 23 +++++
.../unomi/rest/endpoints/PatchServiceEndPoint.java | 8 +-
.../rest/endpoints/ProfileServiceEndPoint.java | 101 ++++++++++++++++++++-
.../unomi/rest/endpoints/QueryServiceEndPoint.java | 16 +++-
.../unomi/rest/endpoints/RulesServiceEndPoint.java | 31 +++++++
.../unomi/rest/endpoints/ScopeServiceEndPoint.java | 11 ++-
.../rest/endpoints/ScoringServiceEndPoint.java | 38 ++++++--
.../rest/endpoints/SegmentServiceEndPoint.java | 52 ++++++++---
.../apache/unomi/rest/endpoints/TestEndPoint.java | 6 +-
.../rest/endpoints/UserListServiceEndPoint.java | 2 +
.../unomi/rest/models/EventCollectorResponse.java | 19 +---
.../apache/unomi/rest/scheduler/TaskEndpoint.java | 55 ++++++-----
.../org/apache/unomi/rest/server/RestServer.java | 7 +-
.../apache/unomi/rest/tenants/TenantEndpoint.java | 57 ++++++++++--
.../apache/unomi/rest/tenants/TenantRequest.java | 16 +++-
.../org/apache/unomi/tracing/api/TraceNode.java | 32 +++++++
81 files changed, 1960 insertions(+), 247 deletions(-)
diff --git a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java
index a68068ee3..3bef97927 100644
--- a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java
+++ b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java
@@ -26,11 +26,34 @@ import org.apache.unomi.api.conditions.Condition;
* applies a {@link PropertyMergeStrategyType} when merging complex fields.
*/
public class BatchUpdate {
+ /**
+ * Property to update, as an Apache Commons BeanUtils expression (for example {@code properties.email}).
+ * @api.example properties.email
+ */
private String propertyName;
+ /**
+ * New value to assign to the property.
+ * @api.example updated@example.com
+ */
private Object propertyValue;
+ /**
+ * Condition that items must match to be updated.
+ */
private Condition condition;
+ /**
+ * Optional property merge strategy name when updating complex fields.
+ * @api.example defaultPropertyMergeStrategy
+ */
private String strategy;
+ /**
+ * Scroll keep-alive window while iterating matches.
+ * @api.example 10m
+ */
private String scrollTimeValidity = "10m";
+ /**
+ * Number of matching items to process per scroll batch.
+ * @api.example 1000
+ */
private int scrollBatchSize = 1000;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/Consent.java b/api/src/main/java/org/apache/unomi/api/Consent.java
index 966c9a53d..5b16a4f7e 100644
--- a/api/src/main/java/org/apache/unomi/api/Consent.java
+++ b/api/src/main/java/org/apache/unomi/api/Consent.java
@@ -31,10 +31,30 @@ import java.util.Map;
*/
public class Consent implements Serializable {
+ /**
+ * Scope this consent applies to (often a site id).
+ * @api.example mysite
+ */
private String scope;
+ /**
+ * Consent type id defined outside Unomi (newsletter, tracking, ...).
+ * @api.example newsletter
+ */
private String typeIdentifier; // type identifiers are defined and managed externally of Apache Unomi
+ /**
+ * Consent status: {@code GRANTED}, {@code DENIED}, or {@code REVOKED}.
+ * @api.example GRANTED
+ */
private ConsentStatus status;
+ /**
+ * When this status was recorded (ISO-8601 in JSON).
+ * @api.example 2024-01-15T09:00:00.000Z
+ */
private Date statusDate;
+ /**
+ * Optional automatic revoke date; omitted when the consent does not expire.
+ * @api.example 2025-01-15T09:00:00.000Z
+ */
private Date revokeDate;
/**
@@ -124,6 +144,7 @@ public class Consent implements Serializable {
* Consent type identifier.
*
* @return the type identifier
+ * @api.example newsletter
*/
public String getTypeIdentifier() {
return typeIdentifier;
diff --git a/api/src/main/java/org/apache/unomi/api/ContextRequest.java b/api/src/main/java/org/apache/unomi/api/ContextRequest.java
index 6a81561e8..d302befc6 100644
--- a/api/src/main/java/org/apache/unomi/api/ContextRequest.java
+++ b/api/src/main/java/org/apache/unomi/api/ContextRequest.java
@@ -50,29 +50,78 @@ import java.util.Map;
public class ContextRequest {
+ /**
+ * Request source item (typically a page/site {@code Item} with {@code scope}). Used to resolve tracked conditions.
+ */
private Item source;
+ /**
+ * When {@code true}, response includes {@code profileSegments}.
+ * @api.example true
+ */
private boolean requireSegments;
+ /**
+ * Profile property names to return, or {@code ["*"]} for public (non-concealed) properties.
+ * @api.example ["firstName","email"]
+ */
private List<String> requiredProfileProperties;
+ /**
+ * Session property names to return, or {@code ["*"]} for all session properties.
+ * @api.example ["*"]
+ */
private List<String> requiredSessionProperties;
+ /**
+ * When {@code true}, response includes {@code profileScores}.
+ * @api.example false
+ */
private boolean requireScores;
+ /**
+ * Client-generated events to process before building the response (view, click, login, …).
+ */
private List<Event> events;
+ /**
+ * Content personalization filters to evaluate against the (possibly overridden) profile/session.
+ */
private List<PersonalizationService.PersonalizedContent> filters;
+ /**
+ * Personalization requests (ordered content lists) to resolve for this profile/session.
+ */
private List<PersonalizationService.PersonalizationRequest> personalizations;
// the following overrides make it possible to override temporarily the current profile segments, properties or
// even session properties. This is useful for building UIs to temporarily override one of these parameters to
// test different filter results.
+ /**
+ * Temporary profile overrides for persona/preview UIs (segments, scores, properties). Applied only when the
+ * active profile is a {@link Persona}.
+ */
private Profile profileOverrides;
+ /**
+ * Temporary session property overrides for persona/preview UIs.
+ * @api.example {"utm_source":"newsletter"}
+ */
private Map<String, Object> sessionPropertiesOverrides;
+ /**
+ * Session identifier for this request (preferred over {@code sessionId} query param).
+ * @api.example session-1
+ */
private String sessionId;
+ /**
+ * Profile identifier hint for this request.
+ * @api.example profile-1
+ */
private String profileId;
+ /**
+ * Optional client / channel identifier.
+ * @api.example web-tracker
+ */
private String clientId;
/**
- * The public API key for tenant authentication.
+ * Public API key for tenant authentication (multi-tenant deployments).
+ * @api.example pk_live_example
*/
private String publicApiKey;
diff --git a/api/src/main/java/org/apache/unomi/api/ContextResponse.java b/api/src/main/java/org/apache/unomi/api/ContextResponse.java
index b6e57c341..7a52dbf88 100644
--- a/api/src/main/java/org/apache/unomi/api/ContextResponse.java
+++ b/api/src/main/java/org/apache/unomi/api/ContextResponse.java
@@ -34,32 +34,87 @@ public class ContextResponse implements Serializable {
private static final long serialVersionUID = -5638595408986826332L;
+ /**
+ * Resolved profile id for this evaluation.
+ * @api.example profile-1
+ */
private String profileId;
+ /**
+ * Resolved session id for this evaluation (may be omitted when no session exists).
+ * @api.example session-1
+ */
private String sessionId;
+ /**
+ * Profile properties subset requested via {@link ContextRequest#getRequiredProfileProperties()}.
+ * @api.example {"firstName":"Ada"}
+ */
private Map<String, Object> profileProperties;
+ /**
+ * Session properties subset requested via {@link ContextRequest#getRequiredSessionProperties()}.
+ * @api.example {"utm_source":"newsletter"}
+ */
private Map<String, Object> sessionProperties;
+ /**
+ * Segment ids for the profile when {@link ContextRequest#isRequireSegments()} was true.
+ * @api.example ["vip","returning"]
+ */
private Set<String> profileSegments;
+ /**
+ * Scoring plan id → score when {@link ContextRequest#isRequireScores()} was true.
+ * @api.example {"engagement":12}
+ */
private Map<String,Integer> profileScores;
+ /**
+ * Filter id → whether the profile matched that content filter.
+ * @api.example {"hero-banner":true}
+ */
private Map<String, Boolean> filteringResults;
+ /**
+ * Number of events from the request that were processed.
+ * @api.example 1
+ */
private int processedEvents;
+ /**
+ * Legacy personalization id → selected content ids.
+ * Prefer {@link #personalizationResults} since 2.1.0.
+ * @api.example {"hero":["variant-a"]}
+ */
private Map<String, List<String>> personalizations;
+ /**
+ * Personalization id → resolution result (content ids, scores, filters).
+ * Prefer this over the legacy {@link #personalizations} map.
+ */
private Map<String, PersonalizationResult> personalizationResults;
+ /**
+ * Tracked conditions clients should watch for (for example form field mapping rules).
+ */
private Set<Condition> trackedConditions;
+ /**
+ * {@code true} when privacy requires anonymous browsing for this profile.
+ * @api.example false
+ */
private boolean anonymousBrowsing;
+ /**
+ * Consent map for the profile, keyed by consent identifier.
+ * @api.example {"newsletter":{"scope":"mysite","typeIdentifier":"newsletter","status":"GRANTED"}}
+ */
private Map<String, Consent> consents = new LinkedHashMap<>();
+ /**
+ * Present only when {@code explain=true} and the caller is an administrator / tenant administrator.
+ */
private TraceNode requestTracing;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/Event.java b/api/src/main/java/org/apache/unomi/api/Event.java
index 988e11cda..8c66a35e6 100644
--- a/api/src/main/java/org/apache/unomi/api/Event.java
+++ b/api/src/main/java/org/apache/unomi/api/Event.java
@@ -53,20 +53,54 @@ public class Event extends Item implements TimestampedItem {
public static final String CLIENT_ID_ATTRIBUTE = "client_id";
private static final long serialVersionUID = -1096874942838593575L;
+ /**
+ * Event verb / type id (for example {@code view}, {@code login}, {@code modifyProperties}).
+ * @api.example view
+ */
private String eventType;
+ /**
+ * Session this event belongs to (optional if the collector request sets a default).
+ * @api.example session-1
+ */
private String sessionId = null;
+ /**
+ * Profile this event belongs to (optional if the collector/context request sets a default).
+ * @api.example profile-1
+ */
private String profileId = null;
+ /**
+ * Event time; defaults to receive time when omitted (ISO-8601 in JSON).
+ * @api.example 2024-06-15T10:30:00.000Z
+ */
private Date timeStamp;
+ /**
+ * Event-specific properties (URL, product id, form fields, …). Free-form map.
+ * @api.example {"pageUrl":"https://example.com/"}
+ */
private Map<String, Object> properties;
+ /**
+ * Flattened copy of nested properties used by some query paths.
+ * @api.example {"pageUrl":"https://example.com/"}
+ */
private Map<String, Object> flattenedProperties;
private transient Profile profile;
private transient Session session;
private transient List<ActionPostExecutor> actionPostExecutors;
+ /**
+ * Event source (subject), often a page {@link Item} with {@code scope} and {@code itemId}.
+ */
private Item source;
+ /**
+ * Event target (object), when applicable.
+ */
private Item target;
+ /**
+ * When {@code false}, the event is evaluated by rules but not persisted.
+ * @api.example true
+ */
private boolean persistent = true;
private transient Map<String, Object> attributes;
diff --git a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java
index 49ff69965..6e3f70cc8 100644
--- a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java
+++ b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java
@@ -27,14 +27,27 @@ import java.util.List;
*/
public class EventsCollectorRequest {
+ /**
+ * Events to collect and evaluate. At least one event is expected for a useful call.
+ * @api.example [{"eventType":"view","scope":"mysite","source":{"itemType":"page","scope":"mysite","itemId":"/home"}}]
+ */
private List<Event> events;
+ /**
+ * Default session id when individual events do not specify one (prefer body over query string).
+ * @api.example session-1
+ */
private String sessionId;
+ /**
+ * Default profile id when individual events do not specify one.
+ * @api.example profile-1
+ */
private String profileId;
/**
- * The public API key for tenant authentication.
+ * Public API key for tenant authentication (multi-tenant deployments).
+ * @api.example pk_live_example
*/
private String publicApiKey;
diff --git a/api/src/main/java/org/apache/unomi/api/Item.java b/api/src/main/java/org/apache/unomi/api/Item.java
index b8e63d4f2..7526e11d3 100644
--- a/api/src/main/java/org/apache/unomi/api/Item.java
+++ b/api/src/main/java/org/apache/unomi/api/Item.java
@@ -77,35 +77,68 @@ public abstract class Item implements Serializable, YamlConvertible {
/**
* Unique id used when this item is persisted or referenced.
* Must be unique among items of the same {@link #itemType}.
+ * @api.example profile-1
*/
protected String itemId;
/**
* Persistence type string for this item.
* Subclasses must define a public {@code ITEM_TYPE} constant with this value.
+ * @api.example profile
*/
protected String itemType;
/**
* Scope that groups related items (often one analyzed site).
* Used by clients to filter data returned from the context server.
+ * @api.example mysite
*/
protected String scope;
/**
* Optimistic-locking version, incremented when the item is updated.
+ * @api.example 3
*/
protected Long version;
/**
* Server-managed metadata keyed by string.
* Stores values that are not part of the item's core properties.
+ * @api.example {"origin":"import"}
*/
protected Map<String, Object> systemMetadata = new HashMap<>();
+ /**
+ * Tenant that owns this item.
+ * @api.example acme
+ */
private String tenantId;
// Audit metadata fields
+ /**
+ * User or system that created this item.
+ * @api.example karaf
+ */
private String createdBy;
+ /**
+ * User or system that last modified this item.
+ * @api.example karaf
+ */
private String lastModifiedBy;
+ /**
+ * When this item was created (ISO-8601 in JSON).
+ * @api.example 2024-06-15T10:30:00.000Z
+ */
private Date creationDate;
+ /**
+ * When this item was last modified (ISO-8601 in JSON).
+ * @api.example 2024-06-15T11:00:00.000Z
+ */
private Date lastModificationDate;
+ /**
+ * Cluster node that originated this item.
+ * @api.example node-1
+ */
private String sourceInstanceId;
+ /**
+ * When this item was last synchronized from another node (ISO-8601 in JSON).
+ * @api.example 2024-06-15T11:05:00.000Z
+ */
private Date lastSyncDate;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/Metadata.java b/api/src/main/java/org/apache/unomi/api/Metadata.java
index ec643e2d8..ab1d14f41 100644
--- a/api/src/main/java/org/apache/unomi/api/Metadata.java
+++ b/api/src/main/java/org/apache/unomi/api/Metadata.java
@@ -43,15 +43,55 @@ public class Metadata implements Comparable<Metadata>, Serializable, YamlConvert
* Default scope, gathers default entities and can also be used to share entities across scopes.
*/
public static final String SYSTEM_SCOPE = "systemscope";
+ /**
+ * Stable identifier of this metadata item (often equals itemId of the owning entity).
+ * @api.example vip
+ */
private String id;
+ /**
+ * Display name.
+ * @api.example VIP customers
+ */
private String name;
+ /**
+ * Human-readable description.
+ * @api.example Customers with high engagement score
+ */
private String description;
+ /**
+ * Scope that owns this metadata.
+ * @api.example mysite
+ */
private String scope;
+ /**
+ * User-visible tags.
+ * @api.example ["marketing"]
+ */
private Set<String> tags = new LinkedHashSet<>();
+ /**
+ * System tags used by Unomi internals and plugins.
+ * @api.example ["profileProperties"]
+ */
private Set<String> systemTags = new LinkedHashSet<>();
+ /**
+ * Whether this item is enabled.
+ * @api.example true
+ */
private boolean enabled = true;
+ /**
+ * {@code true} when required plugins are not installed.
+ * @api.example false
+ */
private boolean missingPlugins = false;
+ /**
+ * Whether this item is hidden from default UIs.
+ * @api.example false
+ */
private boolean hidden = false;
+ /**
+ * Whether this item is read-only.
+ * @api.example false
+ */
private boolean readOnly = false;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/PartialList.java b/api/src/main/java/org/apache/unomi/api/PartialList.java
index f6fe43a44..f0d3f615c 100644
--- a/api/src/main/java/org/apache/unomi/api/PartialList.java
+++ b/api/src/main/java/org/apache/unomi/api/PartialList.java
@@ -32,12 +32,39 @@ import java.util.List;
public class PartialList<T> implements Serializable {
private static final long serialVersionUID = 2661946814840468260L;
+ /**
+ * Elements in the current page.
+ */
private List<T> list;
+ /**
+ * Zero-based index of the first element of this page in the full result set.
+ * @api.example 0
+ */
private long offset;
+ /**
+ * Number of elements in this page.
+ * @api.example 20
+ */
private long pageSize;
+ /**
+ * Total number of matching elements (exact or lower bound; see {@link #totalSizeRelation}).
+ * @api.example 120
+ */
private long totalSize;
+ /**
+ * Whether {@link #totalSize} is exact ({@code EQUAL}) or a lower bound ({@code GREATER_THAN_OR_EQUAL_TO}).
+ * @api.example EQUAL
+ */
private Relation totalSizeRelation;
+ /**
+ * Scroll identifier for continuing a deep scroll query, when applicable.
+ * @api.example DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYmRUMAkwZGY=
+ */
private String scrollIdentifier = null;
+ /**
+ * Scroll keep-alive window associated with {@link #scrollIdentifier}.
+ * @api.example 10m
+ */
private String scrollTimeValidity = null;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java
index 0c6142c89..c9624b163 100644
--- a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java
+++ b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java
@@ -27,8 +27,14 @@ import java.util.List;
* the sessions needed to simulate or inspect that persona.
*/
public class PersonaWithSessions implements Serializable {
+ /**
+ * The persona being simulated or inspected.
+ */
private Persona persona;
+ /**
+ * Sessions associated with the persona.
+ */
private List<PersonaSession> sessions;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java
index 0dab6d134..5134cd362 100644
--- a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java
+++ b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java
@@ -38,13 +38,22 @@ public class PersonalizationResult implements Serializable {
*/
public final static String ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP = "inControlGroup";
- /** Matching content identifiers for the resolved personalization. */
+ /**
+ * Matching content identifiers for the resolved personalization (ordered by strategy).
+ * @api.example ["hero-a","hero-b"]
+ */
List<String> contentIds;
- /** Extra key/value metadata returned to the client (for example control group flags). */
+ /**
+ * Extra key/value metadata returned to the client. Well-known key:
+ * {@link #ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP} ({@code inControlGroup}) → boolean.
+ * @api.example {"inControlGroup":false}
+ */
Map<String, Object> additionalResultInfos = new HashMap<>();
- /** Internal change flags when resolution updated the profile or session. */
+ /**
+ * Internal change flags when resolution updated the profile or session (not serialized on the wire).
+ */
int changeType = EventService.NO_CHANGE;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/Profile.java b/api/src/main/java/org/apache/unomi/api/Profile.java
index 5a8ee35d3..02bf24e21 100644
--- a/api/src/main/java/org/apache/unomi/api/Profile.java
+++ b/api/src/main/java/org/apache/unomi/api/Profile.java
@@ -47,20 +47,42 @@ public class Profile extends Item implements SystemPropertiesItem {
*/
public static final String ITEM_TYPE = "profile";
private static final long serialVersionUID = -7409439322939712238L;
+ /**
+ * All user-visible profile properties.
+ * @api.example {"firstName":"Ada","email":"ada@example.com"}
+ */
private Map<String, Object> properties = new HashMap<>();
+ /**
+ * Internal properties used by implementations and not usually returned to clients.
+ */
private Map<String, Object> systemProperties = new HashMap<>();
+ /**
+ * Segment identifiers matched by this profile.
+ * @api.example ["vip","returning"]
+ */
private Set<String> segments = new HashSet<>();
+ /**
+ * Scoring plan id → numeric score for this profile.
+ * @api.example {"engagement":12}
+ */
private Map<String, Integer> scores;
/**
+ * Legacy merge target profile id; unused since 2.0.0 (profile aliases replaced merges).
+ *
* @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore
+ * @api.example profile-merged-target
*/
@Deprecated
private String mergedWith;
+ /**
+ * Consent map for this profile, keyed by consent identifier.
+ * @api.example {"newsletter":{"scope":"mysite","typeIdentifier":"newsletter","status":"GRANTED"}}
+ */
private Map<String, Consent> consents = new LinkedHashMap<>();
/**
diff --git a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java
index cdfe734b1..6bcb7a797 100644
--- a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java
+++ b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java
@@ -32,12 +32,28 @@ public class ProfileAlias extends Item {
*/
public static final String ITEM_TYPE = "profileAlias";
+ /**
+ * Canonical profile id linked by this alias.
+ * @api.example profile-1
+ */
private String profileID;
+ /**
+ * Client that owns / created this alias link.
+ * @api.example web-tracker
+ */
private String clientID;
+ /**
+ * When this alias was created (ISO-8601 in JSON).
+ * @api.example 2024-06-15T10:00:00.000Z
+ */
private Date creationTime;
+ /**
+ * When this alias was last modified (ISO-8601 in JSON).
+ * @api.example 2024-06-15T11:00:00.000Z
+ */
private Date modifiedTime;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java
index 25b3117d8..fb04f0cf4 100644
--- a/api/src/main/java/org/apache/unomi/api/PropertyType.java
+++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java
@@ -37,18 +37,65 @@ public class PropertyType extends MetadataItem {
*/
public static final String ITEM_TYPE = "propertyType";
+ /**
+ * Item type this property applies to (for example {@code profiles}).
+ * @api.example profiles
+ */
private String target;
+ /**
+ * Value type identifier (for example {@code string}, {@code email}, {@code integer}).
+ * @api.example email
+ */
private String valueTypeId;
+ /**
+ * Resolved value type definition (optional; usually derived from {@link #valueTypeId}).
+ */
private ValueType valueType;
+ /**
+ * Optional default value for new items.
+ * @api.example unknown@example.com
+ */
private String defaultValue;
+ /**
+ * Optional allowed date ranges for date-typed properties.
+ */
private List<DateRange> dateRanges = new ArrayList<>();
+ /**
+ * Optional allowed numeric ranges for numeric properties.
+ */
private List<NumericRange> numericRanges = new ArrayList<>();
+ /**
+ * Optional allowed IP ranges for IP-typed properties.
+ */
private List<IpRange> ipRanges = new ArrayList<>();
+ /**
+ * External property names automatically mapped onto this property type.
+ * @api.example ["mail","e-mail"]
+ */
private Set<String> automaticMappingsFrom = new HashSet<>();
+ /**
+ * Display/order rank among sibling property types.
+ * @api.example 10.0
+ */
private Double rank;
+ /**
+ * Strategy used when merging this property across profiles.
+ * @api.example defaultPropertyMergeStrategy
+ */
private String mergeStrategy;
+ /**
+ * Whether the property accepts multiple values.
+ * @api.example false
+ */
private Boolean multivalued;
+ /**
+ * When {@code true}, the property is protected / concealed from public APIs.
+ * @api.example false
+ */
private Boolean protekted;
+ /**
+ * Nested child property types for structured properties.
+ */
private Set<PropertyType> childPropertyTypes = new LinkedHashSet<>();
/**
diff --git a/api/src/main/java/org/apache/unomi/api/ServerInfo.java b/api/src/main/java/org/apache/unomi/api/ServerInfo.java
index dd0a46d00..b2ce88d87 100644
--- a/api/src/main/java/org/apache/unomi/api/ServerInfo.java
+++ b/api/src/main/java/org/apache/unomi/api/ServerInfo.java
@@ -30,8 +30,11 @@ import java.util.Map;
*/
public class ServerInfo {
+ /** @api.example unomi */
private String serverIdentifier;
+ /** @api.example 3.1.0-SNAPSHOT */
private String serverVersion;
+ /** @api.example 1 */
private String serverBuildNumber;
private Date serverBuildDate;
private String serverTimestamp;
@@ -52,6 +55,7 @@ public class ServerInfo {
* Unique identifier for this server instance.
*
* @return server identifier
+ * @api.example unomi
*/
public String getServerIdentifier() {
return serverIdentifier;
@@ -70,6 +74,7 @@ public class ServerInfo {
* Running Unomi version string.
*
* @return server version
+ * @api.example 3.1.0-SNAPSHOT
*/
public String getServerVersion() {
return serverVersion;
diff --git a/api/src/main/java/org/apache/unomi/api/Session.java b/api/src/main/java/org/apache/unomi/api/Session.java
index ad986b931..f05a1e5b7 100644
--- a/api/src/main/java/org/apache/unomi/api/Session.java
+++ b/api/src/main/java/org/apache/unomi/api/Session.java
@@ -36,25 +36,67 @@ public class Session extends Item implements TimestampedItem, SystemPropertiesIt
*/
public static final String ITEM_TYPE = "session";
private static final long serialVersionUID = 4628640198281687336L;
+ /**
+ * Identifier of the profile linked to this session.
+ * @api.example profile-1
+ */
private String profileId;
+ /**
+ * Profile associated with this session.
+ */
private Profile profile;
+ /**
+ * All user-visible session properties.
+ * @api.example {"utm_source":"newsletter"}
+ */
private Map<String, Object> properties = new HashMap<>();
+ /**
+ * Internal properties used by implementations and not usually returned to clients.
+ */
private Map<String, Object> systemProperties = new HashMap<>();
+ /**
+ * When this session was created (ISO-8601 in JSON).
+ * @api.example 2024-06-15T10:00:00.000Z
+ */
private Date timeStamp;
+ /**
+ * Scope this session belongs to.
+ * @api.example mysite
+ */
private String scope;
+ /**
+ * Timestamp of the most recent event in this session (ISO-8601 in JSON).
+ * @api.example 2024-06-15T10:30:00.000Z
+ */
private Date lastEventDate;
+ /**
+ * Number of events recorded in this session.
+ * @api.example 4
+ */
private int size = 0;
+ /**
+ * Elapsed time between session start and last event, in milliseconds.
+ * @api.example 1800000
+ */
private int duration = 0;
+ /**
+ * Event types that triggered session creation.
+ * @api.example ["view"]
+ */
private List<String> originEventTypes = new ArrayList<>();
+ /**
+ * Event ids that triggered session creation.
+ * @api.example ["evt-1"]
+ */
private List<String> originEventIds = new ArrayList<>();
/**
diff --git a/api/src/main/java/org/apache/unomi/api/actions/Action.java b/api/src/main/java/org/apache/unomi/api/actions/Action.java
index 7d10fe5d7..a7e32bdf1 100644
--- a/api/src/main/java/org/apache/unomi/api/actions/Action.java
+++ b/api/src/main/java/org/apache/unomi/api/actions/Action.java
@@ -38,7 +38,18 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue;
*/
public class Action implements Serializable, YamlConvertible {
private ActionType actionType;
+ /**
+ * Identifier of the action type to execute.
+ * In JSON bodies this id is usually serialized as {@code type}
+ * (for example {@code setPropertyAction}, {@code allEventToProfilePropertiesAction}).
+ * @api.example setPropertyAction
+ */
private String actionTypeId;
+ /**
+ * Parameter values for the action (names depend on the action type).
+ * For {@code setPropertyAction}: {@code setPropertyName}, then one value param ({@code setPropertyValue} string, {@code setPropertyValueBoolean}, {@code setPropertyValueInteger}, or a helper such as {@code setPropertyValueCurrentDate}), optional {@code storeInSession}.
+ * @api.example {"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}
+ */
private Map<String, Object> parameterValues = new HashMap<>();
/**
@@ -80,6 +91,7 @@ public class Action implements Serializable, YamlConvertible {
* Identifier of the associated action type.
*
* @return the identifier of the associated action type
+ * @api.example setPropertyAction
*/
@XmlElement(name = "type")
public String getActionTypeId() {
@@ -99,6 +111,7 @@ public class Action implements Serializable, YamlConvertible {
* Parameter values keyed by parameter name.
*
* @return a Map of parameter name - associated value pairs
+ * @api.example {"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}
*/
public Map<String, Object> getParameterValues() {
return parameterValues;
diff --git a/api/src/main/java/org/apache/unomi/api/actions/ActionType.java b/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
index 501dafd97..0e0e89dca 100644
--- a/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
+++ b/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
@@ -42,6 +42,9 @@ public class ActionType extends MetadataItem implements PluginType, YamlConverti
private static final long serialVersionUID = -3522958600710010935L;
private String actionExecutor;
private List<Parameter> parameters = new ArrayList<Parameter>();
+ /**
+ * Bundle/plugin id that contributed this action type definition.
+ */
private long pluginId;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java
index f28789738..438bcc23f 100644
--- a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java
+++ b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java
@@ -42,6 +42,10 @@ public class Campaign extends MetadataItem {
private Date endDate;
+ /**
+ * Condition a profile must satisfy to enter the campaign (JSON wire field {@code type} + {@code parameterValues}).
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
+ */
private Condition entryCondition;
private Double cost;
@@ -105,8 +109,10 @@ public class Campaign extends MetadataItem {
/**
* Condition a profile must satisfy to enter the campaign.
+ * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
*
* @return the entry condition
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
*/
public Condition getEntryCondition() {
return entryCondition;
diff --git a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
index fd7655286..b30ed6b42 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
@@ -46,7 +46,20 @@ public class Condition implements Serializable, YamlConvertible {
private static final long serialVersionUID = 7584522402785053206L;
ConditionType conditionType;
+ /**
+ * Identifier of the condition type backing this expression.
+ * In JSON request/response bodies this id is usually serialized as {@code type}
+ * (for example {@code eventTypeCondition}, {@code profilePropertyCondition}, {@code booleanCondition}).
+ * @api.example eventTypeCondition
+ */
String conditionTypeId;
+ /**
+ * Parameter values supplied for this condition instance (names depend on the condition type).
+ * Examples: {@code eventTypeCondition} uses {@code eventTypeId};
+ * {@code profilePropertyCondition} uses {@code propertyName}/{@code comparisonOperator}/{@code propertyValue*};
+ * {@code booleanCondition} uses {@code operator} plus nested {@code subConditions}.
+ * @api.example {"eventTypeId":"view"}
+ */
Map<String, Object> parameterValues = new HashMap<>();
/**
diff --git a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
index 4adadf8e6..aab662988 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
@@ -46,6 +46,9 @@ public class ConditionType extends MetadataItem implements PluginType, YamlConve
private String queryBuilder;
private Condition parentCondition;
private List<Parameter> parameters = new ArrayList<Parameter>();
+ /**
+ * Bundle/plugin id that contributed this condition type definition.
+ */
private long pluginId;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/goals/Goal.java b/api/src/main/java/org/apache/unomi/api/goals/Goal.java
index 673191015..1b6862763 100644
--- a/api/src/main/java/org/apache/unomi/api/goals/Goal.java
+++ b/api/src/main/java/org/apache/unomi/api/goals/Goal.java
@@ -45,8 +45,16 @@ public class Goal extends MetadataItem implements YamlConvertible {
/** The constant string used to identify this object as a goal item type. */
public static final String ITEM_TYPE = "goal";
private static final long serialVersionUID = 6131648013470949983L;
+ /**
+ * Optional start condition for funnel-style goals (JSON wire field {@code type} + {@code parameterValues}).
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
+ */
private Condition startEvent;
+ /**
+ * Condition that marks the goal as accomplished (JSON wire field {@code type} + {@code parameterValues}).
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}
+ */
private Condition targetEvent;
private String campaignId;
@@ -69,8 +77,10 @@ public class Goal extends MetadataItem implements YamlConvertible {
/**
* Optional start condition for funnel-style goals.
* When set, goal tracking begins only after this condition is met.
+ * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
*
* @return the start condition, or {@code null} if none is configured
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
*/
public Condition getStartEvent() {
return startEvent;
@@ -87,8 +97,10 @@ public class Goal extends MetadataItem implements YamlConvertible {
/**
* Condition that marks the goal as accomplished when it matches.
+ * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
*
* @return the target condition
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}
*/
public Condition getTargetEvent() {
return targetEvent;
diff --git a/api/src/main/java/org/apache/unomi/api/query/Query.java b/api/src/main/java/org/apache/unomi/api/query/Query.java
index 9e3e0fbe2..be9d1f16c 100644
--- a/api/src/main/java/org/apache/unomi/api/query/Query.java
+++ b/api/src/main/java/org/apache/unomi/api/query/Query.java
@@ -28,13 +28,44 @@ import java.io.Serializable;
* and a {@code forceRefresh} flag that controls whether indexes are refreshed first.
*/
public class Query implements Serializable {
+ /**
+ * Optional full-text filter applied to the search.
+ * @api.example Ada
+ */
private String text;
+ /**
+ * Zero-based index of the first result to return.
+ * @api.example 0
+ */
private int offset;
+ /**
+ * Maximum number of results to return, or {@code -1} for all matches.
+ * @api.example 20
+ */
private int limit = Integer.MIN_VALUE;
+ /**
+ * Comma-separated sort fields with optional {@code :asc} or {@code :desc} (for example {@code properties.firstName:asc}).
+ * @api.example properties.firstName:asc
+ */
private String sortby;
+ /**
+ * Condition that returned items must satisfy.
+ */
private Condition condition;
+ /**
+ * When {@code true}, refresh indexes before running the query (slower, more consistent).
+ * @api.example false
+ */
private boolean forceRefresh;
+ /**
+ * Scroll keep-alive window (for example {@code 10m}) when using scroll search.
+ * @api.example 10m
+ */
private String scrollTimeValidity;
+ /**
+ * Scroll identifier returned by a previous scroll page when continuing deep pagination.
+ * @api.example DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYmRUMAkwZGY=
+ */
private String scrollIdentifier;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/rules/Rule.java b/api/src/main/java/org/apache/unomi/api/rules/Rule.java
index 1e2586f18..3e9a25b83 100644
--- a/api/src/main/java/org/apache/unomi/api/rules/Rule.java
+++ b/api/src/main/java/org/apache/unomi/api/rules/Rule.java
@@ -49,18 +49,46 @@ public class Rule extends MetadataItem implements YamlConvertible {
*/
public static final String ITEM_TYPE = "rule";
private static final long serialVersionUID = 3058739939263056507L;
+ /**
+ * Condition that must match before this rule's actions run.
+ * Typical trigger: {@code eventTypeCondition} with {@code eventTypeId}.
+ */
private Condition condition;
+ /**
+ * Actions performed when the condition matches (ordered).
+ * @api.example [{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}]
+ */
private List<Action> actions;
+ /**
+ * Identifiers of items linked to this rule (for example segments or goals that own it).
+ * @api.example ["goal-welcome"]
+ */
private List<String> linkedItems;
+ /**
+ * When {@code true}, raise the rule-triggered event at most once per profile.
+ * @api.example false
+ */
private boolean raiseEventOnlyOnceForProfile = false;
+ /**
+ * When {@code true}, raise the rule-triggered event at most once per session.
+ * @api.example false
+ */
private boolean raiseEventOnlyOnceForSession = false;
+ /**
+ * When {@code true}, raise the rule-triggered event at most once overall.
+ * @api.example false
+ */
private boolean raiseEventOnlyOnce = false;
+ /**
+ * Execution priority among matching rules (higher runs first when comparable).
+ * @api.example 0
+ */
private int priority;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
index 80cf18ac4..bdd886bd1 100644
--- a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
+++ b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
@@ -38,12 +38,40 @@ public class RuleStatistics extends Item {
public static final String ITEM_TYPE = "rulestats";
private static final long serialVersionUID = 1L;
+ /**
+ * Cluster-wide execution count (excluding unsynchronized local count).
+ * @api.example 42
+ */
private long executionCount = 0;
+ /**
+ * Executions on this node since {@link #lastSyncDate}.
+ * @api.example 3
+ */
private long localExecutionCount = 0;
+ /**
+ * Cluster-wide time spent evaluating conditions, in milliseconds.
+ * @api.example 120
+ */
private long conditionsTime = 0;
+ /**
+ * Condition evaluation time on this node since last sync, in milliseconds.
+ * @api.example 8
+ */
private long localConditionsTime = 0;
+ /**
+ * Cluster-wide time spent running actions, in milliseconds.
+ * @api.example 95
+ */
private long actionsTime = 0;
+ /**
+ * Action execution time on this node since last sync, in milliseconds.
+ * @api.example 5
+ */
private long localActionsTime = 0;
+ /**
+ * When local counters were last merged into cluster totals (ISO-8601 in JSON).
+ * @api.example 2024-06-15T11:00:00.000Z
+ */
private Date lastSyncDate;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
index 30926539f..b9391a5eb 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
@@ -29,8 +29,16 @@ import java.util.List;
*/
public class DependentMetadata implements Serializable {
+ /**
+ * Segments that reference the given segment (for example via profileSegmentCondition).
+ * @api.example [{"id":"premium-buyers","name":"Premium buyers","scope":"mysite","enabled":true}]
+ */
private List<Metadata> segments;
+ /**
+ * Scorings that reference the given segment.
+ * @api.example [{"id":"premium-score","name":"Premium score","scope":"mysite","enabled":true}]
+ */
private List<Metadata> scorings;
/**
@@ -48,6 +56,7 @@ public class DependentMetadata implements Serializable {
* Metadata for segments that depend on the edited item.
*
* @return the dependent segment metadata
+ * @api.example [{"id":"premium-buyers","name":"Premium buyers","scope":"mysite","enabled":true}]
*/
public List<Metadata> getSegments() {
return segments;
@@ -66,6 +75,7 @@ public class DependentMetadata implements Serializable {
* Metadata for scorings that depend on the edited item.
*
* @return the dependent scoring metadata
+ * @api.example [{"id":"premium-score","name":"Premium score","scope":"mysite","enabled":true}]
*/
public List<Metadata> getScorings() {
return scorings;
diff --git a/api/src/main/java/org/apache/unomi/api/segments/Scoring.java b/api/src/main/java/org/apache/unomi/api/segments/Scoring.java
index 85e36ad1e..bb4455a18 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/Scoring.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/Scoring.java
@@ -42,6 +42,10 @@ public class Scoring extends MetadataItem implements YamlConvertible {
*/
public static final String ITEM_TYPE = "scoring";
private static final long serialVersionUID = 6351058906259967559L;
+ /**
+ * Scoring elements that define conditions and point values.
+ * @api.example [{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]
+ */
private List<ScoringElement> elements;
/**
@@ -61,8 +65,10 @@ public class Scoring extends MetadataItem implements YamlConvertible {
/**
* Scoring elements that define conditions and point values.
+ * Each element pairs a {@code condition} ({@code type} + {@code parameterValues}) with a point {@code value}.
*
* @return the scoring elements
+ * @api.example [{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]
*/
public List<ScoringElement> getElements() {
return elements;
diff --git a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java
index 7467f5788..6fbe96074 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java
@@ -35,6 +35,10 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue;
* profile matches the element's condition.
*/
public class ScoringElement implements Serializable, YamlConvertible {
+ /**
+ * Condition evaluated to decide whether this scoring element applies (JSON wire field {@code type} + {@code parameterValues}).
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
+ */
private Condition condition;
private int value;
@@ -46,8 +50,10 @@ public class ScoringElement implements Serializable, YamlConvertible {
/**
* Condition evaluated to decide whether this scoring element applies.
+ * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
*
* @return the condition
+ * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
*/
public Condition getCondition() {
return condition;
diff --git a/api/src/main/java/org/apache/unomi/api/segments/Segment.java b/api/src/main/java/org/apache/unomi/api/segments/Segment.java
index b5a4758ab..cb3749cec 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/Segment.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/Segment.java
@@ -46,6 +46,10 @@ public class Segment extends MetadataItem implements YamlConvertible {
*/
public static final String ITEM_TYPE = "segment";
private static final long serialVersionUID = -1384533444860961296L;
+ /**
+ * Membership condition evaluated against profiles (JSON wire field {@code type} + {@code parameterValues}).
+ * @api.example {"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}
+ */
private Condition condition;
/**
@@ -65,8 +69,10 @@ public class Segment extends MetadataItem implements YamlConvertible {
/**
* Condition that profiles must satisfy to belong to this segment.
+ * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
*
* @return the membership condition
+ * @api.example {"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}
*/
public Condition getCondition() {
return condition;
diff --git a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java
index bcd9a238c..8b3919fb8 100644
--- a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java
@@ -63,12 +63,28 @@ public interface PersonalizationService {
PersonalizationResult personalizeList(Profile profile, Session session, PersonalizationRequest personalizationRequest);
/**
- * Personalization request
+ * Request to resolve an ordered set of content variants for a profile/session
+ * (sent in {@code ContextRequest.personalizations}).
*/
class PersonalizationRequest {
+ /**
+ * Client-defined personalization id; echoed as the key in {@code ContextResponse.personalizationResults}.
+ * @api.example homepage-hero
+ */
private String id;
+ /**
+ * Strategy plugin name (for example {@code matching-content}, {@code score-sort}, {@code random}).
+ * @api.example matching-content
+ */
private String strategy;
+ /**
+ * Strategy-specific options (control groups, fallbacks, size limits, …). Free-form map.
+ * @api.example {"fallback":"default","size":1}
+ */
private Map<String, Object> strategyOptions;
+ /**
+ * Candidate content variants evaluated by the strategy.
+ */
private List<PersonalizedContent> contents;
/**
@@ -145,11 +161,22 @@ public interface PersonalizationService {
}
/**
- * A personalizated content definition.
+ * One content variant (also used as entries in {@code ContextRequest.filters}).
*/
class PersonalizedContent {
+ /**
+ * Variant / filter id returned to the client when selected or when evaluating filters.
+ * @api.example hero-a
+ */
private String id;
+ /**
+ * Optional filters; all applicable conditions must match for the variant to be eligible.
+ */
private List<Filter> filters;
+ /**
+ * Optional free-form metadata for the client (CMS labels, positions, …).
+ * @api.example {"position":"top"}
+ */
private Map<String,Object> properties;
/**
@@ -218,11 +245,21 @@ public interface PersonalizationService {
}
/**
- * A filter definition for content filtering
+ * Filter applied to a content variant: condition plus optional target hints.
*/
class Filter {
+ /**
+ * Optional targets describing which profile/session dimensions the filter concerns.
+ */
private List<Target> appliesOn;
+ /**
+ * Unomi condition that must match the profile/session for this filter to pass.
+ */
private Condition condition;
+ /**
+ * Optional free-form filter metadata.
+ * @api.example {"priority":1}
+ */
private Map<String,Object> properties;
/**
@@ -281,10 +318,18 @@ public interface PersonalizationService {
}
/**
- * A target for content filtering.
+ * Named dimension and allowed values used as filter metadata (client/CMS hinting).
*/
class Target {
+ /**
+ * Dimension name (for example a property key or {@code segments}).
+ * @api.example interests
+ */
private String target;
+ /**
+ * Allowed values for that dimension.
+ * @api.example ["sports","news"]
+ */
private List<String> values;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java
index b6121792f..80b25002c 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java
@@ -44,59 +44,57 @@ public class ApiKey extends Item {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
- * Enum defining the types of API keys.
+ * Scope of an API key. Serialized as {@code PUBLIC} or {@code PRIVATE}.
*/
public enum ApiKeyType {
- /**
- * Public API key for context.json, event collector and other public-facing endpoints
- */
+ /** Public key for context.json, event collector, and other public-facing endpoints. */
PUBLIC,
-
- /**
- * Private API key for protected endpoints including login and updateProperties
- */
+
+ /** Private key for protected endpoints (login, updateProperties, admin-style operations). */
PRIVATE
}
/**
* SHA-256 hex digest of the API key ({@link org.apache.unomi.api.security.SecretHashService#hash(String)}).
- * The plaintext key is never persisted; it is only returned once at creation time.
+ * The plaintext key is never persisted; it is only returned once at creation time via {@link ApiKeyCreationResult}.
*/
private String keyHash;
/**
- * A display-safe, masked representation of the key (e.g. "unomi_v1_****ab12"),
- * suitable for showing in UIs and logs without exposing the secret.
+ * Display-safe masked key (for example {@code unomi_v1_****ab12}) for UIs and logs.
+ * @api.example unomi_v1_****ab12
*/
private String maskedKey;
/**
- * The type of API key (public or private).
+ * Key scope: {@code PUBLIC} or {@code PRIVATE}.
+ * @api.example PUBLIC
*/
private ApiKeyType keyType;
/**
- * The name or identifier of the API key.
+ * Optional operator label for the key.
*/
private String name;
/**
- * A description of the API key's purpose or usage.
+ * Optional description of the key's purpose.
*/
private String description;
/**
- * The date when the API key was created.
+ * When the key was created (ISO-8601 date-time in JSON).
*/
private Date creationDate;
/**
- * The date when the API key expires.
+ * Expiration instant, or omitted/null when the key does not expire.
*/
private Date expirationDate;
/**
- * Whether the API key has been revoked.
+ * {@code true} if the key has been revoked and must not authenticate.
+ * @api.example false
*/
private boolean revoked;
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java
index 8bbea6daa..66689ca3d 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java
@@ -25,7 +25,15 @@ package org.apache.unomi.api.tenants;
*/
public class ApiKeyCreationResult {
+ /**
+ * Persisted API key metadata (hash, maskedKey, type, dates). Does not contain the secret.
+ */
private ApiKey apiKey;
+ /**
+ * One-time plaintext key value, only available at creation. Prefixed with {@code unomi_v1_}.
+ * Store immediately; it cannot be recovered later.
+ * @api.example unomi_v1_0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
+ */
private String plainTextKey;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java b/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java
index c9c8abad9..2ea379863 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java
@@ -35,54 +35,59 @@ public class Tenant extends Item {
public static final String ITEM_TYPE = "tenant";
/**
- * The display name of the tenant.
+ * Display name of the tenant (human-readable label).
+ * @api.example Acme Corp
*/
private String name;
/**
- * A description of the tenant's purpose or usage.
+ * Free-text description of the tenant's purpose or usage.
+ * @api.example Production tenant for Acme
*/
private String description;
/**
- * The current operational status of the tenant.
+ * Lifecycle status. One of {@link TenantStatus}: {@code ACTIVE}, {@code DISABLED},
+ * {@code SUSPENDED}, {@code PENDING_ACTIVATION}, {@code MAINTENANCE}. New tenants are created as {@code ACTIVE}.
+ * @api.example ACTIVE
*/
private TenantStatus status;
/**
- * The date when the tenant was created.
+ * When the tenant was created (ISO-8601 date-time in JSON).
*/
private Date creationDate;
/**
- * The date when the tenant was last modified.
+ * When the tenant was last modified (ISO-8601 date-time in JSON).
*/
private Date lastModificationDate;
/**
- * The list of all API keys (both active and historical) associated with the tenant.
- * This list maintains a history of all API keys that have been generated for the tenant,
- * including both public and private keys, for auditing purposes.
+ * API keys associated with the tenant (active and historical). Each entry is an {@link ApiKey}
+ * with hashed material ({@code keyHash}), display {@code maskedKey}, {@code keyType}
+ * ({@code PUBLIC} or {@code PRIVATE}), dates, and {@code revoked}. The plaintext secret is never stored here.
*/
private List<ApiKey> apiKeys;
/**
- * Additional custom properties for the tenant.
+ * Open map of custom tenant properties. Free-form keys/values; often used for operator metadata
+ * (for example display {@code name}/{@code description} at create time via {@code TenantRequest}).
+ * @api.example {"region":"eu-west","tier":"premium"}
*/
private Map<String, Object> properties;
/**
- * The set of event types that are restricted for this tenant.
- * Events of these types will require IP validation before being processed.
- * This is used to control which event types require additional validation
- * at the tenant level.
+ * Event type names that require IP validation before processing for this tenant
+ * (for example {@code login} or custom event type identifiers). Empty means no extra restriction.
+ * @api.example ["login","updateProperties"]
*/
private Set<String> restrictedEventTypes = new HashSet<>();
/**
- * The set of IP addresses or CIDR ranges that are authorized to make requests
- * for this tenant. Requests from IP addresses not in this set will be rejected.
+ * Authorized client IP addresses or CIDR ranges. When non-empty, requests from other addresses are rejected.
+ * @api.example ["10.0.0.0/8","192.168.1.10"]
*/
private Set<String> authorizedIPs = new HashSet<>();
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java
index eed555a7b..d2229b33b 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java
@@ -23,9 +23,21 @@ package org.apache.unomi.api.tenants;
*/
public class TenantEventPurgeResult {
+ /**
+ * Tenant this purge applies to.
+ * @api.example acme
+ */
private String tenantId;
+ /**
+ * Retention cutoff in whole days (query {@code retentionDays}; minimum
+ * {@link TenantUsageService#MIN_EVENT_RETENTION_DAYS}).
+ * @api.example 30
+ */
private int retentionDays;
- /** Events matching the retention cutoff before delete-by-query was submitted; not a post-delete count. */
+ /**
+ * Events matching the retention cutoff before delete-by-query was submitted; not a post-delete count.
+ * @api.example 1200
+ */
private long eventsMatched;
/**
* {@code true} if the delete-by-query completed successfully; {@code false} if the
@@ -33,7 +45,12 @@ public class TenantEventPurgeResult {
* partial-success indicator: {@link #eventsMatched} is a pre-delete estimate only, so a
* {@code true} result does not by itself confirm how many events were actually removed.
*/
+ /**
+ * {@code true} if delete-by-query completed successfully; {@code false} maps to HTTP 500 on the REST endpoint.
+ * @api.example true
+ */
private boolean purgeRequested;
+ /** When the purge was requested (epoch millis, UTC). */
private long requestedAt;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java
index 057240412..85436b3e6 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java
@@ -24,8 +24,14 @@ package org.apache.unomi.api.tenants;
*/
public class TenantScopeUsage {
+ /**
+ * Scope identifier (site/application id), excluding {@code systemscope} at the aggregate level.
+ * @api.example mysite
+ */
private String scopeId;
+ /** Number of segments in this scope at collection time. */
private long segmentCount;
+ /** Number of rules in this scope at collection time. */
private long ruleCount;
/**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java
index 021f71b6e..4ad721b4d 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java
@@ -17,32 +17,22 @@
package org.apache.unomi.api.tenants;
/**
- * Lifecycle state of a tenant (for example active, suspended, or pending).
- * Administrators filter and manage tenants based on these enum values.
+ * Lifecycle state of a tenant. Serialized as the enum name string in JSON
+ * ({@code ACTIVE}, {@code DISABLED}, {@code SUSPENDED}, {@code PENDING_ACTIVATION}, {@code MAINTENANCE}).
*/
public enum TenantStatus {
- /**
- * Tenant is active and fully operational
- */
+ /** Fully operational; default for newly created tenants. */
ACTIVE,
- /**
- * Tenant is disabled and cannot perform any operations
- */
+ /** Disabled; cannot perform tenant operations. */
DISABLED,
- /**
- * Tenant is temporarily suspended, typically due to policy violations or maintenance
- */
+ /** Temporarily suspended (policy, billing, or maintenance). */
SUSPENDED,
- /**
- * Tenant is created but waiting for activation process to complete
- */
+ /** Created but waiting for activation to complete. */
PENDING_ACTIVATION,
- /**
- * Tenant is undergoing scheduled maintenance
- */
+ /** Undergoing scheduled maintenance. */
MAINTENANCE
}
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java
index 9313ecd8e..2cbaeaff1 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java
@@ -29,27 +29,40 @@ import java.util.List;
*/
public class TenantUsage {
+ /**
+ * Tenant this snapshot describes.
+ * @api.example acme
+ */
private String tenantId;
- /** Normalized period label, e.g. {@code 2026-07} for a calendar month. */
+ /**
+ * Normalized period label. For month windows typically {@code YYYY-MM} (for example {@code 2026-07}).
+ * Request {@code period} query accepts {@code current-month}, {@code YYYY-MM}, or legacy {@code 24h}.
+ * @api.example 2026-07
+ */
private String period;
/** Inclusive start of the reporting period (epoch millis, UTC). */
private long periodStart;
/** Exclusive end of the reporting period (epoch millis, UTC). */
private long periodEnd;
+ /** Point-in-time profile count at collection. */
private long profileCount;
- /** Events in the calendar month identified by {@link #period}. */
+ /** Events whose {@code timeStamp} falls in [{@link #periodStart}, {@link #periodEnd}). */
private long eventCount;
/** Tenant scopes excluding {@code systemscope}. */
private long scopeCount;
+ /** Point-in-time segment count. */
private long segmentCount;
+ /** Point-in-time rule count. */
private long ruleCount;
/** Document count across tenant indices (not byte size). */
private long storageDocumentCount;
- /** Active API keys on the tenant record. */
+ /** Active (non-revoked, non-expired) API keys on the tenant record. */
private long activeApiKeyCount;
- /** In-memory REST request counter for this tenant since the Unomi process started. */
+ /** In-memory REST request counter for this tenant since the Unomi process started (resets on restart). */
private long restRequestCount;
+ /** Per-scope segment/rule breakdown; empty when no scoped usage was collected. */
private List<TenantScopeUsage> scopeUsages = new ArrayList<>();
+ /** When this snapshot was collected (epoch millis, UTC). */
private long collectedAt;
/**
diff --git a/extensions/geonames/rest/pom.xml b/extensions/geonames/rest/pom.xml
index 35d104956..6f0d50fc1 100644
--- a/extensions/geonames/rest/pom.xml
+++ b/extensions/geonames/rest/pom.xml
@@ -101,4 +101,18 @@
</dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
</project>
diff --git a/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java b/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java
index 3f1ad8118..9bb5bf25a 100644
--- a/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java
+++ b/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java
@@ -34,6 +34,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+/**
+ * JAX-RS endpoint for GeoNames lookups (reverse geocoding, hierarchies, cities, capitals).
+ */
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@CrossOriginResourceSharing(
allowAllOrigins = true,
@@ -56,6 +59,17 @@ public class GeonamesEndPoint {
this.geonamesService = geonamesService;
}
+ /**
+ * Reverse-geocodes a latitude/longitude pair.
+ * <p>
+ * {@code latlon} must be {@code lat,lon} (comma-separated). Country names may be localized via {@code Accept-Language}.
+ *
+ * @param latlon comma-separated latitude and longitude
+ * @param language optional {@code Accept-Language} header for localized country names
+ * @return matching geoname entries
+ * @api.status 200 array org.apache.unomi.geonames.services.GeonameEntry Matching entries (may be empty).
+ * @api.example [{"itemId":"2988507","itemType":"geonameEntry","name":"Paris","countryCode":"FR","featureCode":"PPLC"}]
+ */
@GET
@Path("/reverseGeoCode/{latlon}")
public List<GeonameEntry> reverseGeoCode(@PathParam("latlon") String latlon, @HeaderParam("Accept-Language") String language) {
@@ -65,6 +79,15 @@ public class GeonamesEndPoint {
return entries;
}
+ /**
+ * Returns child geoname entries along a slash-separated hierarchy path.
+ *
+ * @param items path segments describing the parent hierarchy (e.g. continent/country/region)
+ * @param language optional {@code Accept-Language} header for localized country names
+ * @return child entries (up to 999)
+ * @api.status 200 org.apache.unomi.api.PartialList GeonameEntry page (list items are GeonameEntry; may be empty).
+ * @api.example {"list":[{"itemId":"3017382","itemType":"geonameEntry","name":"France","countryCode":"FR","featureCode":"PCLI"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+ */
@GET
@Path("/entries/{items:.*}")
public PartialList<GeonameEntry> getChildrenEntries(@PathParam("items") List<PathSegment> items, @HeaderParam("Accept-Language") String language) {
@@ -77,6 +100,15 @@ public class GeonamesEndPoint {
return list;
}
+ /**
+ * Returns child city entries along a slash-separated hierarchy path.
+ *
+ * @param items path segments describing the parent hierarchy
+ * @param language optional {@code Accept-Language} header for localized country names
+ * @return child cities (up to 999)
+ * @api.status 200 org.apache.unomi.api.PartialList GeonameEntry page (list items are GeonameEntry; may be empty).
+ * @api.example {"list":[{"itemId":"2988507","itemType":"geonameEntry","name":"Paris","countryCode":"FR","featureCode":"PPLC"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+ */
@GET
@Path("/cities/{items:.*}")
public PartialList<GeonameEntry> getChildrenCities(@PathParam("items") List<PathSegment> items, @HeaderParam("Accept-Language") String language) {
@@ -89,6 +121,15 @@ public class GeonamesEndPoint {
return list;
}
+ /**
+ * Returns the administrative hierarchy for the given geoname id.
+ *
+ * @param id the geoname identifier
+ * @param language optional {@code Accept-Language} header for localized country names
+ * @return hierarchy entries from root to the requested id
+ * @api.status 200 array org.apache.unomi.geonames.services.GeonameEntry Hierarchy entries (may be empty).
+ * @api.example [{"itemId":"6255148","itemType":"geonameEntry","name":"Europe","featureCode":"CONT"},{"itemId":"3017382","itemType":"geonameEntry","name":"France","countryCode":"FR","featureCode":"PCLI"}]
+ */
@GET
@Path("/hierarchy/{id}")
public List<GeonameEntry> getHierarchy(@PathParam("id") String id, @HeaderParam("Accept-Language") String language) {
@@ -97,6 +138,15 @@ public class GeonamesEndPoint {
return list;
}
+ /**
+ * Returns capital entries for the given parent geoname id.
+ *
+ * @param id the parent geoname identifier
+ * @param language optional {@code Accept-Language} header for localized country names
+ * @return capital entries
+ * @api.status 200 array org.apache.unomi.geonames.services.GeonameEntry Capital entries (may be empty).
+ * @api.example [{"itemId":"2988507","itemType":"geonameEntry","name":"Paris","countryCode":"FR","featureCode":"PPLC"}]
+ */
@GET
@Path("/capitals/{id}")
public List<GeonameEntry> getCapitalEntries(@PathParam("id") String id, @HeaderParam("Accept-Language") String language) {
diff --git a/extensions/groovy-actions/rest/pom.xml b/extensions/groovy-actions/rest/pom.xml
index e3620c88b..3bedcc985 100644
--- a/extensions/groovy-actions/rest/pom.xml
+++ b/extensions/groovy-actions/rest/pom.xml
@@ -109,6 +109,15 @@
</dependencies>
<build>
<plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
diff --git a/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java b/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java
index 8ba9ca26e..1f41cf6f3 100644
--- a/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java
+++ b/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java
@@ -52,10 +52,15 @@ public class GroovyActionsEndPoint {
}
/**
- * Save a groovy action file and create an actionType entry to allow to call this action
+ * Uploads a Groovy action script and registers a matching action type.
+ * <p>
+ * The multipart field {@code file} must be a {@code .groovy} file; the action id is derived from the filename.
*
- * @param file the file to save
- * @return Response of the API call
+ * @param file the Groovy script upload
+ * @return an empty success response
+ * @api.status 200 empty Action saved and action type registered.
+ * @api.status 500 empty Error reading or processing the uploaded file.
+ * @api.example {}
*/
@POST
@Path("/")
@@ -73,9 +78,11 @@ public class GroovyActionsEndPoint {
}
/**
- * Deletes the rule identified by the specified identifier.
+ * Deletes the Groovy action and its action type entry.
*
- * @param actionId the identifier of the groovy action that we want to delete
+ * @param actionId the action identifier
+ * @api.status 204 empty Action deleted.
+ * @api.example {}
*/
@DELETE
@Path("/{actionId}")
diff --git a/extensions/healthcheck/pom.xml b/extensions/healthcheck/pom.xml
index 49f88304d..cf97e9c66 100644
--- a/extensions/healthcheck/pom.xml
+++ b/extensions/healthcheck/pom.xml
@@ -166,6 +166,18 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <servletPaths>
+ <servletPath>
+ <className>org.apache.unomi.healthcheck.servlet.HealthCheckServlet</className>
+ <path>/health/check</path>
+ </servletPath>
+ </servletPaths>
+ </configuration>
+ </plugin>
</plugins>
</build>
</project>
diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java
index 1201a60b2..b8cb1ebf4 100644
--- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java
+++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java
@@ -23,13 +23,45 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
- * A Health Check response.
+ * One liveliness check result returned by GET /health/check.
*/
public class HealthCheckResponse {
+ /**
+ * Name of the check. Built-ins include {@code karaf} (always present) plus
+ * enabled providers such as {@code elasticsearch}, {@code opensearch},
+ * {@code cluster}, {@code persistence}, and {@code unomi} (bundle state).
+ * @api.example elasticsearch
+ */
private final String name;
+ /**
+ * Check outcome. One of: {@code LIVE} (ready to serve traffic),
+ * {@code UP} (running or still starting), {@code DOWN} (unavailable),
+ * {@code ERROR} (the check itself failed). The HTTP endpoint returns 200
+ * only when every item is {@code LIVE}; otherwise 206.
+ * @api.example LIVE
+ */
private final Status status;
+ /**
+ * Time spent collecting this check, in milliseconds.
+ * @api.example 42
+ */
private final long collectingTime;
+ /**
+ * Optional provider-specific details; omitted from JSON when empty.
+ * Values are strings, numbers, or booleans. Common keys:
+ * {@code error} / {@code error.cause} on failure or timeout;
+ * elasticsearch/opensearch: {@code cluster_name}, {@code status}
+ * (green/yellow/red), {@code timed_out}, {@code number_of_nodes},
+ * {@code number_of_data_nodes}, {@code active_primary_shards},
+ * {@code active_shards}, {@code relocating_shards},
+ * {@code initializing_shards}, {@code unassigned_shards};
+ * cluster: {@code cluster.size}, {@code cluster.node.<i>.uptime},
+ * {@code cluster.node.<i>.cpuload}, {@code cluster.node.<i>.loadAverage},
+ * {@code cluster.node.<i>.public}, {@code cluster.node.<i>.internal},
+ * {@code cluster.node.<i>.role}.
+ * @api.example {"cluster_name":"elasticsearch","status":"green","number_of_nodes":1}
+ */
private final Map<String, Object> data;
protected HealthCheckResponse(String name, Status status, long collectingTime, Map<String, Object> data) {
@@ -156,11 +188,18 @@ public class HealthCheckResponse {
}
}
+ /**
+ * Outcome of a single health check.
+ */
public enum Status {
- DOWN, //Not available
- UP, //Running or starting
- LIVE, //Ready to serve requests
- ERROR //Errors during check
+ /** Not available / not ready. */
+ DOWN,
+ /** Running or still starting (not yet ready to serve traffic). */
+ UP,
+ /** Ready to serve requests; required on every check for HTTP 200. */
+ LIVE,
+ /** The check itself failed (see {@code data.error} / {@code data.error.cause}). */
+ ERROR
}
}
diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java
index b25deb56c..e1f5f3755 100644
--- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java
+++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java
@@ -35,7 +35,17 @@ import java.io.IOException;
import java.util.List;
/**
- * A simple servlet that provides a health check endpoint.
+ * Liveliness checks for Unomi, registered at /health/check via the OSGi HttpService.
+ * Callers must have the OSGi role "health"; otherwise the servlet returns HTTP 403.
+ * On success the body is a JSON array of health check results.
+ *
+ * <p>HTTP 200 is returned when every check reports LIVE. HTTP 206 Partial Content is
+ * returned when at least one check is not LIVE (still starting, down, or in error).
+ *
+ * @api.status 200 array org.apache.unomi.healthcheck.HealthCheckResponse All checks report LIVE.
+ * @api.status 206 array org.apache.unomi.healthcheck.HealthCheckResponse At least one check is not LIVE (HTTP 206 Partial Content).
+ * @api.status 403 empty Missing the OSGi health role.
+ * @api.example [{"name":"karaf","status":"LIVE","collectingTime":1},{"name":"elasticsearch","status":"LIVE","collectingTime":42,"data":{"cluster_name":"elasticsearch","status":"green","number_of_nodes":1}}]
*/
public class HealthCheckServlet extends HttpServlet {
diff --git a/extensions/json-schema/rest/pom.xml b/extensions/json-schema/rest/pom.xml
index de0ef217a..dcd7c4ade 100644
--- a/extensions/json-schema/rest/pom.xml
+++ b/extensions/json-schema/rest/pom.xml
@@ -98,6 +98,15 @@
</dependencies>
<build>
<plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
diff --git a/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java b/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java
index 51dc39a48..2c221d0d3 100644
--- a/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java
+++ b/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java
@@ -54,9 +54,11 @@ public class JsonSchemaEndPoint {
}
/**
- * Get the list of installed Json Schema Ids
+ * Returns identifiers of all installed JSON schemas.
*
- * @return A Set of JSON schema ids
+ * @return installed JSON schema ids
+ * @api.status 200 array empty Schema id set (may be empty).
+ * @api.example ["profile","event"]
*/
@GET
@Path("/")
@@ -65,10 +67,14 @@ public class JsonSchemaEndPoint {
}
/**
- * Get a schema by it's id
+ * Returns the JSON schema document for the given id.
+ * <p>
+ * Request body is the schema id as a plain JSON string. When no schema matches, returns {@code null} (HTTP 200 with empty body).
*
- * @param id of the schema
- * @return Json schema matching the id
+ * @param id the schema identifier
+ * @return the schema JSON string, or {@code null} when missing
+ * @api.status 200 empty Schema JSON string, or empty body when missing.
+ * @api.example "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\"}"
*/
@POST
@Path("/query")
@@ -81,10 +87,13 @@ public class JsonSchemaEndPoint {
}
/**
- * Save a JSON schema
+ * Saves a JSON schema document.
*
- * @param jsonSchema the schema as string to save
- * @return Response of the API call
+ * @param jsonSchema the schema JSON as plain text or JSON body
+ * @return an empty success response
+ * @api.status 200 empty Schema saved.
+ * @api.status 400 empty Invalid or unreadable schema body.
+ * @api.example "{\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"self\":{\"vendor\":\"com.example\",\"target\":\"events\",\"name\":\"customEvent\",\"format\":\"jsonschema\",\"version\":\"1-0-0\"},\"title\":\"CustomEvent\",\"type\":\"object\",\"allOf\":[{\"$ref\":\"https://unomi.apache.org/schemas/json/event/1-0-0\"}],\"properties\":{\"properties\":{\"type\":\"object\",\"properties\":{\"category\":{\"type\":\"string\"}}}},\"unevaluatedProperties\":false}"
*/
@POST
@Path("/")
@@ -100,9 +109,14 @@ public class JsonSchemaEndPoint {
}
/**
- * Deletes a JSON schema by it's id.
+ * Deletes the JSON schema with the given id.
+ * <p>
+ * Request body is the schema id as a plain JSON string.
*
- * @param id the identifier of the JSON schema that we want to delete
+ * @param id the schema identifier
+ * @return {@code true} when the schema was deleted, {@code false} when it did not exist
+ * @api.status 200 empty Deletion result flag.
+ * @api.example true
*/
@POST
@Path("/delete")
@@ -111,10 +125,13 @@ public class JsonSchemaEndPoint {
}
/**
- * Being able to validate a given event is useful when you want to develop custom events and associated schemas
+ * Validates a single event JSON document against installed schemas.
*
- * @param event the event to be validated
- * @return Validation error messages if there is some
+ * @param event the event JSON to validate
+ * @return validation errors (empty collection when valid)
+ * @api.status 200 array org.apache.unomi.schema.api.ValidationError Validation errors (may be empty).
+ * @api.status 400 empty Invalid or unreadable event body.
+ * @api.example [{"error":"$.eventType: is missing but it is required"}]
*/
@POST
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@@ -130,10 +147,13 @@ public class JsonSchemaEndPoint {
}
/**
- * Being able to validate a given list of event is useful when you want to develop custom events and associated schemas
+ * Validates a batch of events JSON against installed schemas.
*
- * @param events the events to be validated
- * @return Validation error messages if there is some grouped per event type
+ * @param events the events JSON to validate
+ * @return validation errors grouped by event type (empty map when all valid)
+ * @api.status 200 empty Event type to validation error set map (may be empty).
+ * @api.status 400 empty Invalid or unreadable events body.
+ * @api.example {"view":{"error":"$.scope: is missing but it is required"}}
*/
@POST
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
diff --git a/extensions/lists-extension/rest/pom.xml b/extensions/lists-extension/rest/pom.xml
index ba6ae96e1..b069f3a18 100644
--- a/extensions/lists-extension/rest/pom.xml
+++ b/extensions/lists-extension/rest/pom.xml
@@ -95,4 +95,18 @@
</dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
</project>
diff --git a/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java b/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java
index bd14b4780..b151398e6 100644
--- a/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java
+++ b/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java
@@ -33,7 +33,7 @@ import javax.ws.rs.core.MediaType;
/**
- * @author Christophe Laprun
+ * JAX-RS endpoint for static {@link UserList} CRUD and metadata queries.
*/
@Produces(MediaType.APPLICATION_JSON)
@CrossOriginResourceSharing(
@@ -57,30 +57,69 @@ public class UserListServiceEndPoint {
this.userListService = userListService;
}
+ /**
+ * Returns the first page of user list metadata (offset 0, size 50).
+ *
+ * @return user list metadata page
+ * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata; may be empty).
+ * @api.example {"list":[{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+ */
@GET
@Path("/")
public PartialList<Metadata> getListMetadatas() {
return userListService.getListMetadatas(0, 50, null);
}
+ /**
+ * Returns user list metadata matching the given query.
+ *
+ * @param query the query lists must match
+ * @return a paged list of matching metadata
+ * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+ */
@POST
@Path("/query")
public PartialList<Metadata> getListMetadatas(Query query) {
return userListService.getListMetadatas(query);
}
+ /**
+ * Returns the user list with the given ID.
+ * When the list does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
+ *
+ * @param listId the list identifier
+ * @return the user list, or {@code null} when missing
+ * @api.status 200 org.apache.unomi.lists.UserList List found, or empty body when missing.
+ * @api.example {"itemId":"newsletter-subscribers","itemType":"userList","metadata":{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}}
+ */
@GET
@Path("/{listId}")
public UserList load(@PathParam("listId") String listId) {
return userListService.load(listId);
}
+ /**
+ * Persists the specified user list.
+ *
+ * @param list the list to save
+ * @api.status 204 empty List created or updated.
+ * @api.example {"itemId":"newsletter-subscribers","itemType":"userList","metadata":{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}}
+ */
@POST
@Path("/")
public void save(UserList list) {
userListService.save(list);
}
+ /**
+ * Deletes the user list with the given ID.
+ *
+ * @param listId the list identifier
+ * @api.status 204 empty List deleted.
+ * @api.example {"itemId":"newsletter-subscribers","itemType":"userList","metadata":{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}}
+ */
@DELETE
@Path("/{listId}")
public void delete(@PathParam("listId") String listId) {
diff --git a/extensions/privacy-extension/rest/pom.xml b/extensions/privacy-extension/rest/pom.xml
index 6f02f40c1..0386e5ef8 100644
--- a/extensions/privacy-extension/rest/pom.xml
+++ b/extensions/privacy-extension/rest/pom.xml
@@ -92,5 +92,19 @@
</dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
</project>
diff --git a/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java b/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java
index f804ac164..79cfb3a3a 100644
--- a/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java
+++ b/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java
@@ -29,7 +29,8 @@ import javax.ws.rs.core.Response;
import java.util.List;
/**
- * REST API end point for privacy service
+ * REST endpoint for privacy / GDPR-style profile operations and server identity.
+ * Covers anonymous browsing, event filters, property erasure, anonymization, and profile deletion.
*/
@Produces(MediaType.APPLICATION_JSON)
@CrossOriginResourceSharing(
@@ -43,22 +44,55 @@ public class PrivacyServiceEndPoint {
@Reference
private PrivacyService privacyService;
+ /**
+ * Sets the privacy service.
+ *
+ * @param privacyService the privacy service
+ */
public void setPrivacyService(PrivacyService privacyService) {
this.privacyService = privacyService;
}
+ /**
+ * Returns build/capability information for the Unomi server (first entry when multiple bundles report).
+ *
+ * @return server identity and capability metadata
+ * @api.status 200 org.apache.unomi.api.ServerInfo Unomi server info.
+ * @api.example {"serverIdentifier":"unomi","serverVersion":"3.1.0-SNAPSHOT","serverBuildNumber":"1","serverScmBranch":"main","capabilities":{},"eventTypes":[],"logoLines":[]}
+ */
@GET
@Path("/info")
public ServerInfo getServerInfo() {
return privacyService.getServerInfo();
}
+ /**
+ * Returns build/capability information for all reporting server bundles.
+ *
+ * @return list of server info records (Unomi first)
+ * @api.status 200 array org.apache.unomi.api.ServerInfo Server info entries (may be a single Unomi entry).
+ * @api.example [{"serverIdentifier":"unomi","serverVersion":"3.1.0-SNAPSHOT","serverBuildNumber":"1","serverScmBranch":"main"}]
+ */
@GET
@Path("/infos")
public List<ServerInfo> getServerInfos() {
return privacyService.getServerInfos();
}
+ /**
+ * Deletes or purges privacy-related data for a profile.
+ * <p>
+ * Flag precedence: {@code purgeAll=true} purges associated data then the profile;
+ * else {@code withData=true} deletes profile data without full purge;
+ * else only the profile record is deleted.
+ *
+ * @param profileId the profile identifier
+ * @param withData when {@code true} (and not purgeAll), delete associated browsing data as well
+ * @param purgeAll when {@code true}, purge all associated data then delete the profile
+ * @return HTTP 200 when the operation completes
+ * @api.status 200 empty Profile / data deletion completed.
+ * @api.example {}
+ */
@DELETE
@Path("/profiles/{profileId}")
public Response deleteProfileData(@PathParam("profileId") String profileId, @QueryParam("withData") @DefaultValue("false") boolean withData,
@@ -73,18 +107,46 @@ public class PrivacyServiceEndPoint {
return Response.ok().build();
}
+ /**
+ * Anonymizes the profile in the given scope (fires anonymize events / clears identifying properties).
+ *
+ * @param profileId the profile identifier
+ * @param scope scope used when raising anonymization events
+ * @api.status 204 empty Profile anonymization requested.
+ * @api.example {}
+ */
@POST
@Path("/profiles/{profileId}/anonymize")
public void anonymizeProfile(@PathParam("profileId") String profileId, @QueryParam("scope") String scope) {
privacyService.anonymizeProfile(profileId, scope);
}
+ /**
+ * Returns whether the profile requires anonymous browsing.
+ *
+ * @param profileId the profile identifier
+ * @return {@code true} when anonymous browsing is required
+ * @api.status 200 empty Anonymous-browsing flag.
+ * @api.example false
+ */
@GET
@Path("/profiles/{profileId}/anonymousBrowsing")
public Boolean isAnonymousBrowsing(@PathParam("profileId") String profileId) {
return privacyService.isRequireAnonymousBrowsing(profileId);
}
+ /**
+ * Turns on anonymous browsing for the profile.
+ * When {@code anonymizePastBrowsing=true}, past browsing data is anonymized first.
+ *
+ * @param profileId the profile identifier
+ * @param past when {@code true}, anonymize historical browsing data before enabling the flag
+ * @param scope scope used for related events
+ * @return HTTP 200 when the flag was set; HTTP 500 when the service reports failure
+ * @api.status 200 empty Anonymous browsing enabled.
+ * @api.status 500 empty Failed to enable anonymous browsing.
+ * @api.example {}
+ */
@POST
@Path("/profiles/{profileId}/anonymousBrowsing")
public Response activateAnonymousBrowsing(@PathParam("profileId") String profileId, @QueryParam("anonymizePastBrowsing") @DefaultValue("false") boolean past, @QueryParam("scope") String scope) {
@@ -95,6 +157,16 @@ public class PrivacyServiceEndPoint {
return r ? Response.ok().build() : Response.serverError().build();
}
+ /**
+ * Turns off anonymous browsing for the profile.
+ *
+ * @param profileId the profile identifier
+ * @param scope scope used for related events
+ * @return HTTP 200 when the flag was cleared; HTTP 500 when the service reports failure
+ * @api.status 200 empty Anonymous browsing disabled.
+ * @api.status 500 empty Failed to disable anonymous browsing.
+ * @api.example {}
+ */
@DELETE
@Path("/profiles/{profileId}/anonymousBrowsing")
public Response deactivateAnonymousBrowsing(@PathParam("profileId") String profileId, @QueryParam("scope") String scope) {
@@ -102,12 +174,29 @@ public class PrivacyServiceEndPoint {
return r ? Response.ok().build() : Response.serverError().build();
}
+ /**
+ * Returns event type ids that are filtered (not collected) for this profile.
+ *
+ * @param profileId the profile identifier
+ * @return filtered event type identifiers
+ * @api.status 200 array empty Event type id strings (may be empty).
+ * @api.example ["view","login"]
+ */
@GET
@Path("/profiles/{profileId}/eventFilters")
public List<String> getEventFilters(@PathParam("profileId") String profileId) {
return privacyService.getFilteredEventTypes(profileId);
}
+ /**
+ * Replaces the list of filtered event types for this profile.
+ *
+ * @param profileId the profile identifier
+ * @param eventFilters event type ids to filter (body JSON array of strings)
+ * @return HTTP 200 when stored
+ * @api.status 200 empty Event filters updated.
+ * @api.example ["view","login"]
+ */
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/profiles/{profileId}/eventFilters")
@@ -116,6 +205,15 @@ public class PrivacyServiceEndPoint {
return Response.ok().build();
}
+ /**
+ * Removes a single property from the profile (privacy erasure of one field).
+ *
+ * @param profileId the profile identifier
+ * @param propertyName property path to remove (for example {@code email} or {@code properties.email})
+ * @return HTTP 200 when removed
+ * @api.status 200 empty Property removed from the profile.
+ * @api.example {}
+ */
@DELETE
@Path("/profiles/{profileId}/properties/{propertyName}")
public Response removeProperty(@PathParam("profileId") String profileId, @PathParam("propertyName") String propertyName) {
diff --git a/extensions/router/router-rest/pom.xml b/extensions/router/router-rest/pom.xml
index cb56bf0ff..b7b9233db 100644
--- a/extensions/router/router-rest/pom.xml
+++ b/extensions/router/router-rest/pom.xml
@@ -107,6 +107,15 @@
<build>
<plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
diff --git a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java
index 7d180ee49..df966f634 100644
--- a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java
+++ b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java
@@ -23,16 +23,19 @@ import javax.ws.rs.core.MediaType;
import java.util.List;
/**
- * Created by amidani on 26/06/2017.
+ * Abstract JAX-RS base for router import/export configuration CRUD.
+ *
+ * @param <T> configuration item type
*/
public abstract class AbstractConfigurationServiceEndpoint<T> {
protected ImportExportConfigurationService<T> configurationService;
/**
- * Retrieves all the configurations.
+ * Returns all router configurations of this type.
*
- * @return all the configurations.
+ * @return all configurations (may be empty)
+ * @api.status 200 array empty Configuration list (may be empty).
*/
@GET
@Path("/")
@@ -42,6 +45,13 @@ public abstract class AbstractConfigurationServiceEndpoint<T> {
return this.configurationService.getAll();
}
+ /**
+ * Creates or updates a router configuration.
+ *
+ * @param configuration the configuration to save
+ * @return the persisted configuration
+ * @api.status 200 empty Configuration saved.
+ */
@POST
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@@ -49,10 +59,12 @@ public abstract class AbstractConfigurationServiceEndpoint<T> {
public abstract T saveConfiguration(T configuration);
/**
- * Retrieves a configuration by id.
+ * Returns the configuration with the given id.
+ * When it does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
- * @param configId config id
- * @return the configuration that matches the given id.
+ * @param configId the configuration identifier
+ * @return the configuration, or {@code null} when missing
+ * @api.status 200 empty Configuration found, or empty body when missing.
*/
@GET
@Path("/{configId}")
@@ -63,9 +75,10 @@ public abstract class AbstractConfigurationServiceEndpoint<T> {
}
/**
- * Delete a configuration by id.
+ * Deletes the configuration with the given id.
*
- * @param configId config id
+ * @param configId the configuration identifier
+ * @api.status 204 empty Configuration deleted.
*/
@DELETE
@Path("/{configId}")
diff --git a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java
index 317345209..4371e2d13 100644
--- a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java
+++ b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java
@@ -37,6 +37,7 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
/**
* A JAX-RS endpoint to manage {@link ExportConfiguration}s.
@@ -75,9 +76,37 @@ public class ExportConfigurationServiceEndPoint extends AbstractConfigurationSer
}
/**
- * Save the given export configuration.
+ * Returns all export configurations.
*
- * @return the export configuration saved.
+ * @return all export configurations (may be empty)
+ * @api.status 200 array org.apache.unomi.router.api.ExportConfiguration Export configuration list (may be empty).
+ * @api.example [{"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}]
+ */
+ @Override
+ public List<ExportConfiguration> getConfigurations() {
+ return super.getConfigurations();
+ }
+
+ /**
+ * Returns the export configuration with the given id.
+ *
+ * @param configId the configuration identifier
+ * @return the export configuration, or {@code null} when missing
+ * @api.status 200 org.apache.unomi.router.api.ExportConfiguration Export configuration found, or empty body when missing.
+ * @api.example {"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}
+ */
+ @Override
+ public ExportConfiguration getConfiguration(String configId) {
+ return super.getConfiguration(configId);
+ }
+
+ /**
+ * Creates or updates an export configuration and persists it for Camel routes.
+ *
+ * @param exportConfiguration the export configuration to save
+ * @return the persisted export configuration
+ * @api.status 200 org.apache.unomi.router.api.ExportConfiguration Export configuration saved.
+ * @api.example {"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}
*/
@Override
public ExportConfiguration saveConfiguration(ExportConfiguration exportConfiguration) {
@@ -86,17 +115,25 @@ public class ExportConfigurationServiceEndPoint extends AbstractConfigurationSer
return exportConfigSaved;
}
+ /**
+ * Deletes the export configuration with the given id.
+ *
+ * @param configId the configuration identifier
+ * @api.status 204 empty Export configuration deleted.
+ * @api.example {"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}
+ */
@Override
public void deleteConfiguration(String configId) {
this.configurationService.delete(configId);
}
/**
- * Save/Update the given import configuration.
- * Prepare the file to be processed with Camel routes
+ * Runs a one-shot profile export for the given export configuration and returns CSV.
*
- * @param exportConfiguration configuration
- * @return OK / NOK Http Code.
+ * @param exportConfiguration the export configuration describing the segment and columns
+ * @return CSV attachment with exported profiles
+ * @api.status 200 empty CSV export payload ({@code text/csv} with Content-Disposition attachment).
+ * @api.example itemId,properties.email\nprofile-1,user@example.com
*/
@POST
@Path("/oneshot")
diff --git a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java
index fa87f47ad..e8d639df1 100644
--- a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java
+++ b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java
@@ -44,6 +44,8 @@ import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
/**
* A JAX-RS endpoint to manage {@link ImportConfiguration}s.
*/
@@ -74,9 +76,37 @@ public class ImportConfigurationServiceEndPoint extends AbstractConfigurationSer
}
/**
- * Save the given import configuration.
+ * Returns all import configurations.
+ *
+ * @return all import configurations (may be empty)
+ * @api.status 200 array org.apache.unomi.router.api.ImportConfiguration Import configuration list (may be empty).
+ * @api.example [{"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}]
+ */
+ @Override
+ public List<ImportConfiguration> getConfigurations() {
+ return super.getConfigurations();
+ }
+
+ /**
+ * Returns the import configuration with the given id.
+ *
+ * @param configId the configuration identifier
+ * @return the import configuration, or {@code null} when missing
+ * @api.status 200 org.apache.unomi.router.api.ImportConfiguration Import configuration found, or empty body when missing.
+ * @api.example {"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}
+ */
+ @Override
+ public ImportConfiguration getConfiguration(String configId) {
+ return super.getConfiguration(configId);
+ }
+
+ /**
+ * Creates or updates an import configuration and persists it for Camel routes.
*
- * @return the import configuration saved.
+ * @param importConfiguration the import configuration to save
+ * @return the persisted import configuration
+ * @api.status 200 org.apache.unomi.router.api.ImportConfiguration Import configuration saved.
+ * @api.example {"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}
*/
@Override
public ImportConfiguration saveConfiguration(ImportConfiguration importConfiguration) {
@@ -86,18 +116,30 @@ public class ImportConfigurationServiceEndPoint extends AbstractConfigurationSer
return importConfigSaved;
}
+ /**
+ * Deletes the import configuration with the given id.
+ *
+ * @param configId the configuration identifier
+ * @api.status 204 empty Import configuration deleted.
+ * @api.example {"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}
+ */
@Override
public void deleteConfiguration(String configId) {
this.configurationService.delete(configId);
}
/**
- * Save/Update the given import configuration.
- * Prepare the file to be processed with Camel routes
+ * Uploads a one-shot CSV file for an existing import configuration.
+ * <p>
+ * The file is stored under the configured one-shot upload directory as {@code {importConfigId}.csv}
+ * for subsequent Camel processing.
*
- * @param file file
- * @param importConfigId config
- * @return OK / NOK Http Code.
+ * @param importConfigId the import configuration id (multipart field)
+ * @param file the CSV file upload (multipart field)
+ * @return an empty success response
+ * @api.status 200 empty CSV uploaded and ready for processing.
+ * @api.status 500 empty Error writing the uploaded file.
+ * @api.example {}
*/
@POST
@Path("/oneshot")
diff --git a/extensions/salesforce-connector/rest/pom.xml b/extensions/salesforce-connector/rest/pom.xml
index aaa1fef1f..ee589fcac 100644
--- a/extensions/salesforce-connector/rest/pom.xml
+++ b/extensions/salesforce-connector/rest/pom.xml
@@ -109,6 +109,15 @@
<build>
<plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
diff --git a/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java b/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java
index d882a022c..e68239bc4 100644
--- a/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java
+++ b/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java
@@ -34,6 +34,9 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
+/**
+ * JAX-RS endpoint exposing Salesforce connector bundle metadata and API limits.
+ */
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@CrossOriginResourceSharing(
allowAllOrigins = true,
@@ -64,6 +67,13 @@ public class SFDCEndPoint {
this.bundleContext = bundleContext;
}
+ /**
+ * Returns OSGi bundle headers for the Salesforce connector extension.
+ *
+ * @return bundle header name to value map
+ * @api.status 200 empty Bundle header map.
+ * @api.example {"Bundle-Name":"Apache Unomi :: Extensions :: Salesforce Connector","Bundle-Version":"3.0.0"}
+ */
@GET
@Path("/version")
public Map<String,String> getVersion() {
@@ -77,6 +87,13 @@ public class SFDCEndPoint {
return versionInfo;
}
+ /**
+ * Returns current Salesforce API limits from the connected org.
+ *
+ * @return limit name to usage snapshot map
+ * @api.status 200 empty Salesforce limits map (shape depends on the connected org).
+ * @api.example {"DailyApiRequests":{"Max":15000,"Remaining":14200}}
+ */
@GET
@Path("/limits")
public Map<String,Object> getLimits() {
diff --git a/pom.xml b/pom.xml
index 00b844bdb..233143697 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,7 @@
<jakarta-json-api.version>2.1.2</jakarta-json-api.version>
<swagger-ui.version>5.27.1</swagger-ui.version>
<cxf.version>3.6.8</cxf.version>
+ <openapi.enrich.version>0.1.0-SNAPSHOT</openapi.enrich.version>
<rs-api.version>2.1</rs-api.version>
<groovy.version>4.0.28</groovy.version>
<httpclient-osgi.version>4.5.14</httpclient-osgi.version>
@@ -921,6 +922,19 @@
<!-- Broken {@link}/HTML/syntax fail the build; missing @param/@return do not (use -Pjavadoc-tags-warn) -->
<failOnError>true</failOnError>
<failOnWarnings>false</failOnWarnings>
+ <!-- UNOMI-963: OpenAPI enrichment packs read @api.status / @api.example from source -->
+ <tags>
+ <tag>
+ <name>api.status</name>
+ <placement>a</placement>
+ <head>API Status:</head>
+ </tag>
+ <tag>
+ <name>api.example</name>
+ <placement>a</placement>
+ <head>API Example:</head>
+ </tag>
+ </tags>
</configuration>
</plugin>
<plugin>
@@ -1027,6 +1041,19 @@
<artifactId>license-maven-plugin</artifactId>
<version>${license-maven-plugin.version}</version>
</plugin>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <version>${openapi.enrich.version}</version>
+ <executions>
+ <execution>
+ <id>generate-openapi-doc</id>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
<plugin>
<groupId>org.jasig.maven</groupId>
<artifactId>maven-notice-plugin</artifactId>
diff --git a/rest/pom.xml b/rest/pom.xml
index d57a09b24..9b15ad8b1 100644
--- a/rest/pom.xml
+++ b/rest/pom.xml
@@ -210,10 +210,34 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
+
+ <!-- UNOMI-963: OpenAPI Javadoc enrichment (embedded into this OSGi bundle) -->
+ <dependency>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>cxf-openapi-enricher</artifactId>
+ <version>${openapi.enrich.version}</version>
+ </dependency>
+ <!-- Direct dep so parent Embed-Dependency=* embeds the model JAR too (not only transitive). -->
+ <dependency>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-model</artifactId>
+ <version>${openapi.enrich.version}</version>
+ </dependency>
</dependencies>
<build>
<plugins>
+ <plugin>
+ <groupId>dev.inoyu.openapi</groupId>
+ <artifactId>openapi-enrich-maven-plugin</artifactId>
+ <configuration>
+ <extraSourceRoots>
+ <extraSourceRoot>${project.basedir}/../api/src/main/java</extraSourceRoot>
+ <!-- TraceNode on ContextResponse / EventCollectorResponse -->
+ <extraSourceRoot>${project.basedir}/../tracing/tracing-api/src/main/java</extraSourceRoot>
+ </extraSourceRoots>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
@@ -244,9 +268,33 @@
<extensions>true</extensions>
<configuration>
<instructions>
+ <!--
+ Classloader design (UNOMI-963):
+
+ Embed ONLY private deps that must not be shared OSGi APIs:
+ - opencsv (pre-existing Unomi pattern)
+ - cxf-openapi-enricher + openapi-enrich-model (Inoyu-owned)
+
+ Do NOT embed Jackson / CXF / Swagger / SLF4J / OSGi — those stay
+ Import-Package so EnrichingOpenApiCustomizer and CXF OpenApiFeature
+ share the same OpenAPI/Jackson classes (avoids LinkageError).
+
+ Exclude dev.inoyu.openapi.enrich.* from Import-Package: those packages
+ are on Bundle-ClassPath via the embedded JARs, not exported bundles.
+
+ Do not set Private-Package: it replaces BND defaults and can drop
+ org.apache.unomi.rest.service.impl from this bundle.
+ -->
<Import-Package>
+ !dev.inoyu.openapi.enrich.*,
*
</Import-Package>
+ <Embed-Dependency>
+ opencsv,
+ cxf-openapi-enricher,
+ openapi-enrich-model
+ </Embed-Dependency>
+ <Embed-Transitive>false</Embed-Transitive>
</instructions>
</configuration>
</plugin>
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java
index b40061e20..91397d3ac 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java
@@ -73,6 +73,8 @@ public class CampaignsServiceEndPoint {
* Returns metadata for all campaigns.
*
* @return campaign metadata for every stored campaign
+ * @api.status 200 array org.apache.unomi.api.Metadata Campaign metadata for all stored campaigns (may be empty).
+ * @api.example [{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}]
*/
@GET
@Path("/")
@@ -82,8 +84,13 @@ public class CampaignsServiceEndPoint {
/**
* Saves the specified campaign in the context server and creates associated {@link Rule}s if the campaign is enabled.
+ * Body is a full {@link Campaign}: {@code metadata}, {@code entryCondition} (JSON field {@code type} + {@code parameterValues}),
+ * date range, cost, and optional {@code primaryGoal}.
*
* @param campaign the Campaign to be saved
+ * @api.status 204 empty Campaign created or updated.
+ * @api.status 400 empty Invalid or unreadable campaign body.
+ * @api.example {"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true},"startDate":"2024-06-01T00:00:00.000Z","endDate":"2024-08-31T23:59:59.000Z","entryCondition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"cost":5000.0,"currency":"USD","primaryGoal":"checkout-goal","timezone":"UTC"}
*/
@POST
@Path("/")
@@ -96,6 +103,9 @@ public class CampaignsServiceEndPoint {
*
* @param query the query used to filter campaigns
* @return metadata for campaigns that match the query
+ * @api.status 200 array org.apache.unomi.api.Metadata Matching campaign metadata (may be empty).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example [{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}]
*/
@POST
@Path("/query")
@@ -105,9 +115,13 @@ public class CampaignsServiceEndPoint {
/**
* Returns detailed campaign data matching the given query.
+ * Each list element includes engagement metrics and the embedded {@link Campaign}.
*
* @param query the query specifying which campaigns to include
* @return a paged list of campaign details
+ * @api.status 200 org.apache.unomi.api.PartialList Campaign details page (list items are CampaignDetail).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"engagedProfiles":120,"campaignSessionViews":450,"campaignSessionSuccess":38,"numberOfGoals":2,"conversionRate":0.084,"campaign":{"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}}}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/query/detailed")
@@ -117,9 +131,12 @@ public class CampaignsServiceEndPoint {
/**
* Returns detailed data for the campaign with the given ID.
+ * When the campaign does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param campaignID the campaign identifier
* @return the campaign detail, or {@code null} when no such campaign exists
+ * @api.status 200 org.apache.unomi.api.campaigns.CampaignDetail Campaign detail found, or empty body when missing.
+ * @api.example {"engagedProfiles":120,"campaignSessionViews":450,"campaignSessionSuccess":38,"numberOfGoals":2,"conversionRate":0.084,"campaign":{"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}}}
*/
@GET
@Path("/{campaignID}/detailed")
@@ -129,9 +146,12 @@ public class CampaignsServiceEndPoint {
/**
* Returns the campaign definition for the given ID.
+ * When the campaign does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param campaignID the campaign identifier
* @return the campaign, or {@code null} when no such campaign exists
+ * @api.status 200 org.apache.unomi.api.campaigns.Campaign Campaign found, or empty body when missing.
+ * @api.example {"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true},"startDate":"2024-06-01T00:00:00.000Z","endDate":"2024-08-31T23:59:59.000Z","entryCondition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"cost":5000.0,"currency":"USD","primaryGoal":"checkout-goal","timezone":"UTC"}
*/
@GET
@Path("/{campaignID}")
@@ -143,6 +163,8 @@ public class CampaignsServiceEndPoint {
* Removes the campaign associated with the specified identifier, also removing associated rules if needed.
*
* @param campaignID the identifier of the campaign to be removed
+ * @api.status 204 empty Campaign deleted.
+ * @api.example {"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true},"startDate":"2024-06-01T00:00:00.000Z","endDate":"2024-08-31T23:59:59.000Z","entryCondition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"cost":5000.0,"currency":"USD","primaryGoal":"checkout-goal","timezone":"UTC"}
*/
@DELETE
@Path("/{campaignID}")
@@ -152,8 +174,12 @@ public class CampaignsServiceEndPoint {
/**
* Saves the specified campaign event in the context server.
+ * Body is a full {@link CampaignEvent}: {@code metadata}, {@code eventDate}, {@code campaignId}, and optional cost fields.
*
* @param campaignEvent the CampaignEvent to be saved
+ * @api.status 204 empty Campaign event created or updated.
+ * @api.status 400 empty Invalid or unreadable campaign event body.
+ * @api.example {"itemId":"summer-launch","itemType":"campaignevent","metadata":{"id":"summer-launch","name":"Campaign launch","scope":"mysite","enabled":true},"eventDate":"2024-06-01T09:00:00.000Z","campaignId":"summer-sale","cost":1000.0,"currency":"USD","timezone":"UTC"}
*/
@POST
@Path("/event")
@@ -165,6 +191,8 @@ public class CampaignsServiceEndPoint {
* Removes the campaign event associated with the specified identifier.
*
* @param campaignEventID the identifier of the campaign event to be removed
+ * @api.status 204 empty Campaign event deleted.
+ * @api.example {"itemId":"summer-launch","itemType":"campaignevent","metadata":{"id":"summer-launch","name":"Campaign launch","scope":"mysite","enabled":true},"eventDate":"2024-06-01T09:00:00.000Z","campaignId":"summer-sale","cost":1000.0,"currency":"USD","timezone":"UTC"}
*/
@DELETE
@Path("/event/{eventId}")
@@ -177,6 +205,9 @@ public class CampaignsServiceEndPoint {
*
* @param query the query specifying which campaign events to include
* @return a paged list of matching campaign events
+ * @api.status 200 org.apache.unomi.api.PartialList Campaign events page (list items are CampaignEvent).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"itemId":"summer-launch","itemType":"campaignevent","metadata":{"id":"summer-launch","name":"Campaign launch","scope":"mysite","enabled":true},"eventDate":"2024-06-01T09:00:00.000Z","campaignId":"summer-sale","cost":1000.0,"currency":"USD","timezone":"UTC"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/events/query")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java
index 2197b5c72..6fae685b0 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java
@@ -73,6 +73,8 @@ public class ClientEndpoint {
* Handles CORS preflight for the client endpoint.
*
* @return an empty CORS preflight response
+ * @api.status 204 empty CORS preflight accepted.
+ * @api.example {}
*/
@OPTIONS
@Path("/client/{operation}/{param}")
@@ -82,11 +84,18 @@ public class ClientEndpoint {
/**
* Exports profile data for the requested client operation.
+ * <p>
+ * Currently supports {@code myprofile} with a download format in {@code param}
+ * (must appear in the {@code allowedProfileDownloadFormats} configuration).
+ * Requires a profile id cookie; CSV layout can be vertical via the {@code vertical} query parameter.
*
- * @param operation the export operation
- * @param param the operation parameter
- * @return the export response
- * @throws JsonProcessingException if response serialization fails
+ * @param operation the export operation (currently {@code myprofile})
+ * @param param the download format ({@code json}, {@code yaml}, {@code csv}, or {@code text})
+ * @return the export response with attachment headers
+ * @api.status 200 empty Profile export payload (JSON, YAML, CSV, or plain text depending on {@code param}).
+ * @api.status 404 empty Unknown operation, format, or missing profile cookie/profile.
+ * @api.status 500 empty {@code param} is not in {@code allowedProfileDownloadFormats}.
+ * @api.example {"itemId":"profile-42","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"]}
*/
@GET
@Path("/client/{operation}/{param}")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java
index 23fdc3232..05b3ee935 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java
@@ -81,6 +81,8 @@ public class ClusterServiceEndPoint {
* Returns cluster nodes known to this server instance.
*
* @return the available cluster nodes
+ * @api.status 200 array org.apache.unomi.api.ClusterNode Cluster nodes (may be empty).
+ * @api.example [{"itemId":"node-1","itemType":"clusterNode","publicHostAddress":"10.0.0.1","master":true,"data":true}]
*/
@GET
@Path("/")
@@ -90,8 +92,13 @@ public class ClusterServiceEndPoint {
/**
* Removes all data before the specified date from the context server.
+ * <p>
+ * The date must use {@code yyyy-MM-dd} format; unparseable values are logged and no purge is performed.
*
- * @param date the Date before which all data needs to be removed
+ * @param date the date before which all data is removed ({@code yyyy-MM-dd})
+ * @deprecated As of version 1.1.0-incubating, use scope-scoped purge instead
+ * @api.status 204 empty Purge requested when the date parsed successfully.
+ * @api.example {}
*/
@GET
@Path("/purge/{date}")
@@ -108,7 +115,9 @@ public class ClusterServiceEndPoint {
/**
* Removes all data associated with the provided scope.
*
- * @param scope the scope for which we want to remove data
+ * @param scope the scope for which data is removed
+ * @api.status 204 empty Scope data purge requested.
+ * @api.example {}
*/
@DELETE
@Path("{scope}")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
index 7543b8e35..c17076d26 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
@@ -53,6 +53,8 @@ import java.util.stream.Collectors;
/**
* REST endpoint for context.js and context.json requests.
+ * Public CDP entry points used by client SDKs to resolve profile/session context, ingest
+ * events, evaluate personalization filters, and return consented profile data.
*/
@Consumes(MediaType.APPLICATION_JSON)
@CrossOriginResourceSharing(allowAllOrigins = true, allowCredentials = true)
@@ -90,6 +92,8 @@ public class ContextJsonEndpoint {
* Handles CORS preflight for context.js.
*
* @return an empty CORS preflight response
+ * @api.status 204 empty CORS preflight (no body).
+ * @api.example {}
*/
@OPTIONS
@Path("/context.js")
@@ -101,6 +105,8 @@ public class ContextJsonEndpoint {
* Handles CORS preflight for context.json.
*
* @return an empty CORS preflight response
+ * @api.status 204 empty CORS preflight (no body).
+ * @api.example {}
*/
@OPTIONS
@Path("/context.json")
@@ -110,17 +116,23 @@ public class ContextJsonEndpoint {
/**
* Processes a context.js POST request.
+ * Same evaluation as {@code /context.json}, but returns {@code text/plain} JavaScript that
+ * assigns the {@link ContextResponse} JSON to {@code window.digitalData} / {@code cxs}.
*
* @param contextRequest the context request payload
- * @param personaId optional persona identifier
- * @param sessionId optional session identifier
- * @param timestampAsLong optional request timestamp
- * @param invalidateProfile whether to invalidate the profile
- * @param invalidateSession whether to invalidate the session
- * @param explain whether to include tracing details
+ * @param personaId optional persona identifier used to load a persona instead of a live profile
+ * @param sessionId optional session identifier (prefer body {@code sessionId} over the query string)
+ * @param timestampAsLong optional request time as epoch millis; defaults to server now
+ * @param invalidateProfile whether to drop and recreate the profile cookie/state
+ * @param invalidateSession whether to drop and recreate the session
+ * @param explain when {@code true}, include request tracing (requires administrator or tenant administrator)
* @param securityContext the security context
- * @return the context response wrapped as JavaScript
+ * @return JavaScript text wrapping a {@link ContextResponse}
* @throws JsonProcessingException if response serialization fails
+ * @api.status 200 empty JavaScript that sets {@code cxs} from a ContextResponse JSON object.
+ * @api.status 400 empty Invalid personaId/sessionId schema or invalid request payload.
+ * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+ * @api.example window.digitalData=window.digitalData||{};var cxs={"profileId":"profile-42","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"processedEvents":0,"anonymousBrowsing":false,"consents":{}};
*/
@POST
@Produces(MediaType.TEXT_PLAIN)
@@ -138,17 +150,23 @@ public class ContextJsonEndpoint {
/**
* Processes a context.js GET request.
+ * The {@link ContextRequest} is supplied as the {@code payload} query parameter (JSON).
+ * Prefer POST for production traffic; GET exists for legacy script tags.
*
- * @param contextRequest the context request payload
- * @param personaId optional persona identifier
- * @param sessionId optional session identifier
- * @param timestampAsLong optional request timestamp
- * @param invalidateProfile whether to invalidate the profile
- * @param invalidateSession whether to invalidate the session
- * @param explain whether to include tracing details
+ * @param contextRequest context request decoded from the {@code payload} query parameter
+ * @param personaId optional persona identifier used to load a persona instead of a live profile
+ * @param sessionId optional session identifier (prefer payload {@code sessionId} over the query string)
+ * @param timestampAsLong optional request time as epoch millis; defaults to server now
+ * @param invalidateProfile whether to drop and recreate the profile cookie/state
+ * @param invalidateSession whether to drop and recreate the session
+ * @param explain when {@code true}, include request tracing (requires administrator or tenant administrator)
* @param securityContext the security context
- * @return the context response wrapped as JavaScript
+ * @return JavaScript text wrapping a {@link ContextResponse}
* @throws JsonProcessingException if response serialization fails
+ * @api.status 200 empty JavaScript that sets {@code cxs} from a ContextResponse JSON object.
+ * @api.status 400 empty Invalid personaId/sessionId schema or invalid payload.
+ * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+ * @api.example window.digitalData=window.digitalData||{};var cxs={"profileId":"profile-42","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"processedEvents":0,"anonymousBrowsing":false,"consents":{}};
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
@@ -172,16 +190,22 @@ public class ContextJsonEndpoint {
/**
* Processes a context.json GET request.
+ * The {@link ContextRequest} is supplied as the {@code payload} query parameter (JSON).
+ * Prefer POST for production traffic.
*
- * @param contextRequest the context request payload
- * @param personaId optional persona identifier
- * @param sessionId optional session identifier
- * @param timestampAsLong optional request timestamp
- * @param invalidateProfile whether to invalidate the profile
- * @param invalidateSession whether to invalidate the session
- * @param explain whether to include tracing details
+ * @param contextRequest context request decoded from the {@code payload} query parameter
+ * @param personaId optional persona identifier used to load a persona instead of a live profile
+ * @param sessionId optional session identifier (prefer payload {@code sessionId} over the query string)
+ * @param timestampAsLong optional request time as epoch millis; defaults to server now
+ * @param invalidateProfile whether to drop and recreate the profile cookie/state
+ * @param invalidateSession whether to drop and recreate the session
+ * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
* @param securityContext the security context
* @return the context response
+ * @api.status 200 org.apache.unomi.api.ContextResponse Context evaluation succeeded.
+ * @api.status 400 empty Invalid personaId/sessionId schema or invalid payload.
+ * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+ * @api.example {"profileId":"profile-1","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"processedEvents":0,"anonymousBrowsing":false,"consents":{}}
*/
@GET
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@@ -198,17 +222,23 @@ public class ContextJsonEndpoint {
}
/**
- * Processes a context.json POST request.
+ * Processes a context.json POST request (preferred public CDP entry point).
+ * Evaluates optional events, content filters, and personalizations; returns profile/session
+ * ids plus any requested properties, segments, scores, consents, and tracked conditions.
*
- * @param contextRequest the context request payload
- * @param personaId optional persona identifier
- * @param sessionId optional session identifier
- * @param timestampAsLong optional request timestamp
- * @param invalidateProfile whether to invalidate the profile
- * @param invalidateSession whether to invalidate the session
- * @param explain whether to include tracing details
+ * @param contextRequest the JSON context request body
+ * @param personaId optional persona identifier used to load a persona instead of a live profile
+ * @param sessionId optional session identifier (prefer body {@code sessionId} over the query string)
+ * @param timestampAsLong optional request time as epoch millis; defaults to server now
+ * @param invalidateProfile whether to drop and recreate the profile cookie/state
+ * @param invalidateSession whether to drop and recreate the session
+ * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
* @param securityContext the security context
* @return the context response
+ * @api.status 200 org.apache.unomi.api.ContextResponse Context evaluation succeeded.
+ * @api.status 400 empty Invalid personaId/sessionId schema or invalid request body.
+ * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+ * @api.example {"profileId":"profile-1","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"profileSegments":["vip"],"processedEvents":1,"anonymousBrowsing":false,"consents":{}}
*/
@POST
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java
index 2a2c3c62d..42522a745 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java
@@ -72,9 +72,13 @@ public class DefinitionsServiceEndPoint {
/**
* Returns all condition types localized for the requested language.
+ * <p>
+ * Labels and descriptions follow the {@code Accept-Language} header when provided.
*
- * @param language the locale to use for labels and descriptions
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return all condition types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTConditionType All condition types (may be empty).
+ * @api.example [{"id":"profilePropertyCondition","name":"Profile property","description":"Checks a profile property value","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}],"version":1}]
*/
@GET
@Path("/conditions")
@@ -85,10 +89,15 @@ public class DefinitionsServiceEndPoint {
/**
* Returns condition types that match any of the given tags.
+ * <p>
+ * The {@code tags} path segment is a comma-separated list; each tag is matched independently
+ * and results are de-duplicated.
*
- * @param language the locale to use for labels and descriptions
* @param tags a comma-separated list of tag identifiers
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return matching condition types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTConditionType Matching condition types (may be empty).
+ * @api.example [{"id":"eventTypeCondition","name":"Event type","description":"Matches events of a given type","tags":["event"],"systemTags":["event"],"parameters":[{"id":"eventTypeId","type":"string"}],"version":1}]
*/
@GET
@Path("/conditions/tags/{tags}")
@@ -103,10 +112,15 @@ public class DefinitionsServiceEndPoint {
/**
* Returns condition types that match any of the given system tags.
+ * <p>
+ * The {@code tags} path segment is a comma-separated list; each system tag is matched
+ * independently and results are de-duplicated.
*
- * @param language the locale to use for labels and descriptions
* @param tags a comma-separated list of system tag identifiers
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return matching condition types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTConditionType Matching condition types (may be empty).
+ * @api.example [{"id":"profilePropertyCondition","name":"Profile property","description":"Checks a profile property value","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}],"version":1}]
*/
@GET
@Path("/conditions/systemTags/{tags}")
@@ -121,10 +135,13 @@ public class DefinitionsServiceEndPoint {
/**
* Returns the condition type with the given ID, localized for the requested language.
+ * When the condition type does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
- * @param language the locale to use for labels and descriptions
* @param id the condition type identifier
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return the condition type in REST form, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.rest.models.RESTConditionType Condition type found, or empty body when missing.
+ * @api.example {"id":"profilePropertyCondition","name":"Profile property","description":"Checks a profile property value","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}],"version":1}
*/
@GET
@Path("/conditions/{conditionId}")
@@ -135,8 +152,12 @@ public class DefinitionsServiceEndPoint {
/**
* Stores the given condition type definition.
+ * Body is a full {@link ConditionType}: {@code metadata}, evaluator metadata, and {@code parameters}.
*
* @param conditionType the condition type to store
+ * @api.status 204 empty Condition type created or updated.
+ * @api.status 400 empty Invalid or unreadable condition type body.
+ * @api.example {"itemId":"profilePropertyCondition","itemType":"conditionType","metadata":{"id":"profilePropertyCondition","name":"Profile property","scope":"systemscope","enabled":true},"conditionEvaluator":"profilePropertyConditionEvaluator","parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}]}
*/
@POST
@Path("/conditions")
@@ -148,6 +169,8 @@ public class DefinitionsServiceEndPoint {
* Deletes the condition type with the given ID.
*
* @param conditionTypeId the condition type identifier
+ * @api.status 204 empty Condition type deleted.
+ * @api.example {"itemId":"profilePropertyCondition","itemType":"conditionType","metadata":{"id":"profilePropertyCondition","name":"Profile property","scope":"systemscope","enabled":true},"conditionEvaluator":"profilePropertyConditionEvaluator","parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}]}
*/
@DELETE
@Path("/conditions/{conditionTypeId}")
@@ -157,9 +180,13 @@ public class DefinitionsServiceEndPoint {
/**
* Returns all action types localized for the requested language.
+ * <p>
+ * Labels and descriptions follow the {@code Accept-Language} header when provided.
*
- * @param language the locale to use for labels and descriptions
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return all action types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTActionType All action types (may be empty).
+ * @api.example [{"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}]
*/
@GET
@Path("/actions")
@@ -170,10 +197,15 @@ public class DefinitionsServiceEndPoint {
/**
* Returns action types that match any of the given tags.
+ * <p>
+ * The {@code tags} path segment is a comma-separated list; each tag is matched independently
+ * and results are de-duplicated.
*
- * @param language the locale to use for labels and descriptions
* @param tags a comma-separated list of tag identifiers
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return matching action types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTActionType Matching action types (may be empty).
+ * @api.example [{"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}]
*/
@GET
@Path("/actions/tags/{tags}")
@@ -188,10 +220,15 @@ public class DefinitionsServiceEndPoint {
/**
* Returns action types that match any of the given system tags.
+ * <p>
+ * The {@code tags} path segment is a comma-separated list; each system tag is matched
+ * independently and results are de-duplicated.
*
- * @param language the locale to use for labels and descriptions
* @param tags a comma-separated list of system tag identifiers
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return matching action types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTActionType Matching action types (may be empty).
+ * @api.example [{"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}]
*/
@GET
@Path("/actions/systemTags/{tags}")
@@ -206,10 +243,13 @@ public class DefinitionsServiceEndPoint {
/**
* Returns the action type with the given ID, localized for the requested language.
+ * When the action type does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
- * @param language the locale to use for labels and descriptions
* @param id the action type identifier
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return the action type in REST form, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.rest.models.RESTActionType Action type found, or empty body when missing.
+ * @api.example {"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}
*/
@GET
@Path("/actions/{actionId}")
@@ -220,8 +260,12 @@ public class DefinitionsServiceEndPoint {
/**
* Stores the given action type definition.
+ * Body is a full {@link ActionType}: {@code metadata}, executor metadata, and {@code parameters}.
*
* @param actionType the action type to store
+ * @api.status 204 empty Action type created or updated.
+ * @api.status 400 empty Invalid or unreadable action type body.
+ * @api.example {"itemId":"setPropertyAction","itemType":"actionType","metadata":{"id":"setPropertyAction","name":"Set property","scope":"systemscope","enabled":true},"actionExecutor":"setPropertyActionExecutor","parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}]}
*/
@POST
@Path("/actions")
@@ -233,6 +277,8 @@ public class DefinitionsServiceEndPoint {
* Deletes the action type with the given ID.
*
* @param actionTypeId the action type identifier
+ * @api.status 204 empty Action type deleted.
+ * @api.example {"itemId":"setPropertyAction","itemType":"actionType","metadata":{"id":"setPropertyAction","name":"Set property","scope":"systemscope","enabled":true},"actionExecutor":"setPropertyActionExecutor","parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}]}
*/
@DELETE
@Path("/actions/{actionTypeId}")
@@ -242,9 +288,13 @@ public class DefinitionsServiceEndPoint {
/**
* Returns all value types localized for the requested language.
+ * <p>
+ * Labels and descriptions follow the {@code Accept-Language} header when provided.
*
- * @param language the locale to use for labels and descriptions
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return all value types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTValueType All value types (may be empty).
+ * @api.example [{"id":"string","name":"String","description":"Text value","tags":["primitive"]}]
*/
@GET
@Path("/values")
@@ -254,10 +304,15 @@ public class DefinitionsServiceEndPoint {
/**
* Returns value types that match any of the given tags.
+ * <p>
+ * The {@code tags} path segment is a comma-separated list; each tag is matched independently
+ * and results are de-duplicated.
*
- * @param language the locale to use for labels and descriptions
* @param tags a comma-separated list of tag identifiers
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
* @return matching value types in REST form
+ * @api.status 200 array org.apache.unomi.rest.models.RESTValueType Matching value types (may be empty).
+ * @api.example [{"id":"string","name":"String","description":"Text value","tags":["primitive"]}]
*/
@GET
@Path("/values/tags/{tags}")
@@ -273,9 +328,12 @@ public class DefinitionsServiceEndPoint {
/**
* Returns the value type with the given ID, localized for the requested language.
*
- * @param language the locale to use for labels and descriptions
* @param id the value type identifier
- * @return the value type in REST form, or {@code null} when it does not exist
+ * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
+ * @return the value type in REST form
+ * @api.status 200 org.apache.unomi.rest.models.RESTValueType Value type found.
+ * @api.status 404 empty Value type not found.
+ * @api.example {"id":"string","name":"String","description":"Text value","tags":["primitive"]}
*/
@GET
@Path("/values/{valueTypeId}")
@@ -289,8 +347,12 @@ public class DefinitionsServiceEndPoint {
/**
* Returns plugin types grouped by plugin identifier.
+ * <p>
+ * Keys are numeric plugin IDs; values are lists of condition and action types contributed by each plugin.
*
* @return plugin ID to plugin type list mappings
+ * @api.status 200 empty Map of plugin id to condition/action type lists (may be empty).
+ * @api.example {"42":[{"itemId":"profilePropertyCondition","itemType":"conditionType"}]}
*/
@GET
@Path("/typesByPlugin")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java
index e3497922d..bff09b231 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java
@@ -55,9 +55,13 @@ public class EventServiceEndpoint {
/**
* Searches events using the given query.
+ * The query may include a condition tree ({@code type} + {@code parameterValues}), offset, and limit.
*
* @param query the search query, including optional condition tree, offset, and limit
* @return a paged list of matching events
+ * @api.status 200 org.apache.unomi.api.PartialList Events page (list items are Event).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-1","scope":"mysite","timeStamp":"2024-06-15T10:30:00.000Z","properties":{"pageUrl":"https://example.com/"},"persistent":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/search")
@@ -69,7 +73,10 @@ public class EventServiceEndpoint {
* Returns the event with the given ID.
*
* @param id the event identifier
- * @return the event, or {@code null} when it does not exist
+ * @return the event, or HTTP 404 when it does not exist
+ * @api.status 200 org.apache.unomi.api.Event Event found.
+ * @api.status 404 empty Event not found.
+ * @api.example {"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-1","scope":"mysite","timeStamp":"2024-06-15T10:30:00.000Z","properties":{"pageUrl":"https://example.com/"},"persistent":true}
*/
@GET
@Path("/{id}")
@@ -85,6 +92,8 @@ public class EventServiceEndpoint {
* Deletes an event by id.
*
* @param id the identifier for the event to delete
+ * @api.status 204 empty Event deleted.
+ * @api.example {"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-42","scope":"mysite","timeStamp":"2024-06-15T10:30:00.000Z","properties":{"pageUrl":"https://example.com/"},"persistent":true}
*/
@DELETE
@Path("/{id}")
@@ -96,6 +105,8 @@ public class EventServiceEndpoint {
* Returns event type identifiers known to the server.
*
* @return the processed event type identifiers
+ * @api.status 200 array empty Event type ids known to the server (may be empty).
+ * @api.example ["view","login","purchase"]
*/
@GET
@Path("types")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java
index 1cfa8a1fe..48e7e5c7f 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java
@@ -43,7 +43,9 @@ import java.util.Date;
import java.util.List;
/**
- * REST endpoint for collecting client events.
+ * REST endpoint for collecting client events ({@code /eventcollector}).
+ * Accepts a batch of {@link org.apache.unomi.api.Event} objects, attaches them to a profile/session,
+ * runs rules, and returns a bitwise {@code updated} change mask.
*/
@Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@Consumes(MediaType.APPLICATION_JSON)
@@ -70,6 +72,8 @@ public class EventsCollectorEndpoint {
* Handles CORS preflight for the event collector endpoint.
*
* @return an empty CORS preflight response
+ * @api.status 204 empty CORS preflight (no body).
+ * @api.example {}
*/
@OPTIONS
@Path("/eventcollector")
@@ -79,12 +83,18 @@ public class EventsCollectorEndpoint {
/**
* Collects events from a GET request.
+ * The {@link EventsCollectorRequest} is supplied as the {@code payload} query parameter (JSON).
+ * Prefer POST for production traffic.
*
- * @param eventsCollectorRequest the events collector request
- * @param timestampAsString optional request timestamp
- * @param explain whether to include tracing details
+ * @param eventsCollectorRequest collector request decoded from the {@code payload} query parameter
+ * @param timestampAsString optional request time as epoch millis; defaults to server now
+ * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
* @param securityContext the security context
* @return the event collector response
+ * @api.status 200 org.apache.unomi.rest.models.EventCollectorResponse Events processed; see {@code updated} bitwise flags.
+ * @api.status 400 empty Missing/empty collector payload or invalid event data.
+ * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+ * @api.example {"updated":6}
*/
@GET
@Path("/eventcollector")
@@ -96,13 +106,18 @@ public class EventsCollectorEndpoint {
}
/**
- * Collects events from a POST request.
+ * Collects events from a POST request (preferred).
+ * Submits an {@link org.apache.unomi.api.EventsCollectorRequest} body so Unomi can attach events to a session/profile, run rules, and return bitwise update flags.
*
- * @param eventsCollectorRequest the events collector request
- * @param timestampAsLong optional request timestamp
- * @param explain whether to include tracing details
+ * @param eventsCollectorRequest JSON body with {@code events} plus optional {@code sessionId}/{@code profileId}/{@code publicApiKey}
+ * @param timestampAsLong optional request time as epoch millis; defaults to server now
+ * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
* @param securityContext the security context
* @return the event collector response
+ * @api.status 200 org.apache.unomi.rest.models.EventCollectorResponse Events processed; see {@code updated} bitwise flags.
+ * @api.status 400 empty Missing/empty collector body or invalid event data.
+ * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+ * @api.example {"updated":6}
*/
@POST
@Path("/eventcollector")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java
index 2236ba876..f5a3c15b1 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java
@@ -61,6 +61,8 @@ public class GoalsServiceEndPoint {
* Returns metadata for all goals.
*
* @return goal metadata for every stored goal
+ * @api.status 200 array org.apache.unomi.api.Metadata Goal metadata for all stored goals (may be empty).
+ * @api.example [{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true}]
*/
@GET
@Path("/")
@@ -70,8 +72,13 @@ public class GoalsServiceEndPoint {
/**
* Saves the specified goal in the context server and creates associated {@link Rule}s if the goal is enabled.
+ * Body is a full {@link Goal}: {@code metadata}, optional {@code startEvent} and {@code targetEvent}
+ * (each a condition with JSON field {@code type} + {@code parameterValues}), and optional {@code campaignId}.
*
* @param goal the Goal to be saved
+ * @api.status 204 empty Goal created or updated.
+ * @api.status 400 empty Invalid or unreadable goal body.
+ * @api.example {"itemId":"checkout-goal","itemType":"goal","metadata":{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true},"startEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"targetEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}}
*/
@POST
@Path("/")
@@ -84,6 +91,9 @@ public class GoalsServiceEndPoint {
*
* @param query the query used to filter goals
* @return metadata for goals that match the query
+ * @api.status 200 array org.apache.unomi.api.Metadata Matching goal metadata (may be empty).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example [{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true}]
*/
@POST
@Path("/query")
@@ -93,9 +103,12 @@ public class GoalsServiceEndPoint {
/**
* Returns the goal with the given ID.
+ * When the goal does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param goalId the goal identifier
* @return the goal, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.goals.Goal Goal found, or empty body when missing.
+ * @api.example {"itemId":"checkout-goal","itemType":"goal","metadata":{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true},"startEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"targetEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}}
*/
@GET
@Path("/{goalId}")
@@ -107,6 +120,8 @@ public class GoalsServiceEndPoint {
* Removes the goal associated with the specified identifier, also removing associated rules if needed.
*
* @param goalId the identifier of the goal to be removed
+ * @api.status 204 empty Goal deleted.
+ * @api.example {"itemId":"checkout-goal","itemType":"goal","metadata":{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true},"startEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"targetEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}}
*/
@DELETE
@Path("/{goalId}")
@@ -119,6 +134,9 @@ public class GoalsServiceEndPoint {
*
* @param goalId the goal identifier
* @return the goal report
+ * @api.status 200 org.apache.unomi.api.goals.GoalReport Goal report with global and split statistics.
+ * @api.status 404 empty Goal not found.
+ * @api.example {"globalStats":{"key":"global","startCount":1000,"targetCount":120,"conversionRate":0.12,"percentage":100.0},"split":[{"key":"variant-a","startCount":500,"targetCount":70,"conversionRate":0.14,"percentage":50.0}]}
*/
@GET
@Path("/{goalID}/report")
@@ -131,10 +149,15 @@ public class GoalsServiceEndPoint {
/**
* Returns a filtered goal report for the given goal and aggregate query.
+ * The aggregate query limits which report elements are included in the response.
*
* @param goalId the goal identifier
* @param query the aggregate query that limits report elements
* @return the filtered goal report
+ * @api.status 200 org.apache.unomi.api.goals.GoalReport Filtered goal report.
+ * @api.status 400 empty Invalid or unreadable aggregate query body.
+ * @api.status 404 empty Goal not found.
+ * @api.example {"globalStats":{"key":"global","startCount":1000,"targetCount":120,"conversionRate":0.12,"percentage":100.0},"split":[{"key":"variant-a","startCount":500,"targetCount":70,"conversionRate":0.14,"percentage":50.0}]}
*/
@POST
@Path("/{goalID}/report")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java
index 19fe51fb4..00429f82e 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java
@@ -52,10 +52,14 @@ public class PatchServiceEndPoint {
}
/**
- * Apply a patch on an item
+ * Applies a patch to an item.
+ * <p>
+ * When {@code force} is {@code false} or omitted and the patch was already applied, the call is a no-op.
*
* @param patch the patch to apply
- * @param force a boolean to force (or not) the application of the patch even if it was previously applied.
+ * @param force when {@code true}, re-applies even if previously applied
+ * @api.status 204 empty Patch applied or skipped because it was already applied.
+ * @api.example {"itemId":"profile-1","itemType":"patch","patches":[{"operation":"set","path":"properties.firstName","value":"Ada"}]}
*/
@POST
@Path("/apply")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
index b03208e87..3c43745f9 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
@@ -116,6 +116,8 @@ public class ProfileServiceEndPoint {
* Returns the total number of unique profiles.
*
* @return the profile count
+ * @api.status 200 empty Total number of unique profiles.
+ * @api.example 1200
*/
@GET
@Path("/count")
@@ -125,9 +127,13 @@ public class ProfileServiceEndPoint {
/**
* Returns profiles matching the given query.
+ * Prefer a {@link org.apache.unomi.api.conditions.Condition} in the body for precise filters; optional full-text {@code text} and paging fields are also supported.
*
* @param query the search query
* @return a paged list of matching profiles
+ * @api.status 200 org.apache.unomi.api.PartialList Profiles page (list items are Profile).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"itemId":"profile-1","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"]}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/search")
@@ -137,9 +143,14 @@ public class ProfileServiceEndPoint {
/**
* Exports matching profiles as a downloadable CSV file.
+ * The {@code query} parameter must be a JSON-encoded {@link org.apache.unomi.api.query.Query}.
*
* @param query JSON query string describing which profiles to export
* @return a CSV download response
+ * @api.status 200 empty CSV attachment with matching profile properties.
+ * @api.status 400 empty {@code query} JSON is missing or not a valid Query.
+ * @api.status 500 empty Export failed while parsing the query or generating CSV.
+ * @api.example itemId,properties.email,properties.firstName\nprofile-42,ada@example.com,Ada
*/
@GET
@Path("/export")
@@ -158,6 +169,9 @@ public class ProfileServiceEndPoint {
*
* @param query a form-encoded representation of the query the profiles to export should match
* @return a Response object configured to allow caller to download the CSV export file
+ * @api.status 200 empty CSV attachment with matching profile properties.
+ * @api.status 400 empty Form {@code query} is missing or not a valid Query JSON.
+ * @api.status 500 empty Export failed while parsing the query or generating CSV.
*/
@GET
@Path("/export")
@@ -177,6 +191,9 @@ public class ProfileServiceEndPoint {
*
* @param query the query describing which profiles to export
* @return a CSV download response
+ * @api.status 200 empty CSV attachment with matching profile properties.
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example itemId,properties.email,properties.firstName\nprofile-42,ada@example.com,Ada
*/
@POST
@Path("/export")
@@ -191,8 +208,12 @@ public class ProfileServiceEndPoint {
/**
* Applies a batch update to all matching profiles.
+ * Selects matching profiles with {@link org.apache.unomi.api.BatchUpdate#getCondition()} and sets {@code propertyName} to {@code propertyValue}.
*
* @param update the batch update specification
+ * @api.status 204 empty Batch update accepted and processed.
+ * @api.status 400 empty Invalid batch update body (missing propertyName/condition).
+ * @api.example {"propertyName":"properties.email","propertyValue":"updated@example.com","condition":{"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.email","comparisonOperator":"exists","propertyValue":""}},"strategy":"defaultPropertyMergeStrategy","scrollTimeValidity":"10m","scrollBatchSize":1000}
*/
@POST
@Path("/batchProfilesUpdate")
@@ -202,9 +223,12 @@ public class ProfileServiceEndPoint {
/**
* Returns the profile with the given ID.
+ * When the profile does not exist the endpoint returns {@code null} (HTTP 200 with empty body), not 404.
*
* @param profileId the profile identifier
* @return the profile, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.Profile Profile found, or empty body when missing.
+ * @api.example {"itemId":"profile-1","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"],"scores":{"engagement":12},"consents":{}}
*/
@GET
@Path("/{profileId}")
@@ -214,9 +238,13 @@ public class ProfileServiceEndPoint {
/**
* Saves the specified profile in the context server, sending a {@code profileUpdated} event.
+ * Creates a new profile or merges into an existing one when applicable, then fires a non-persistent {@code profileUpdated} event.
*
* @param profile the profile to be saved
* @return the newly saved profile
+ * @api.status 200 org.apache.unomi.api.Profile Profile saved (or merged).
+ * @api.status 400 empty Invalid or unreadable profile body.
+ * @api.example {"itemId":"profile-1","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"]}
*/
@POST
@Path("/")
@@ -238,6 +266,8 @@ public class ProfileServiceEndPoint {
*
* @param profileId the identifier of the profile or persona to delete
* @param persona {@code true} if the specified identifier is supposed to refer to a persona, {@code false} if it is supposed to refer to a profile
+ * @api.status 204 empty Profile or persona deleted.
+ * @api.example {"itemId":"profile-42","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"}}
*/
@DELETE
@Path("/{profileId}")
@@ -256,6 +286,8 @@ public class ProfileServiceEndPoint {
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return a paged list of matching sessions
+ * @api.status 200 org.apache.unomi.api.PartialList Sessions page (list items are Session).
+ * @api.example {"list":[{"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite","size":4}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@GET
@Path("/{profileId}/sessions")
@@ -272,6 +304,8 @@ public class ProfileServiceEndPoint {
*
* @param profileId the profile identifier
* @return segment metadata for memberships of this profile
+ * @api.status 200 array org.apache.unomi.api.Metadata Segment memberships for the profile (may be empty).
+ * @api.example [{"id":"vip","name":"VIP customers","scope":"mysite","enabled":true}]
*/
@GET
@Path("/{profileId}/segments")
@@ -285,6 +319,8 @@ public class ProfileServiceEndPoint {
*
* @param fromPropertyTypeId the source property type identifier
* @return the mapped target property type identifier
+ * @api.status 200 empty Mapped target property type id (may be empty/null when unmapped).
+ * @api.example email
*/
@GET
@Path("/properties/mappings/{fromPropertyTypeId}")
@@ -297,6 +333,9 @@ public class ProfileServiceEndPoint {
*
* @param query the search query
* @return a paged list of matching personas
+ * @api.status 200 org.apache.unomi.api.PartialList Personas page (list items are Persona).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/personas/search")
@@ -306,9 +345,12 @@ public class ProfileServiceEndPoint {
/**
* Returns the persona with the given ID.
+ * When the persona does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param personaId the persona identifier
* @return the persona, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.Persona Persona found, or empty body when missing.
+ * @api.example {"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"},"segments":["vip"]}
*/
@GET
@Path("/personas/{personaId}")
@@ -321,6 +363,8 @@ public class ProfileServiceEndPoint {
*
* @param personaId the persona identifier
* @return the persona and its sessions
+ * @api.status 200 org.apache.unomi.api.PersonaWithSessions Persona bundled with its sessions.
+ * @api.example {"persona":{"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}},"sessions":[{"itemId":"ps-1","profileId":"persona-vip","scope":"mysite"}]}
*/
@GET
@Path("/personasWithSessions/{personaId}")
@@ -333,6 +377,9 @@ public class ProfileServiceEndPoint {
*
* @param personaWithSessions the persona and sessions to persist
* @return the saved persona and sessions
+ * @api.status 200 org.apache.unomi.api.PersonaWithSessions Persona and sessions saved.
+ * @api.status 400 empty Invalid or unreadable body.
+ * @api.example {"persona":{"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}},"sessions":[{"itemId":"ps-1","profileId":"persona-vip","scope":"mysite"}]}
*/
@POST
@Path("/personasWithSessions")
@@ -345,6 +392,9 @@ public class ProfileServiceEndPoint {
*
* @param persona the persona to persist
* @return the newly persisted persona
+ * @api.status 200 org.apache.unomi.api.Persona Persona saved.
+ * @api.status 400 empty Invalid or unreadable persona body.
+ * @api.example {"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"},"segments":["vip"]}
*/
@POST
@Path("/personas")
@@ -357,6 +407,8 @@ public class ProfileServiceEndPoint {
*
* @param personaId the identifier of the persona to delete
* @param persona {@code true} if the specified identifier is supposed to refer to a persona, {@code false} if it is supposed to refer to a profile
+ * @api.status 204 empty Persona deleted.
+ * @api.example {"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}}
*/
@DELETE
@Path("/personas/{personaId}")
@@ -369,6 +421,8 @@ public class ProfileServiceEndPoint {
*
* @param personaId the identifier to use for the new persona
* @return the newly created persona
+ * @api.status 200 org.apache.unomi.api.Persona Persona created with an initial session.
+ * @api.example {"itemId":"persona-vip","itemType":"persona"}
*/
@PUT
@Path("/personas/{personaId}")
@@ -385,6 +439,8 @@ public class ProfileServiceEndPoint {
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return a paged list of persona sessions
+ * @api.status 200 org.apache.unomi.api.PartialList Persona sessions page (list items are PersonaSession).
+ * @api.example {"list":[{"itemId":"ps-1","profileId":"persona-vip","scope":"mysite"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@GET
@Path("/personas/{personaId}/sessions")
@@ -397,10 +453,13 @@ public class ProfileServiceEndPoint {
/**
* Returns the session with the given ID.
+ * When the session does not exist the endpoint may return {@code null} (HTTP 200 with empty body).
*
* @param sessionId the session identifier
* @return the session
* @throws ParseException if a stored date hint cannot be parsed
+ * @api.status 200 org.apache.unomi.api.Session Session found, or empty body when missing.
+ * @api.example {"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite","size":4,"duration":1800000}
*/
@GET
@Path("/sessions/{sessionId}")
@@ -413,6 +472,9 @@ public class ProfileServiceEndPoint {
*
* @param session the session to be saved
* @return the newly saved session
+ * @api.status 200 org.apache.unomi.api.Session Session saved.
+ * @api.status 400 empty Invalid or unreadable session body.
+ * @api.example {"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite"}
*/
@POST
@Path("/sessions/{sessionId}")
@@ -424,6 +486,8 @@ public class ProfileServiceEndPoint {
* Deletes the session with the given ID.
*
* @param sessionId the session identifier
+ * @api.status 204 empty Session deleted.
+ * @api.example {"itemId":"session-1","itemType":"session","profileId":"profile-42","scope":"mysite"}
*/
@DELETE
@Path("/sessions/{sessionId}")
@@ -443,6 +507,8 @@ public class ProfileServiceEndPoint {
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return a paged list of matching events
+ * @api.status 200 org.apache.unomi.api.PartialList Events page (list items are Event).
+ * @api.example {"list":[{"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-1","scope":"mysite"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@GET
@Path("/sessions/{sessionId}/events")
@@ -489,6 +555,9 @@ public class ProfileServiceEndPoint {
* @param response the HTTP response used to signal missing query parameters
* @return matching property types, or {@code null} when required parameters are missing
* @throws IOException if sending the error response fails
+ * @api.status 200 array org.apache.unomi.api.PropertyType Property types already used for the item type/tag.
+ * @api.status 400 empty Missing mandatory {@code tag} or {@code itemType} query parameter.
+ * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"}]
*/
@GET
@Path("/existingProperties")
@@ -513,6 +582,8 @@ public class ProfileServiceEndPoint {
*
* @param language the requested locale for property descriptions (currently unused)
* @return target name to property type mappings
+ * @api.status 200 empty Map of target name to property type collections.
+ * @api.example {"profiles":[{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"}]}
*/
@GET
@Path("/properties")
@@ -528,6 +599,8 @@ public class ProfileServiceEndPoint {
* @param propertyId the property identifier
* @param language the requested locale for property descriptions (currently unused)
* @return the property type
+ * @api.status 200 org.apache.unomi.api.PropertyType Property type found, or empty body when missing.
+ * @api.example {"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"}
*/
@GET
@Path("/properties/{propertyId}")
@@ -543,6 +616,8 @@ public class ProfileServiceEndPoint {
* @param target the property target name
* @param language the requested locale for property descriptions (currently unused)
* @return property types for the target
+ * @api.status 200 array org.apache.unomi.api.PropertyType Property types for the target.
+ * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"},{"itemId":"firstName","itemType":"propertyType","metadata":{"id":"firstName","name":"First Name","scope":"systemscope"},"target":"profiles","valueTypeId":"text"}]
*/
@GET
@Path("/properties/targets/{target}")
@@ -559,6 +634,8 @@ public class ProfileServiceEndPoint {
* @param tags comma-separated tag identifiers
* @param language the requested locale for property descriptions (currently unused)
* @return matching property types
+ * @api.status 200 array org.apache.unomi.api.PropertyType Property types matching any of the tags.
+ * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope","tags":["contact"]},"target":"profiles","valueTypeId":"email"}]
*/
@GET
@Path("/properties/tags/{tags}")
@@ -580,6 +657,8 @@ public class ProfileServiceEndPoint {
* @param tags comma-separated system tag identifiers
* @param language the requested locale for property descriptions (currently unused)
* @return matching property types
+ * @api.status 200 array org.apache.unomi.api.PropertyType Property types matching any of the system tags.
+ * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope","systemTags":["contactInfo"]},"target":"profiles","valueTypeId":"email"}]
*/
@GET
@Path("/properties/systemTags/{tags}")
@@ -598,7 +677,10 @@ public class ProfileServiceEndPoint {
* Property-type lookup helpers that may move to a dedicated endpoint in a future release.
*
* @param property the property type to persist
- * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed
+ * @return {@code true} if the property type was properly created, {@code false} otherwise (for example if it already existed)
+ * @api.status 200 empty {@code true} when created/updated, {@code false} when not persisted.
+ * @api.status 400 empty Invalid or unreadable property type body.
+ * @api.example true
*/
@POST
@Path("/properties")
@@ -612,7 +694,10 @@ public class ProfileServiceEndPoint {
* Property-type lookup helpers that may move to a dedicated endpoint in a future release.
*
* @param properties the properties type to persist
- * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed
+ * @return {@code true} if at least one property type was persisted, {@code false} otherwise
+ * @api.status 200 empty {@code true} when at least one property type was saved.
+ * @api.status 400 empty Invalid or unreadable property type list body.
+ * @api.example true
*/
@POST
@Path("/properties/bulk")
@@ -631,6 +716,8 @@ public class ProfileServiceEndPoint {
*
* @param propertyId the identifier of the property type to delete
* @return {@code true} if the property type was properly deleted, {@code false} otherwise
+ * @api.status 200 empty {@code true} when deleted, {@code false} when not found / not deleted.
+ * @api.example true
*/
@DELETE
@Path("/properties/{propertyId}")
@@ -643,6 +730,9 @@ public class ProfileServiceEndPoint {
*
* @param query the search query
* @return a paged list of matching sessions
+ * @api.status 200 org.apache.unomi.api.PartialList Sessions page (list items are Session).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/search/sessions")
@@ -652,10 +742,13 @@ public class ProfileServiceEndPoint {
/**
* Adds an alias to a profile.
+ * Links a client-specific alias id to the canonical {@code profileId}. When {@code X-Unomi-ClientId} is omitted, {@code defaultClientId} is used.
*
* @param profileId the profile identifier
* @param aliasId the alias identifier
* @param headerClientID optional client identifier from the request header
+ * @api.status 204 empty Alias linked to the profile.
+ * @api.example {"itemId":"alias-crm-9","itemType":"profileAlias","profileID":"profile-42","clientID":"web-tracker"}
*/
@POST
@Path("/{profileId}/aliases/{aliasId}")
@@ -672,6 +765,8 @@ public class ProfileServiceEndPoint {
* @param profileId the profile identifier
* @param aliasId the alias identifier
* @param headerClientID optional client identifier from the request header
+ * @api.status 204 empty Alias removed from the profile.
+ * @api.example {"itemId":"alias-crm-9","itemType":"profileAlias","profileID":"profile-42","clientID":"web-tracker"}
*/
@DELETE
@Path("/{profileId}/aliases/{aliasId}")
@@ -690,6 +785,8 @@ public class ProfileServiceEndPoint {
* @param size page size
* @param sortBy optional sort field
* @return the matching profile aliases
+ * @api.status 200 org.apache.unomi.api.PartialList Aliases page (list items are ProfileAlias).
+ * @api.example {"list":[{"itemId":"alias-crm-9","itemType":"profileAlias","profileID":"profile-1","clientID":"web-tracker"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@GET
@Path("/{profileId}/aliases")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java
index d52844361..d97b3ce79 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java
@@ -80,6 +80,8 @@ public class QueryServiceEndPoint {
* @param property the property whose distinct values form aggregation buckets
* @return property value to item count mappings
* @see Item Item for a discussion of {@code ITEM_TYPE}
+ * @api.status 200 empty Property value to item count map (may be empty).
+ * @api.example {"vip":42,"standard":128}
*/
@GET
@Path("/{type}/{property}")
@@ -98,6 +100,8 @@ public class QueryServiceEndPoint {
* @param aggregateQuery optional aggregate query constraints
* @return property value to item count mappings
* @see Item Item for a discussion of {@code ITEM_TYPE}
+ * @api.status 200 empty Property value to item count map (may be empty).
+ * @api.example {"FR":1200,"DE":980}
*/
@POST
@Path("/{type}/{property}")
@@ -112,13 +116,18 @@ public class QueryServiceEndPoint {
/**
* Returns numeric metrics for a field on items that match the given condition.
+ * <p>
+ * Example path: {@code POST /cxs/query/profile/properties.nbOfVisits/sum/avg} with a condition body
+ * returns visit-count metrics across matching profiles.
*
* @param condition the condition matching items must satisfy
* @param metricsType slash-separated metric names ({@code sum}, {@code avg}, {@code min}, {@code max})
- * @param property the numeric field to aggregate
- * @param type the item type name from the class {@code ITEM_TYPE} field
+ * @param property the numeric field to aggregate (e.g. {@code properties.nbOfVisits})
+ * @param type the item type name from the class {@code ITEM_TYPE} field (e.g. {@code profile})
* @return metric name to computed value mappings
* @see Item Item for a discussion of {@code ITEM_TYPE}
+ * @api.status 200 empty Metric name to computed value map (keys are the requested metric names).
+ * @api.example {"sum":12500.0,"avg":250.0,"min":1.0,"max":900.0}
*/
@POST
@Path("/{type}/{property}/{metricTypes:((sum|avg|min|max)/?)*}")
@@ -135,6 +144,9 @@ public class QueryServiceEndPoint {
* @param response the HTTP response used to set status on validation failure
* @return the matching item count, or {@code 0} when validation fails
* @see Item Item for a discussion of {@code ITEM_TYPE}
+ * @api.status 200 empty Match count as a JSON number.
+ * @api.status 400 empty Invalid or unreadable condition when {@code validate} is {@code true} or omitted.
+ * @api.example 42
*/
@POST
@Path("/{type}/count")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java
index a908d5373..018afb51f 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java
@@ -74,6 +74,8 @@ public class RulesServiceEndPoint {
* Note that this includes only rules currently loaded in memory, not every rule in storage.
*
* @return known rule metadata
+ * @api.status 200 array org.apache.unomi.api.Metadata Rule metadata currently loaded in memory (may be empty).
+ * @api.example [{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true}]
*/
@GET
@Path("/")
@@ -83,8 +85,13 @@ public class RulesServiceEndPoint {
/**
* Persists the specified rule to the context server.
+ * Body is a full {@link Rule}: {@code metadata}, a {@code condition} (JSON field {@code type} + {@code parameterValues}),
+ * and an {@code actions} list (each with {@code type} + {@code parameterValues}).
*
* @param rule the rule to be persisted
+ * @api.status 204 empty Rule created or updated.
+ * @api.status 400 empty Invalid or unreadable rule body (condition/actions schema).
+ * @api.example {"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority":0,"raise [...]
*/
@POST
@Path("/")
@@ -96,6 +103,8 @@ public class RulesServiceEndPoint {
* Returns execution statistics for all known rules.
*
* @return rule ID to statistics mappings
+ * @api.status 200 empty Map of rule id to {@link RuleStatistics}.
+ * @api.example {"set-premium-on-view":{"itemId":"set-premium-on-view","itemType":"rulestats","executionCount":42,"localExecutionCount":3,"conditionsTime":120,"actionsTime":95}}
*/
@GET
@Path("/statistics")
@@ -105,6 +114,9 @@ public class RulesServiceEndPoint {
/**
* Resets execution statistics for all rules to zero.
+ *
+ * @api.status 204 empty All rule statistics reset.
+ * @api.example {}
*/
@DELETE
@Path("/statistics")
@@ -117,6 +129,9 @@ public class RulesServiceEndPoint {
*
* @param query the query rules must match
* @return a paged list of matching rule metadata
+ * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/query")
@@ -126,9 +141,13 @@ public class RulesServiceEndPoint {
/**
* Returns full rule definitions matching the given query.
+ * Each list element includes {@code condition} and {@code actions} (see {@link Rule}).
*
* @param query the query specifying which rules to include
* @return a paged list of matching rules
+ * @api.status 200 org.apache.unomi.api.PartialList Rules page (list items are Rule).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority" [...]
*/
@POST
@Path("/query/detailed")
@@ -138,9 +157,14 @@ public class RulesServiceEndPoint {
/**
* Returns the rule with the given ID.
+ * When the rule does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
+ * Use the embedded {@code condition}/{@code actions} objects as templates for new rules
+ * ({@code type} + {@code parameterValues} on each).
*
* @param ruleId the rule identifier
* @return the rule, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.rules.Rule Rule found, or empty body when missing.
+ * @api.example {"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority":0,"raise [...]
*/
@GET
@Path("/{ruleId}")
@@ -153,6 +177,8 @@ public class RulesServiceEndPoint {
*
* @param ruleId the rule identifier
* @return the rule statistics, or {@code null} when the rule does not exist
+ * @api.status 200 org.apache.unomi.api.rules.RuleStatistics Statistics for the rule, or empty body when missing.
+ * @api.example {"itemId":"set-premium-on-view","itemType":"rulestats","executionCount":42,"localExecutionCount":3,"conditionsTime":120,"actionsTime":95}
*/
@GET
@Path("/{ruleId}/statistics")
@@ -164,6 +190,8 @@ public class RulesServiceEndPoint {
* Deletes the rule identified by the specified identifier.
*
* @param ruleId the identifier of the rule we want to delete
+ * @api.status 204 empty Rule deleted.
+ * @api.example {"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority":0,"raise [...]
*/
@DELETE
@Path("/{ruleId}")
@@ -173,8 +201,11 @@ public class RulesServiceEndPoint {
/**
* Deprecated maintenance endpoint kept for backward compatibility.
+ * Reloads every in-memory rule from storage by re-saving it.
*
* @deprecated As of version 1.1.0-incubating, not needed anymore
+ * @api.status 204 empty In-memory rules re-persisted.
+ * @api.example {}
*/
@Deprecated
@GET
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java
index cbee62b60..6b0a5b4df 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java
@@ -65,6 +65,8 @@ public class ScopeServiceEndPoint {
* Returns all configured scopes.
*
* @return all known scopes
+ * @api.status 200 array org.apache.unomi.api.Scope All scopes (may be empty).
+ * @api.example [{"itemId":"systemscope","itemType":"scope","metadata":{"id":"systemscope","name":"System scope","scope":"systemscope","enabled":true}}]
*/
@GET
@Path("/")
@@ -76,6 +78,8 @@ public class ScopeServiceEndPoint {
*
* @param scope the scope to be persisted
* @return an empty success response
+ * @api.status 200 empty Scope created or updated.
+ * @api.example {"itemId":"mysite","itemType":"scope","metadata":{"id":"mysite","name":"My site","scope":"mysite","enabled":true}}
*/
@POST
@Path("/")
@@ -86,9 +90,12 @@ public class ScopeServiceEndPoint {
/**
* Returns the scope with the given ID.
+ * When the scope does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param scopeId the scope identifier
* @return the scope, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.Scope Scope found, or empty body when missing.
+ * @api.example {"itemId":"mysite","itemType":"scope","metadata":{"id":"mysite","name":"My site","scope":"mysite","enabled":true}}
*/
@GET
@Path("/{scopeId}")
@@ -97,9 +104,11 @@ public class ScopeServiceEndPoint {
}
/**
- * Deletes a scope.
+ * Deletes the scope with the given ID.
*
* @param scopeId the identifier of the scope
+ * @api.status 204 empty Scope deleted.
+ * @api.example {"itemId":"mysite","itemType":"scope","metadata":{"id":"mysite","name":"My site","scope":"mysite","enabled":true}}
*/
@DELETE
@Path("/{scopeId}")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java
index 8d78bf84b..a5315c798 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java
@@ -75,6 +75,8 @@ public class ScoringServiceEndPoint {
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return matching scoring metadata
+ * @api.status 200 array org.apache.unomi.api.Metadata Scoring metadata for the requested page (may be empty).
+ * @api.example [{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true}]
*/
@GET
@Path("/")
@@ -87,6 +89,9 @@ public class ScoringServiceEndPoint {
*
* @param query the query scorings must match
* @return a paged list of matching scoring metadata
+ * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/query")
@@ -96,9 +101,12 @@ public class ScoringServiceEndPoint {
/**
* Returns the scoring definition with the given ID.
+ * When the scoring does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param scoringId the scoring identifier
* @return the scoring, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.segments.Scoring Scoring found, or empty body when missing.
+ * @api.example {"itemId":"engagement-score","itemType":"scoring","metadata":{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true},"elements":[{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]}
*/
@GET
@Path("/{scoringID}")
@@ -108,8 +116,13 @@ public class ScoringServiceEndPoint {
/**
* Persists the specified scoring in the context server.
+ * Body is a full {@link Scoring}: {@code metadata} plus {@code elements}, each with a
+ * {@code condition} (JSON field {@code type} + {@code parameterValues}) and a point {@code value}.
*
* @param scoring the scoring to be persisted
+ * @api.status 204 empty Scoring created or updated.
+ * @api.status 400 empty Invalid or unreadable scoring body (condition schema / validation).
+ * @api.example {"itemId":"engagement-score","itemType":"scoring","metadata":{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true},"elements":[{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]}
*/
@POST
@Path("/")
@@ -125,6 +138,8 @@ public class ScoringServiceEndPoint {
* @param scoringName the name of the new scoring
* @param scoringDescription the description of the new scoring
* @see Item Item's description for a discussion of scope
+ * @api.status 204 empty Scoring created.
+ * @api.example {"itemId":"engagement-score","itemType":"scoring","metadata":{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true},"elements":[{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]}
*/
@PUT
@Path("/{scope}/{scoringID}")
@@ -134,14 +149,17 @@ public class ScoringServiceEndPoint {
}
/**
- * Removes the scoring definition identified by the specified identifier. We can specify that we want the operation to be validated beforehand so that we can
- * know if any other segment that might use the segment we're trying to delete as a condition might be impacted. If {@code validate} is set to {@code false}, no
- * validation is performed. If set to {@code true}, we will first check if any segment or scoring depends on the scoring we're trying to delete and if so we will not delete the
- * scoring but rather return the list of the metadata of the impacted items. If no dependents are found, then we properly delete the scoring.
+ * Removes the scoring definition identified by the specified identifier.
+ * <p>
+ * When {@code validate} is {@code true}, dependents are checked first: if any segment or scoring
+ * references this scoring, it is <strong>not</strong> deleted and the impacted metadata is returned.
+ * When {@code validate} is {@code false}, the scoring is deleted without that check.
*
* @param scoringId the identifier of the scoring we want to delete
- * @param validate whether or not to perform validation
- * @return a list of impacted items metadata if any or an empty list if none were found or validation was skipped
+ * @param validate whether or not to perform dependency validation before delete
+ * @return dependent metadata when delete was blocked; empty lists when deleted or nothing depended on it
+ * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Empty lists when deleted; otherwise dependents that blocked delete.
+ * @api.example {"segments":[],"scorings":[{"id":"loyalty-score","name":"Loyalty score","scope":"mysite","enabled":true}]}
*/
@DELETE
@Path("/{scoringID}")
@@ -152,10 +170,13 @@ public class ScoringServiceEndPoint {
/**
* Returns segment and scoring metadata that depend on the given scoring.
* <p>
- * A dependent definition includes a scoring condition that references this scoring.
+ * A dependent definition includes a scoring condition that references this scoring
+ * (useful before delete or when editing related definitions).
*
* @param scoringId the scoring identifier
* @return metadata for dependent segments and scorings
+ * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Dependent segments/scorings (lists may be empty).
+ * @api.example {"segments":[],"scorings":[]}
*/
@GET
@Path("/{scoringID}/impacted")
@@ -165,8 +186,11 @@ public class ScoringServiceEndPoint {
/**
* Deprecated maintenance endpoint kept for backward compatibility.
+ * Reloads in-memory scorings from storage by re-saving each definition.
*
* @deprecated As of version 1.1.0-incubating, not needed anymore
+ * @api.status 204 empty In-memory scorings re-persisted.
+ * @api.example {}
*/
@Deprecated
@GET
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java
index ab4529011..f6b19ac3c 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java
@@ -69,13 +69,15 @@ public class SegmentServiceEndPoint {
}
/**
- * Returns profiles that match the segment with the given ID.
+ * Returns profiles that currently match the segment with the given ID.
*
* @param segmentId the segment identifier
* @param offset zero-based index of the first result
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return a paged list of matching profiles
+ * @api.status 200 org.apache.unomi.api.PartialList Profiles page (list items are Profile).
+ * @api.example {"list":[{"itemId":"profile-1","itemType":"profile","properties":{"isPremium":true}}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@GET
@Path("/{segmentID}/match")
@@ -84,10 +86,12 @@ public class SegmentServiceEndPoint {
}
/**
- * Returns how many profiles match the segment with the given ID.
+ * Returns how many profiles currently match the segment with the given ID.
*
* @param segmentId the segment identifier
* @return the number of matching profiles
+ * @api.status 200 empty Match count as a JSON number.
+ * @api.example 42
*/
@GET
@Path("/{segmentID}/count")
@@ -96,11 +100,15 @@ public class SegmentServiceEndPoint {
}
/**
- * Determines whether the specified profile is part of the segment identified by the specified identifier.
+ * Determines whether the specified profile is a member of the given segment.
+ * <p>
+ * The {@code profile} path variable is the profile identifier (the JAX-RS type is {@link Profile} for binding).
*
- * @param profile the profile we want to check
+ * @param profile the profile to check (bound from the profile id path segment)
* @param segmentId the identifier of the segment against which we want to check the profile
* @return {@code true} if the specified profile is in the specified segment, {@code false} otherwise
+ * @api.status 200 empty Membership flag.
+ * @api.example true
*/
@GET
@Path("/{segmentID}/match/{profile}")
@@ -115,6 +123,8 @@ public class SegmentServiceEndPoint {
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return matching segment metadata
+ * @api.status 200 array org.apache.unomi.api.Metadata Segment metadata for the requested page (may be empty).
+ * @api.example [{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true}]
*/
@GET
@Path("/")
@@ -125,10 +135,13 @@ public class SegmentServiceEndPoint {
/**
* Returns segment and scoring metadata that depend on the given segment.
* <p>
- * A dependent definition includes a profile-segment condition that references this segment.
+ * A dependent definition includes a profile-segment condition that references this segment
+ * (useful before delete or when editing related definitions).
*
* @param segmentId the segment identifier
* @return metadata for dependent segments and scorings
+ * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Dependent segments/scorings (lists may be empty).
+ * @api.example {"segments":[],"scorings":[]}
*/
@GET
@Path("/{segmentID}/impacted")
@@ -138,8 +151,13 @@ public class SegmentServiceEndPoint {
/**
* Persists the specified segment in the context server.
+ * Body is a full {@link Segment}: {@code metadata} plus a membership {@code condition}
+ * (JSON field {@code type} + {@code parameterValues}).
*
* @param segment the segment to be persisted
+ * @api.status 204 empty Segment created or updated.
+ * @api.status 400 empty Invalid or unreadable segment body (condition schema / validation).
+ * @api.example {"itemId":"premium-profiles","itemType":"segment","metadata":{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true,"description":"Profiles marked as premium"},"condition":{"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}}
*/
@POST
@Path("/")
@@ -152,6 +170,9 @@ public class SegmentServiceEndPoint {
*
* @param query the query segments must match
* @return a paged list of matching segment metadata
+ * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+ * @api.status 400 empty Invalid or unreadable query body.
+ * @api.example {"list":[{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@POST
@Path("/query")
@@ -161,9 +182,12 @@ public class SegmentServiceEndPoint {
/**
* Returns the segment definition with the given ID.
+ * When the segment does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
*
* @param segmentId the segment identifier
* @return the segment, or {@code null} when it does not exist
+ * @api.status 200 org.apache.unomi.api.segments.Segment Segment found, or empty body when missing.
+ * @api.example {"itemId":"premium-profiles","itemType":"segment","metadata":{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true,"description":"Profiles marked as premium"},"condition":{"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}}
*/
@GET
@Path("/{segmentID}")
@@ -172,14 +196,17 @@ public class SegmentServiceEndPoint {
}
/**
- * Removes the segment definition identified by the specified identifier. We can specify that we want the operation to be validated beforehand so that we can
- * know if any other segment that might use the segment we're trying to delete as a condition might be impacted. If {@code validate} is set to {@code false}, no
- * validation is performed. If set to {@code true}, we will first check if any segment or scoring depends on the segment we're trying to delete and if so we will not delete the
- * segment but rather return the list of the metadata of the impacted items. If no dependents are found, then we properly delete the segment.
+ * Removes the segment definition identified by the specified identifier.
+ * <p>
+ * When {@code validate} is {@code true}, dependents are checked first: if any segment or scoring
+ * references this segment, it is <strong>not</strong> deleted and the impacted metadata is returned.
+ * When {@code validate} is {@code false}, the segment is deleted without that check.
*
* @param segmentId the identifier of the segment we want to delete
- * @param validate whether or not to perform validation
- * @return a list of impacted segment metadata if any or an empty list if none were found or validation was skipped
+ * @param validate whether or not to perform dependency validation before delete
+ * @return dependent metadata when delete was blocked; empty lists when deleted or nothing depended on it
+ * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Empty lists when deleted; otherwise dependents that blocked delete.
+ * @api.example {"segments":[{"id":"premium-buyers","name":"Premium buyers","scope":"mysite","enabled":true}],"scorings":[]}
*/
@DELETE
@Path("/{segmentID}")
@@ -189,8 +216,11 @@ public class SegmentServiceEndPoint {
/**
* Deprecated maintenance endpoint kept for backward compatibility.
+ * Reloads in-memory segments from storage by re-saving each definition.
*
* @deprecated As of version 1.1.0-incubating, not needed anymore
+ * @api.status 204 empty In-memory segments re-persisted.
+ * @api.example {}
*/
@Deprecated
@GET
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java
index 2d86ec0ec..29f90efca 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java
@@ -47,9 +47,11 @@ public class TestEndPoint {
}
/**
- * Returns a simple health-check response.
+ * Returns a simple health-check response for connectivity probes.
*
- * @return the ping response
+ * @return the ping response body ({@code pong})
+ * @api.status 200 empty Plain-text {@code pong} when the REST layer is reachable.
+ * @api.example pong
*/
@GET
@Path("/ping")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java
index 5d8e80ea8..c79ac9fd3 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java
@@ -73,6 +73,8 @@ public class UserListServiceEndPoint {
* @param size maximum number of results to return, or {@code -1} for all matches
* @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
* @return matching user list metadata
+ * @api.status 200 array org.apache.unomi.api.Metadata User list metadata for the requested page (may be empty).
+ * @api.example [{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}]
*/
@GET
@Path("/")
diff --git a/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java b/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java
index b442c17c1..6cd0c9fe2 100644
--- a/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java
+++ b/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java
@@ -27,24 +27,15 @@ import java.io.Serializable;
*/
public class EventCollectorResponse implements Serializable {
/**
- * A bitwise combination of EventService flags indicating what was updated during event processing.
- * The value is composed of the following flags:
- * <ul>
- * <li>0 = NO_CHANGE - No changes occurred</li>
- * <li>1 = ERROR - An error occurred during processing</li>
- * <li>2 = SESSION_UPDATED - The associated session was updated</li>
- * <li>4 = PROFILE_UPDATED - The associated profile was updated</li>
- * </ul>
- * Multiple flags can be combined, for example:
- * <ul>
- * <li>6 = SESSION_UPDATED (2) + PROFILE_UPDATED (4) - Both session and profile were updated</li>
- * </ul>
+ * Bitwise change flags from event processing:
+ * {@code 0}=NO_CHANGE, {@code 1}=ERROR, {@code 2}=SESSION_UPDATED, {@code 4}=PROFILE_UPDATED
+ * (combine with OR, e.g. {@code 6}=session+profile updated).
+ * @api.example 6
*/
private int updated;
/**
- * Contains tracing information about the request processing.
- * This can be used for debugging and monitoring purposes.
+ * Present only when {@code explain=true} and the caller is an administrator / tenant administrator.
*/
private TraceNode requestTracing;
diff --git a/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java
index ee0536692..5fbba51f0 100644
--- a/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java
@@ -48,14 +48,19 @@ public class TaskEndpoint {
private SchedulerService schedulerService;
/**
- * Retrieves all tasks in the system.
+ * Returns scheduled tasks with optional status or type filtering and paging.
+ * <p>
+ * When neither {@code status} nor {@code type} is set, all tasks are loaded and sliced in memory.
*
- * @param status optional status filter
- * @param type optional type filter
- * @param offset pagination offset
- * @param limit pagination limit
- * @param sortBy sort field
- * @return a partial list of tasks matching the criteria
+ * @param status optional {@link ScheduledTask.TaskStatus} name (case-insensitive)
+ * @param type optional task type filter
+ * @param offset zero-based index of the first result
+ * @param limit maximum number of results to return
+ * @param sortBy optional sort field
+ * @return a paged list of matching tasks
+ * @api.status 200 org.apache.unomi.api.PartialList ScheduledTask page (list items are ScheduledTask; may be empty).
+ * @api.status 400 empty Invalid {@code status} value.
+ * @api.example {"list":[{"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"SCHEDULED"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@@ -87,11 +92,13 @@ public class TaskEndpoint {
}
/**
- * Retrieves a specific task by ID.
+ * Returns the scheduled task with the given ID.
*
- * @param taskId the ID of the task to retrieve
+ * @param taskId the task identifier
* @return the requested task
- * @throws WebApplicationException with 404 status if task is not found
+ * @api.status 200 org.apache.unomi.api.tasks.ScheduledTask Task found.
+ * @api.status 404 empty Task not found.
+ * @api.example {"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"SCHEDULED"}
*/
@GET
@Path("/{taskId}")
@@ -105,11 +112,13 @@ public class TaskEndpoint {
}
/**
- * Cancels a scheduled task.
+ * Cancels the scheduled task with the given ID.
*
- * @param taskId the ID of the task to cancel
- * @return 204 No Content on success
- * @throws WebApplicationException with 404 status if task is not found
+ * @param taskId the task identifier
+ * @return an empty response on success
+ * @api.status 204 empty Task cancelled.
+ * @api.status 404 empty Task not found.
+ * @api.example {}
*/
@DELETE
@Path("/{taskId}")
@@ -123,12 +132,14 @@ public class TaskEndpoint {
}
/**
- * Retries a failed task.
+ * Retries a failed scheduled task and returns its updated state.
*
- * @param taskId the ID of the task to retry
- * @param resetFailureCount whether to reset the failure count
+ * @param taskId the task identifier
+ * @param resetFailureCount when {@code true}, resets the failure counter before retry
* @return the retried task
- * @throws WebApplicationException with 404 status if task is not found
+ * @api.status 200 org.apache.unomi.api.tasks.ScheduledTask Task retried.
+ * @api.status 404 empty Task not found.
+ * @api.example {"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"SCHEDULED"}
*/
@POST
@Path("/{taskId}/retry")
@@ -145,11 +156,13 @@ public class TaskEndpoint {
}
/**
- * Resumes a crashed task.
+ * Resumes a crashed scheduled task and returns its updated state.
*
- * @param taskId the ID of the task to resume
+ * @param taskId the task identifier
* @return the resumed task
- * @throws WebApplicationException with 404 status if task is not found
+ * @api.status 200 org.apache.unomi.api.tasks.ScheduledTask Task resumed.
+ * @api.status 404 empty Task not found.
+ * @api.example {"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"RUNNING"}
*/
@POST
@Path("/{taskId}/resume")
diff --git a/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java b/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java
index 788be2537..81cf960bb 100644
--- a/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java
+++ b/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java
@@ -23,12 +23,14 @@ import org.apache.cxf.Bus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
-import org.apache.cxf.jaxrs.openapi.OpenApiCustomizer;
import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
import org.apache.cxf.jaxrs.security.SimpleAuthorizingFilter;
import org.apache.cxf.jaxrs.swagger.ui.SwaggerUiConfig;
import org.apache.cxf.message.Message;
import org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter;
+import dev.inoyu.openapi.enrich.cxf.BundleContextDocPackLoader;
+import dev.inoyu.openapi.enrich.cxf.EnrichingOpenApiCustomizer;
+import dev.inoyu.openapi.enrich.model.DocPack;
import org.apache.unomi.api.ContextRequest;
import org.apache.unomi.api.EventsCollectorRequest;
import org.apache.unomi.api.security.SecurityService;
@@ -430,7 +432,8 @@ public class RestServer {
openApiFeature.setUseContextBasedConfig(true);
SwaggerUiConfig swaggerUiConfig = new SwaggerUiConfig().url("openapi.json").deepLinking(true).queryConfigEnabled(false);
openApiFeature.setSwaggerUiConfig(swaggerUiConfig);
- OpenApiCustomizer customizer = new OpenApiCustomizer();
+ DocPack openApiDocPack = new BundleContextDocPackLoader().loadMerged(bundleContext);
+ EnrichingOpenApiCustomizer customizer = new EnrichingOpenApiCustomizer(openApiDocPack);
customizer.setDynamicBasePath(true);
openApiFeature.setCustomizer(customizer);
jaxrsServerFactoryBean.getFeatures().add(openApiFeature);
diff --git a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java
index 3a61a09b7..26ada07f1 100644
--- a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java
@@ -38,7 +38,8 @@ import java.util.List;
/**
* REST endpoint for managing tenants in the Apache Unomi system.
* Provides operations for creating, updating, deleting, and retrieving tenants,
- * as well as managing their API keys and configurations.
+ * as well as managing their API keys, usage, and event retention.
+ * Class-level access requires the administrator role; usage and purge also accept tenant administrators.
*/
@Produces(MediaType.APPLICATION_JSON)
@CrossOriginResourceSharing(
@@ -59,7 +60,9 @@ public class TenantEndpoint {
/**
* Retrieves all tenants in the system.
*
- * @return a list of all tenants
+ * @return a JSON array of all tenants
+ * @api.status 200 array org.apache.unomi.api.tenants.Tenant List of tenants (may be empty).
+ * @api.example [{"itemId":"acme","itemType":"tenant","name":"Acme Corp","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","apiKeys":[],"properties":{},"restrictedEventTypes":[],"authorizedIPs":[]}]
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@@ -72,6 +75,9 @@ public class TenantEndpoint {
*
* @param tenantId the ID of the tenant to retrieve
* @return the requested tenant with 200 status, or 404 if tenant is not found
+ * @api.status 200 org.apache.unomi.api.tenants.Tenant Tenant found.
+ * @api.status 404 empty Tenant not found.
+ * @api.example {"itemId":"acme","itemType":"tenant","name":"Acme Corp","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","lastModificationDate":"2026-01-15T10:00:00.000Z","apiKeys":[{"itemId":"key-1","itemType":"apiKey","maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","creationDate":"2026-01-15T10:00:00.000Z","revoked":false}],"properties":{},"restrictedEventTypes":[],"authorizedIPs":["10.0.0.0/8"]}
*/
@GET
@Path("/{tenantId}")
@@ -87,10 +93,17 @@ public class TenantEndpoint {
/**
* Creates a new tenant.
+ * On success, Unomi generates both a PUBLIC and a PRIVATE API key for the tenant.
+ * Plaintext keys are only returned in responses that explicitly expose
+ * {@link ApiKeyCreationResult} (not on this create response — the returned
+ * {@link Tenant} includes hashed/masked key metadata only).
*
* @param request the tenant creation request containing tenant details
- * @return the created tenant with generated API keys
+ * @return the created tenant with generated API key metadata
* @throws WebApplicationException with 400 status if request is invalid
+ * @api.status 200 org.apache.unomi.api.tenants.Tenant Tenant created (status ACTIVE, public and private keys generated).
+ * @api.status 400 empty Missing or blank requestedId.
+ * @api.example {"itemId":"acme","itemType":"tenant","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","apiKeys":[{"maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","revoked":false},{"maskedKey":"unomi_v1_****cd34","keyType":"PRIVATE","revoked":false}],"properties":{"name":"Acme"},"restrictedEventTypes":[],"authorizedIPs":[]}
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@@ -107,11 +120,16 @@ public class TenantEndpoint {
/**
* Updates an existing tenant.
+ * The path {@code tenantId} must equal {@code tenant.itemId}.
*
* @param tenantId the ID of the tenant to update
* @param tenant the updated tenant information
* @return the updated tenant
- * @throws WebApplicationException with 404 status if tenant is not found
+ * @throws WebApplicationException with 400 if IDs mismatch, 404 if tenant is not found
+ * @api.status 200 org.apache.unomi.api.tenants.Tenant Tenant updated.
+ * @api.status 400 empty Path tenantId does not match body itemId.
+ * @api.status 404 empty Tenant not found.
+ * @api.example {"itemId":"acme","itemType":"tenant","name":"Acme Corp","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","lastModificationDate":"2026-01-15T10:00:00.000Z","apiKeys":[{"itemId":"key-1","itemType":"apiKey","maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","creationDate":"2026-01-15T10:00:00.000Z","revoked":false}],"properties":{},"restrictedEventTypes":[],"authorizedIPs":["10.0.0.0/8"]}
*/
@PUT
@Path("/{tenantId}")
@@ -137,6 +155,9 @@ public class TenantEndpoint {
* @param tenantId the ID of the tenant to delete
* @return 204 No Content on success
* @throws WebApplicationException with 404 status if tenant is not found
+ * @api.status 204 empty Tenant deleted.
+ * @api.status 404 empty Tenant not found.
+ * @api.example {}
*/
@DELETE
@Path("/{tenantId}")
@@ -152,12 +173,17 @@ public class TenantEndpoint {
/**
* Generates a new API key for a tenant.
+ * Replaces any existing key of the same {@code type}. The plaintext key is returned
+ * only in this response ({@link ApiKeyCreationResult#getPlainTextKey()}).
*
* @param tenantId the ID of the tenant
- * @param type the type of API key to generate (PUBLIC or PRIVATE)
- * @param validityDays the validity period in days (0 or null for no expiration)
+ * @param type the type of API key to generate ({@code PUBLIC} or {@code PRIVATE})
+ * @param validityDays the validity period in days ({@code 0} or omitted for no expiration)
* @return the generated key metadata together with its one-time plaintext value
* @throws WebApplicationException with 404 status if tenant is not found
+ * @api.status 200 org.apache.unomi.api.tenants.ApiKeyCreationResult Key created; store plainTextKey immediately.
+ * @api.status 404 empty Tenant not found.
+ * @api.example {"apiKey":{"itemId":"key-1","itemType":"apiKey","maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","creationDate":"2026-01-15T10:00:00.000Z","revoked":false},"plainTextKey":"unomi_v1_0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"}
*/
@POST
@Path("/{tenantId}/apikeys")
@@ -185,9 +211,12 @@ public class TenantEndpoint {
* Validates an API key for a tenant.
*
* @param tenantId the ID of the tenant
- * @param apiKey the API key to validate
- * @param type the type of API key (PUBLIC or PRIVATE)
+ * @param apiKey the plaintext API key to validate
+ * @param type the type of API key ({@code PUBLIC} or {@code PRIVATE})
* @return 200 OK if valid, 401 Unauthorized if invalid
+ * @api.status 200 empty API key is valid for the tenant and type.
+ * @api.status 401 empty API key is missing, wrong type, revoked, expired, or unknown.
+ * @api.example {}
*/
@GET
@Path("/{tenantId}/apikeys/validate")
@@ -208,8 +237,12 @@ public class TenantEndpoint {
* this endpoint exposes measured usage for operators and control planes.
*
* @param tenantId tenant identifier
- * @param period reporting window: {@code current-month}, {@code YYYY-MM}, or legacy {@code 24h}
+ * @param period reporting window: {@code current-month} (default), {@code YYYY-MM}, or legacy {@code 24h}
* @return usage snapshot, 404 if tenant is missing, 400 for unsupported period
+ * @api.status 200 org.apache.unomi.api.tenants.TenantUsage Usage snapshot (may be slightly stale until the next collection cycle).
+ * @api.status 400 empty Unsupported period value.
+ * @api.status 404 empty Tenant not found.
+ * @api.example {"tenantId":"acme","period":"2026-07","periodStart":1751328000000,"periodEnd":1754006400000,"profileCount":1200,"eventCount":45000,"scopeCount":3,"segmentCount":12,"ruleCount":8,"storageDocumentCount":52000,"activeApiKeyCount":2,"restRequestCount":180,"scopeUsages":[{"scopeId":"site","segmentCount":4,"ruleCount":3}],"collectedAt":1754000000000}
*/
@GET
@Path("/{tenantId}/usage")
@@ -232,11 +265,17 @@ public class TenantEndpoint {
/**
* Deletes events for the tenant at least {@code retentionDays} days old.
* Destructive operation; intended for upstream retention jobs.
+ * Minimum retention is {@link TenantUsageService#MIN_EVENT_RETENTION_DAYS} days.
*
* @param tenantId tenant identifier
* @param retentionDays age cutoff in whole days (minimum {@link TenantUsageService#MIN_EVENT_RETENTION_DAYS})
* @return purge summary, 404 if tenant is missing, 400 for invalid retention, 500 if the
* persistence layer failed to complete the deletion (see {@code purgeRequested})
+ * @api.status 200 org.apache.unomi.api.tenants.TenantEventPurgeResult Delete-by-query completed ({@code purgeRequested}=true).
+ * @api.status 400 empty retentionDays missing, not positive, or below the configured minimum.
+ * @api.status 404 empty Tenant not found.
+ * @api.status 500 org.apache.unomi.api.tenants.TenantEventPurgeResult Persistence accepted the request but deletion failed ({@code purgeRequested}=false).
+ * @api.example {"tenantId":"acme","retentionDays":30,"eventsMatched":1200,"purgeRequested":true,"requestedAt":1754000000000}
*/
@POST
@Path("/{tenantId}/purge/events")
diff --git a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java
index cddc93b4e..8ec93db62 100644
--- a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java
+++ b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java
@@ -19,12 +19,20 @@ package org.apache.unomi.rest.tenants;
import java.util.Map;
/**
- * REST request payload for creating or updating a {@link org.apache.unomi.api.tenants.Tenant}.
- * Carries the desired tenant id and an open properties map that maps to
- * tenant configuration fields accepted by {@link org.apache.unomi.api.tenants.TenantService}.
+ * REST request body for creating a {@link org.apache.unomi.api.tenants.Tenant}.
+ * Carries the desired tenant id and an open properties map stored on the tenant.
*/
public class TenantRequest {
+ /**
+ * Desired tenant identifier ({@link org.apache.unomi.api.Item#itemId}). Required; blank values yield HTTP 400.
+ * @api.example acme
+ */
private String requestedId;
+ /**
+ * Optional open map of custom tenant properties persisted as {@code Tenant.properties}.
+ * Free-form keys; common ones include display hints such as {@code name} or {@code description}.
+ * @api.example {"name":"Acme Corp","description":"Production tenant"}
+ */
private Map<String, Object> properties;
/**
@@ -62,4 +70,4 @@ public class TenantRequest {
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
-}
\ No newline at end of file
+}
diff --git a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java
index 683d75bc8..9c11fec8d 100644
--- a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java
+++ b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java
@@ -31,13 +31,44 @@ import java.util.List;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class TraceNode implements Serializable {
+ /**
+ * Type of operation represented by this node.
+ * @api.example event-collection
+ */
private String operationType;
+ /**
+ * Human-readable description of this operation.
+ * @api.example Processing event collection request
+ */
private String description;
+ /**
+ * Additional context information for this operation.
+ * @api.example scope=mysite
+ */
private String context;
+ /**
+ * Result summary for this operation.
+ * @api.example updated=6
+ */
private String result;
+ /**
+ * Start time of this operation in milliseconds since epoch.
+ * @api.example 1718448600000
+ */
private long startTime;
+ /**
+ * End time of this operation in milliseconds since epoch.
+ * @api.example 1718448600125
+ */
private long endTime;
+ /**
+ * Trace messages recorded for this node.
+ * @api.example ["started","rules-evaluated"]
+ */
private List<String> traces;
+ /**
+ * Nested child trace nodes.
+ */
private List<TraceNode> children;
/**
@@ -160,6 +191,7 @@ public class TraceNode implements Serializable {
* Returns the operation duration in milliseconds.
*
* @return duration as {@code endTime - startTime}, or zero if end time precedes start time
+ * @api.example 125
*/
public long getDuration() {
return Math.max(0, endTime - startTime);