{# Bengal OpenAPI Component Library (Kida) ======================================= Self-contained {% def %} components for the OpenAPI endpoint template. Each component receives all data it needs through explicit parameters instead of ambient {% let %} context, so they are importable and isolated. Migrated from (kept as reference/fallback partials): - partials/endpoint-header.html -> endpoint_header(element, section) - partials/param-row.html -> param_row(param) - partials/request-body.html -> request_body(req_body) - partials/responses.html -> responses(responses, response_scope=...) - partials/code-samples.html -> code_samples(method, path, ...) Usage: {% from 'autodoc/openapi/_components.html' import endpoint_header, param_row, request_body, responses, code_samples %} Kida Features (confirmed against installed kida-templates 0.6.0): - {% def name(param=default) %} with default values - {% match %}/{% case %} for status code dispatch - {% with %} for nil-safe optional sections - {% spaceless %} for compact badge output - {% from %} import of the recursive schema_viewer component - Optional chaining (?.) and null coalescing (??) #} {% from 'autodoc/openapi/_schema.html' import schema_viewer %} {# ============================================================================= ENDPOINT HEADER Displays: breadcrumb, title, operation id, description, auth + tag badges. Ported from partials/endpoint-header.html. ============================================================================= #} {% def endpoint_header(element, section) %} {% let meta = element.typed_metadata %} {% let method = meta?.method ?? 'GET' %} {% let path = meta?.path ?? '/unknown' %} {% let summary = meta?.summary ?? element.name %} {% let operation_id = meta?.operation_id %} {% let tags = meta?.tags ?? () %} {% let security = meta?.security ?? () %} {# Section context: the endpoint's parent section is its FIRST tag's section, whose parent is the REST API root. The endpoint has a single canonical page (under its first tag), but it may carry secondary tags that each have their own tag section ({root}/tags/{tag}/). Derive every tag's section URL from the API root so secondary-tag chips cross-link to the right section rather than all pointing at the current (first-tag) section. Link from real section hrefs rather than named routes (this engine resolves url_for() string targets as literal paths, not Flask-style endpoints). #} {% let tag_section = section %} {# The API root href already carries a trailing slash (section snapshot href), so per-tag section URLs join as {root}tags/{tag-slug}/, matching the {prefix}/tags/{tag} sections that section_builders.py creates. #} {% let api_home_href = section?.parent?.href ?? section?.href ?? '/' %} {% let tag_href = tag_section?.href ?? api_home_href %}
{# Breadcrumb / Tag Path #} {% if tags %} {% end %} {# Title: Summary or Operation ID #}

{{ summary }}

{# Operation ID (for developers) #} {% with operation_id as op_id %}
{{ op_id }}
{% end %} {# Description #} {% with element.description as desc %}
{{ desc | markdownify | safe }}
{% end %} {# Meta Badges Row #}
{# Authentication Requirements #} {% if security %}
{% for scheme in security %} {{ scheme }} {% end %}
{% end %} {# Tags. The endpoint lives under its FIRST tag's section, so the first chip uses that section's href; secondary-tag chips construct their own section URL. The tag is used RAW (not slugified) so the href matches the section directory section_builders.py creates ({root}tags/{tag}/) — slugifying here produced broken links for tags like "Admin Tools". #} {% if tags %}
{% for tag in tags %} {% let chip_href = tag_href if loop.first else (api_home_href ~ 'tags/' ~ tag ~ '/') %} {{ tag }} {% end %}
{% end %}
{% end %} {# ============================================================================= PARAM ROW Atomic display unit for a single API parameter. Ported from partials/param-row.html. ============================================================================= #} {% def param_row(param) %} {% let name = param?.name ?? 'unnamed' %} {% let schema = param.get('schema', {}) if param is mapping else {} %} {% let type = param?.schema_type ?? schema?.type ?? 'string' %} {% let location = param?.location ?? (param.get('in', 'query') if param is mapping else 'query') %} {% let is_required = param?.required ?? false %} {% let description = param?.description ?? '' %} {# `default`/`enum`/`example` only exist on raw dict parameters, not on the OpenAPIParameterMetadata dataclass, so access them mapping-safely and fall back to the nested schema object. #} {% let default_val = (param.get('default') if param is mapping else none) ?? schema?.default %} {% let enum_vals = (param.get('enum') if param is mapping else none) ?? schema?.enum %} {% let example = (param.get('example') if param is mapping else none) ?? schema?.example %}
{# Parameter Meta: Name, Type, Badges #}
{{ name }} {{ type }} {% spaceless %}
{% if is_required %} required {% end %} {{ location }}
{% end %}
{# Description #} {% if description %}
{{ description | markdownify | safe }}
{% end %} {# Default Value #} {% with default_val as default %}
Default: {{ default }}
{% end %} {# Enum Values #} {% with enum_vals as enums %} {% if enums | length > 0 %}
Allowed: {% spaceless %} {% for val in enums %} {{ val }}{% if not loop.last %} {% end %} {% end %} {% end %}
{% end %} {% end %} {# Example #} {% with example as ex %}
Example: {{ ex }}
{% end %}
{% end %} {# ============================================================================= REQUEST BODY Displays request body content type, description, schema ref + inline schema. Ported from partials/request-body.html. ============================================================================= #} {% def request_body(req_body) %} {% let content_type = req_body?.content_type ?? 'application/json' %} {% let is_required = req_body?.required ?? false %} {% let description = req_body?.description ?? '' %} {% let schema_ref = req_body?.schema_ref %} {# `schema` only exists on raw dict request bodies, not on the OpenAPIRequestBodyMetadata dataclass, so access it mapping-safely. #} {% let inline_schema = req_body.get('schema') if req_body is mapping else none %}

Request Body

{% spaceless %} {% if is_required %} required {% end %} {{ content_type }} {% end %}
{# Description #} {% if description %}
{{ description | markdownify | safe }}
{% end %} {# Schema Reference Link #} {% with schema_ref as ref %} {% let schema_name = ref | split('#/components/schemas/') | last %}
Schema: {{ schema_name }}
{% end %} {# Inline Schema Viewer #} {% with inline_schema as s %} {{ schema_viewer(s, name=none, depth=0) }} {% end %}
{% end %} {# ============================================================================= RESPONSES Displays API responses with status code tabs and schema information. Ported from partials/responses.html. ============================================================================= #} {% def status_category(code) %} {% let code_str = code | string %} {% match code_str | first %} {% case '2' %}success {% case '3' %}redirect {% case '4' %}client-error {% case '5' %}server-error {% case _ %}info {% end %} {% end %} {% def status_color_class(code) %} {% let code_str = code | string %} {% match code_str | first %} {% case '2' %}badge--success {% case '3' %}badge--info {% case '4' %}badge--warning {% case '5' %}badge--danger {% case _ %}badge--outline {% end %} {% end %} {% def responses(responses, response_scope='response') %} {% let response_scope = response_scope ?? 'response' %}

Responses

{# Response Tabs #} {% if responses | length > 1 %}
{% for response in responses %} {% let code = response?.status_code ?? '200' %} {% let category = status_category(code) %} {% let color_class = status_color_class(code) %} {% spaceless %} {% end %} {% end %}
{% end %} {# Response Panels #}
{% for response in responses %} {% let code = response?.status_code ?? '200' %} {% let description = response?.description ?? '' %} {% let content_type = response?.content_type %} {% let schema_ref = response?.schema_ref %} {# `schema`/`example` only exist on raw dict responses, not on the OpenAPIResponseMetadata dataclass, so access them mapping-safely. #} {% let inline_schema = response.get('schema') if response is mapping else none %} {% let example = response.get('example') if response is mapping else none %} {% let category = status_category(code) %}
{# Status + Description Header #}
{{ code }} {% if description %} {{ description }} {% end %}
{# Content Type #} {% with content_type as ct %}
Content-Type: {{ ct }}
{% end %} {# Schema Reference #} {% with schema_ref as ref %} {% let schema_name = ref | split('#/components/schemas/') | last %} {% end %} {# Inline Schema #} {% with inline_schema as s %} {{ schema_viewer(s, name=none, depth=0) }} {% end %} {# Example Response #} {% with example as ex %}
Example Response
{{ ex | tojson(indent=2) }}
{% end %}
{% end %}
{% end %} {# ============================================================================= CODE SAMPLES Multi-language code sample panel with tab switching. Ported from partials/code-samples.html. ============================================================================= #} {% def code_samples(method, path, request_body=none, response_example=none, base_url=none, parameters=none, auth_scheme=none) %} {% let method_upper = method |> upper ?? 'GET' %} {% let api_base = base_url ?? 'https://api.example.com' %} {% let full_url = api_base ~ path %} {% let auth = auth_scheme ?? 'Bearer' %} {% let sample_id = (method_upper ~ '-' ~ path) | slugify %} {# Available languages for code samples #} {% let languages = [ {'id': 'curl', 'name': 'cURL', 'icon': 'terminal'}, {'id': 'python', 'name': 'Python', 'icon': 'code'}, {'id': 'javascript', 'name': 'JavaScript', 'icon': 'code'}, {'id': 'go', 'name': 'Go', 'icon': 'code'}, {'id': 'ruby', 'name': 'Ruby', 'icon': 'code'}, {'id': 'php', 'name': 'PHP', 'icon': 'code'} ] %}
{# Header with Language Tabs #}

Request

{% for lang in languages %} {% spaceless %} {% end %} {% end %}
{# Code Panels #}
{# cURL Panel #}
{{ generate_code_sample('curl', method, path, base_url=api_base, request_body=request_body, parameters=parameters, auth_scheme=auth) }}
{# Python Panel #}
{{ generate_code_sample('python', method, path, base_url=api_base, request_body=request_body, parameters=parameters, auth_scheme=auth) }}
{# JavaScript Panel #}
{{ generate_code_sample('javascript', method, path, base_url=api_base, request_body=request_body, parameters=parameters, auth_scheme=auth) }}
{# Go Panel #}
{{ generate_code_sample('go', method, path, base_url=api_base, request_body=request_body, parameters=parameters, auth_scheme=auth) }}
{# Ruby Panel #}
{{ generate_code_sample('ruby', method, path, base_url=api_base, request_body=request_body, parameters=parameters, auth_scheme=auth) }}
{# PHP Panel #}
{{ generate_code_sample('php', method, path, base_url=api_base, request_body=request_body, parameters=parameters, auth_scheme=auth) }}
{# Response Preview (optional) #} {% with response_example as example %}

Response

200 OK
{{ example | tojson(indent=2) }}
{% end %}
{% end %}