Class ChatApiHelper

java.lang.Object
org.codelibs.fess.helper.ChatApiHelper

public class ChatApiHelper extends Object
Shared utilities for v2 chat API handlers.

Extracted common logic from ChatHandler and ChatStreamHandler so it can be reused across chat-related handlers without duplication. (Historically these utilities were also used to mirror the v1 ChatApiManager behavior; the v1 implementation has since been extracted into the fess-webapp-v1-api plugin.)

This is a DI-managed singleton helper (registered as chatApiHelper); obtain it via ComponentUtil.getChatApiHelper() and call the instance methods. Helpers in Fess are singletons accessed through ComponentUtil, not static utility classes.

  • Constructor Details

    • ChatApiHelper

      public ChatApiHelper()
      Default constructor for ChatApiHelper.
  • Method Details

    • parseFieldFilters

      public Map<String,String[]> parseFieldFilters(Map<String,Object> raw, Map<String,List<String>> warnings) throws IOException
      Parses the fields object (nested structure) or legacy fields.label dotted-key from a raw request map into the map shape accepted by ChatClient.

      The v2 wire format accepts either:

      • "fields": {"label": "value"} — nested object (preferred v2 form)
      • "fields": {"label": ["v1", "v2"]} — nested object with array

      Values that are not present in the LabelTypeHelper allowlist are dropped silently; rejected values are appended to warnings under the key "fields.label".

      Parameters:
      raw - the raw request body map
      warnings - mutable map to collect rejected values (keyed by field name)
      Returns:
      a map of validated field filters, or an empty map when none are present
      Throws:
      IOException - if fields.label exceeds the configured maximum array size or per-element length
    • parseExtraQueries

      public String[] parseExtraQueries(Map<String,Object> raw, Map<String,List<String>> warnings) throws IOException
      Parses the extra_queries array (or scalar) from the raw request map.

      Values are validated against the ViewHelper facet-query allowlist. Rejected values are appended to warnings under the key "extra_queries".

      Parameters:
      raw - the raw request body map
      warnings - mutable map to collect rejected values (keyed by field name)
      Returns:
      array of validated extra query strings; empty array when none present
      Throws:
      IOException - if extra_queries exceeds the configured maximum array size or per-element length
    • parseRequestBody

      public ChatRequestBody parseRequestBody(Map<String,Object> raw, int maxMessageLength) throws IOException
      Parses a raw v2 chat JSON body into a validated ChatRequestBody.
      Parameters:
      raw - the parsed JSON request body
      maxMessageLength - upper bound on the message length, in characters
      Returns:
      a validated request body
      Throws:
      ChatRequestBody.InvalidSessionIdException - if session_id exceeds 100 characters or contains invalid characters
      ChatRequestBody.MessageTooLongException - if message exceeds maxMessageLength
      ChatRequestBody.TooManyValuesException - if extra_queries or fields.label exceeds the count or per-element length limit
      IOException - if validation reports an unrecoverable error
    • toSourceMaps

      public List<Map<String,Object>> toSourceMaps(List<ChatMessage.ChatSource> sources)
      Converts chat sources to the v2 API response shape.
      Parameters:
      sources - list of chat sources to convert
      Returns:
      list of snake_case maps, one per source
    • getUserId

      public String getUserId()
      Resolves the effective user identifier for a chat request.

      Mirrors the logic in v1 ChatApiManager#getUserId: prefer the authenticated username, fall back to the cookie-bound userCode for guests. This keeps chat usable for anonymous SPA visitors when login is not required.

      Returns:
      the user identifier (never null)
    • resolveChatRateLimitKey

      public String resolveChatRateLimitKey(String username, Supplier<String> clientIpSupplier)
      Resolves a rate-limit key for the chat endpoints that an anonymous caller cannot rotate.

      Unlike getUserId() (which keys chat-session continuity and intentionally falls back to the cookie-bound, client-supplied userCode for guests), this key must be robust against abuse: the guest userCode is only validated by regex/length, so a malicious client could forge and rotate it per request to obtain a fresh throttle bucket and bypass the limit. Authenticated (non-guest) callers are keyed by their server-validated username ("u:"+username); all other callers (guest / anonymous / forged / rotated / blank userCode) are keyed by the proxy-aware client IP ("ip:"+clientIp), which an anonymous attacker cannot freely rotate. The client-IP supplier is evaluated lazily so the IP is only resolved for guest visitors.

      The returned key is namespaced ("u:" for usernames, "ip:" for client IPs) so the two key spaces never collide, and is always non-blank so the chat throttle always applies.

      The client IP is resolved by the caller (the v2 chat handlers, via RateLimitHelper#getClientIp) rather than here, so this helper carries no servlet-API dependency and stays loadable by non-web Fess processes (e.g. the crawler) whose DI container also instantiates it. Behind a reverse proxy the per-IP key is only as trustworthy as the proxy setup: rate.limit.trusted.proxies gates which proxies' X-Forwarded-For / X-Real-IP headers are honored, so the fronting proxy must be trusted and must sanitize any inbound forwarded headers for the per-IP key to resist rotation.

      Parameters:
      username - the authenticated username (may be Constants.GUEST_USER)
      clientIpSupplier - supplies the proxy-aware client IP for guest visitors
      Returns:
      the namespaced chat rate-limit key (never null/blank)
    • getMaxMessageLength

      public int getMaxMessageLength(org.codelibs.fess.mylasta.direction.FessConfig fessConfig)
      Resolves rag.chat.message.max.length from fess_config system properties, defaulting to 4000 on parse failure.
      Parameters:
      fessConfig - active Fess config
      Returns:
      max chat message length in characters