API Explorer

Dynamic Resource Doc

A Dynamic Resource Doc defines a single Endpoint at runtime: its verb, URL path, summary, description, example request and response bodies, error list, tags and Roles - plus a method body written in Scala which is compiled at runtime and becomes the handler of the Endpoint.

Whereas a Dynamic Endpoint (see here) is created from a Swagger / OpenAPI file and contains no code (its behaviour is selected by the swagger host field), a Dynamic Resource Doc is code: the method body has access to the full CallContext and can transform payloads, call Connector methods and NewStyle functions, or invoke Dynamic Message Docs.

Like all Resource Docs, Dynamic Resource Docs are part of the server registry of the API (see here), so they appear in the API Explorer and resource-docs endpoints like any Static endpoint.

Dynamic Resource Docs can be created at System level or Bank / Space level, and are served under the /obp/dynamic-endpoint/dynamic-resource-doc path prefix (configurable via the url.prefix.dynamic.resourceDoc prop).

Authentication and Role checks are applied to the compiled endpoint exactly as for Static endpoints - including the checks that run inside the shared authentication step: Consumer disabled, User locked / deleted, Consent processing and Rate Limiting.

Every call to a Dynamic Resource Doc endpoint is recorded as an API Metric, like a call to a Static endpoint: the response it gave, and also a call refused for missing authentication or Roles.

Some other cross-cutting features of the Static pipeline do not currently apply to Dynamic Resource Doc endpoints: the JSON Schema Validation and Force-Error interceptors are not run, the Idempotency-Key mechanism is unavailable, and handlers run on auto-commit (no request-scoped database transaction). Dynamic Endpoints created from Swagger (the proxy path) do run the JSON Schema Validation interceptors.

Because the method body is user-supplied code compiled at runtime, this feature is guarded by the allow_user_generated_scala_code prop (default: false) and the Roles CanCreateDynamicResourceDoc / CanCreateBankLevelDynamicResourceDoc etc.

A helper endpoint (POST /management/dynamic-resource-docs/endpoint-code) can generate a method-body template from example request / response bodies.

The method body

The body is inlined, unchanged, into a native http4s handler. Do not wrap it in a method or class. In scope:

  • callContext: CallContext - the authenticated User, Consumer, resourceDocument, httpBody (the raw request body as a String) and callContext.callContext (the same as an Option).
  • request: org.http4s.Request[IO] - the raw request, for headers or the URI.
  • pathParams: Map[String, String] - one entry per UPPER_CASE segment of the request URL, for example pathParams("BANK_ID").
  • RequestRootJsonClass / ResponseRootJsonClass - case classes generated from the example request body and success response body. Parse the request with JsonAliases.parse(rawBody).extract[RequestRootJsonClass].
  • errorResponse(message, code = 400) - returns the standard OBP error JSON with that status.
  • Imports: Future and the OBP execution context, HttpCode, OBPReturnType, ErrorMessages.{InvalidJsonFormat, InvalidRequestPayload}, MappingException, cats.effect.IO, net.liftweb.common.{Box, Empty, Failure, Full} (for matching on Connector results), and an implicit formats.

The last expression is the response. Return Future.successful((value, HttpCode.200(callContext))) - any case class, Map or List that serialises to JSON, paired with the call context whose status was set by HttpCode - or errorResponse(...). An implicit converts that Future[(T, Option[CallContext])] into the http4s response. The Lift-era shapes Full(successJsonResponse(...)) and Box[JsonResponse] are not accepted; a body that returns them fails to compile (OBP-40045).

The smallest valid body:

Future.successful((Map("hello" -> "world"), HttpCode.`200`(callContext)))

To check a body before creating anything, POST /obp/v7.0.0/management/dynamic-resource-docs/compile compiles it the same way and returns the compiler's errors with line numbers relative to the body. The API Manager's Create page uses it for its Compile button and for the loop in which Opey rewrites the body until it compiles.

See here for how Dynamic Resource Docs relate to the other runtime-defined building blocks, and here for how an operator can require a second person to approve each definition before it is compiled and served.

The method body is Scala unless programming_lang says otherwise: Java for a Java class, or Query for a declaration that reads Dynamic Entity records instead of code (see here).