mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-07-21 17:19:29 +00:00
949 lines
33 KiB
Python
949 lines
33 KiB
Python
"""OpenAPI examples and operation metadata for the public JSON API."""
|
|
from __future__ import annotations
|
|
|
|
from copy import deepcopy
|
|
from typing import Any
|
|
|
|
from .routes.api import BulkVerseRequest, BulkVerseResponse
|
|
|
|
|
|
JSON = "application/json"
|
|
|
|
|
|
def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
|
|
"""Merge nested OpenAPI metadata dictionaries without mutating inputs."""
|
|
merged = deepcopy(base)
|
|
for key, value in override.items():
|
|
if isinstance(merged.get(key), dict) and isinstance(value, dict):
|
|
merged[key] = _deep_merge(merged[key], value)
|
|
else:
|
|
merged[key] = deepcopy(value)
|
|
return merged
|
|
|
|
|
|
def _path_param(name: str, schema_type: str, example: Any, description: str) -> dict:
|
|
return {
|
|
"name": name,
|
|
"in": "path",
|
|
"required": True,
|
|
"description": description,
|
|
"schema": {"type": schema_type},
|
|
"example": example,
|
|
}
|
|
|
|
|
|
def _query_param(
|
|
name: str,
|
|
schema: dict[str, Any],
|
|
example: Any,
|
|
description: str,
|
|
*,
|
|
required: bool = False,
|
|
) -> dict:
|
|
return {
|
|
"name": name,
|
|
"in": "query",
|
|
"required": required,
|
|
"description": description,
|
|
"schema": schema,
|
|
"example": example,
|
|
}
|
|
|
|
|
|
def _json_examples(examples: dict[str, dict[str, Any]]) -> dict:
|
|
return {"content": {JSON: {"examples": examples}}}
|
|
|
|
|
|
def _success_examples(examples: dict[str, dict[str, Any]], description: str) -> dict:
|
|
response = {"description": description}
|
|
response.update(_json_examples(examples))
|
|
return {"responses": {"200": response}}
|
|
|
|
|
|
BOOK_PARAM = _path_param(
|
|
"book",
|
|
"string",
|
|
"John",
|
|
"Canonical KJV book name or supported abbreviation, such as John, Gen, or 1 Cor.",
|
|
)
|
|
CHAPTER_PARAM = _path_param("chapter", "integer", 3, "Chapter number within the book.")
|
|
VERSE_PARAM = _path_param("verse", "integer", 16, "Verse number within the chapter.")
|
|
|
|
|
|
OPENAPI_OPERATION_EXAMPLES: dict[str, dict[str, Any]] = {
|
|
"/api/": {
|
|
"tags": ["API"],
|
|
"summary": "API index",
|
|
"description": "Discover documentation links and the main JSON endpoints.",
|
|
**_success_examples(
|
|
{
|
|
"index": {
|
|
"summary": "Documentation and endpoint index",
|
|
"value": {
|
|
"name": "KJV Study API",
|
|
"version": "1.0.0",
|
|
"documentation": {
|
|
"swagger_ui": "/api/docs",
|
|
"redoc": "/api/redoc",
|
|
"openapi_json": "/api/openapi.json",
|
|
},
|
|
"endpoints": {
|
|
"verse": "/api/verse/{book}/{chapter}/{verse}",
|
|
"search": "/api/search?q={query}",
|
|
"bulk_verses": "/api/verses/bulk",
|
|
},
|
|
},
|
|
}
|
|
},
|
|
"API index with documentation and endpoint links.",
|
|
),
|
|
},
|
|
"/api/health": {
|
|
"tags": ["API"],
|
|
"summary": "API health check",
|
|
"description": "Lightweight JSON health check for API monitors.",
|
|
**_success_examples(
|
|
{
|
|
"healthy": {
|
|
"summary": "Healthy response",
|
|
"value": {
|
|
"status": "healthy",
|
|
"service": "KJV Study API",
|
|
"version": "1.0.0",
|
|
},
|
|
}
|
|
},
|
|
"API health status.",
|
|
),
|
|
},
|
|
"/api/search": {
|
|
"tags": ["Search"],
|
|
"summary": "Search verses",
|
|
"description": "Search KJV verses by word, phrase, or direct verse reference.",
|
|
"parameters": [
|
|
_query_param(
|
|
"q",
|
|
{"type": "string", "nullable": True},
|
|
"love",
|
|
"Search text or direct verse reference.",
|
|
),
|
|
_query_param(
|
|
"limit",
|
|
{"type": "integer", "nullable": True},
|
|
5,
|
|
"Optional maximum number of verse results.",
|
|
),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"word_search": {
|
|
"summary": "Search for a word",
|
|
"value": {
|
|
"query": "love",
|
|
"results": [
|
|
{
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world...",
|
|
"score": 1.0,
|
|
}
|
|
],
|
|
"total": 1,
|
|
"is_direct_verse": False,
|
|
},
|
|
},
|
|
"direct_reference": {
|
|
"summary": "Direct verse reference",
|
|
"value": {
|
|
"query": "John 3:16",
|
|
"results": [
|
|
{
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world...",
|
|
"score": 100.0,
|
|
}
|
|
],
|
|
"total": 1,
|
|
"is_direct_verse": True,
|
|
},
|
|
},
|
|
},
|
|
"Search results.",
|
|
),
|
|
},
|
|
"/api/universal-search": {
|
|
"tags": ["Search"],
|
|
"summary": "Search all study content",
|
|
"description": "Search across books, verses, topics, stories, reading plans, and resources.",
|
|
"parameters": [
|
|
_query_param(
|
|
"q",
|
|
{"type": "string", "nullable": True},
|
|
"faith",
|
|
"Search text.",
|
|
),
|
|
_query_param(
|
|
"limit",
|
|
{"type": "integer", "minimum": 1, "maximum": 100, "default": 5},
|
|
5,
|
|
"Maximum results per content type.",
|
|
),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"faith": {
|
|
"summary": "Search across content types",
|
|
"value": {
|
|
"query": "faith",
|
|
"results": {
|
|
"verses": [
|
|
{
|
|
"reference": "Hebrews 11:1",
|
|
"text": "Now faith is the substance...",
|
|
"url": "/book/Hebrews/chapter/11/verse/1",
|
|
}
|
|
],
|
|
"topics": [{"name": "Faith", "url": "/topics/faith"}],
|
|
"resources": [
|
|
{"name": "Prayer & Faith", "url": "/study-guides/prayer-faith"}
|
|
],
|
|
},
|
|
},
|
|
}
|
|
},
|
|
"Grouped universal search results.",
|
|
),
|
|
},
|
|
"/api/verse-of-the-day": {
|
|
"tags": ["Verses"],
|
|
"summary": "Get verse of the day",
|
|
"description": "Return the daily featured KJV verse.",
|
|
**_success_examples(
|
|
{
|
|
"daily_verse": {
|
|
"summary": "Daily verse",
|
|
"value": {
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world...",
|
|
"url": "/book/John/chapter/3#verse-16",
|
|
"red_letter": "full",
|
|
},
|
|
}
|
|
},
|
|
"Daily verse.",
|
|
),
|
|
},
|
|
"/api/verse/{book}/{chapter}/{verse}": {
|
|
"tags": ["Verses"],
|
|
"summary": "Get a single verse",
|
|
"description": "Return one KJV verse with red-letter metadata. Add `?interlinear=true` to include word-level interlinear data when available.",
|
|
"parameters": [
|
|
BOOK_PARAM,
|
|
CHAPTER_PARAM,
|
|
VERSE_PARAM,
|
|
_query_param(
|
|
"interlinear",
|
|
{"type": "boolean", "nullable": True},
|
|
True,
|
|
"Include interlinear word data when available.",
|
|
),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"john_3_16": {
|
|
"summary": "John 3:16",
|
|
"value": {
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world, that he gave his only begotten Son...",
|
|
"red_letter": "full",
|
|
},
|
|
},
|
|
"genesis_1_1": {
|
|
"summary": "Genesis 1:1 by abbreviation",
|
|
"value": {
|
|
"book": "Genesis",
|
|
"chapter": 1,
|
|
"verse": 1,
|
|
"reference": "Genesis 1:1",
|
|
"text": "In the beginning God created the heaven and the earth.",
|
|
"red_letter": None,
|
|
},
|
|
},
|
|
},
|
|
"Verse response.",
|
|
),
|
|
},
|
|
"/api/verse-range/{book}/{chapter}/{start}/{end}": {
|
|
"tags": ["Verses"],
|
|
"summary": "Get a verse range",
|
|
"description": "Return a contiguous range of verses from one chapter.",
|
|
"parameters": [
|
|
_path_param("book", "string", "Psalms", "Canonical book name or abbreviation."),
|
|
_path_param("chapter", "integer", 23, "Chapter number within the book."),
|
|
_path_param("start", "integer", 1, "First verse in the range."),
|
|
_path_param("end", "integer", 6, "Last verse in the range."),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"psalm_23": {
|
|
"summary": "Psalm 23",
|
|
"value": {
|
|
"book": "Psalms",
|
|
"chapter": 23,
|
|
"start": 1,
|
|
"end": 6,
|
|
"reference": "Psalms 23:1-6",
|
|
"verses": [
|
|
{
|
|
"verse": 1,
|
|
"text": "The LORD is my shepherd; I shall not want.",
|
|
"red_letter": None,
|
|
}
|
|
],
|
|
"text": "The LORD is my shepherd; I shall not want...",
|
|
},
|
|
}
|
|
},
|
|
"Verse range response.",
|
|
),
|
|
},
|
|
"/api/interlinear/{book}/{chapter}/{verse}": {
|
|
"tags": ["Verses"],
|
|
"summary": "Get interlinear word data",
|
|
"description": "Return word-by-word interlinear data for a verse when it is available.",
|
|
"parameters": [BOOK_PARAM, CHAPTER_PARAM, VERSE_PARAM],
|
|
**_success_examples(
|
|
{
|
|
"john_3_16": {
|
|
"summary": "Interlinear lookup",
|
|
"value": {
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world...",
|
|
"interlinear_available": True,
|
|
"words": [
|
|
{
|
|
"word": "loved",
|
|
"strongs": "G25",
|
|
"transliteration": "agapao",
|
|
"definition": "to love",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Interlinear response.",
|
|
),
|
|
},
|
|
"/api/books": {
|
|
"tags": ["Books"],
|
|
"summary": "List Bible books",
|
|
"description": "Return all 66 KJV books grouped by testament.",
|
|
**_success_examples(
|
|
{
|
|
"books": {
|
|
"summary": "Book list",
|
|
"value": {
|
|
"total_books": 66,
|
|
"old_testament": [
|
|
{
|
|
"name": "Genesis",
|
|
"chapters": 50,
|
|
"testament": "Old Testament",
|
|
}
|
|
],
|
|
"new_testament": [
|
|
{
|
|
"name": "Matthew",
|
|
"chapters": 28,
|
|
"testament": "New Testament",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Book list.",
|
|
),
|
|
},
|
|
"/api/books/{book}": {
|
|
"tags": ["Books"],
|
|
"summary": "Get book metadata",
|
|
"description": "Return chapter counts and available introduction metadata for a Bible book.",
|
|
"parameters": [BOOK_PARAM],
|
|
**_success_examples(
|
|
{
|
|
"john": {
|
|
"summary": "John",
|
|
"value": {
|
|
"name": "John",
|
|
"total_chapters": 21,
|
|
"chapters": [{"chapter": 1, "verses": 51}],
|
|
"links": {"pdf": "/api/books/John/pdf"},
|
|
},
|
|
}
|
|
},
|
|
"Book metadata.",
|
|
),
|
|
},
|
|
"/api/books/{book}/chapters/{chapter}": {
|
|
"tags": ["Books"],
|
|
"summary": "Get a chapter",
|
|
"description": "Return all verses in one Bible chapter.",
|
|
"parameters": [BOOK_PARAM, CHAPTER_PARAM],
|
|
**_success_examples(
|
|
{
|
|
"john_3": {
|
|
"summary": "John 3",
|
|
"value": {
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"total_verses": 36,
|
|
"verses": [
|
|
{
|
|
"verse": 16,
|
|
"text": "For God so loved the world...",
|
|
}
|
|
],
|
|
"links": {"pdf": "/api/books/John/chapters/3/pdf"},
|
|
},
|
|
}
|
|
},
|
|
"Chapter response.",
|
|
),
|
|
},
|
|
"/api/cross-references/{book}/{chapter}/{verse}": {
|
|
"tags": ["Verses"],
|
|
"summary": "Get cross-references",
|
|
"description": "Return cross-references for a verse when available.",
|
|
"parameters": [BOOK_PARAM, CHAPTER_PARAM, VERSE_PARAM],
|
|
**_success_examples(
|
|
{
|
|
"john_3_16": {
|
|
"summary": "Cross-references",
|
|
"value": {
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"cross_references": ["John 3:15", "Romans 5:8"],
|
|
},
|
|
}
|
|
},
|
|
"Cross-reference response.",
|
|
),
|
|
},
|
|
"/api/topics": {
|
|
"tags": ["Study Resources"],
|
|
"summary": "List topics",
|
|
"description": "Return the topical index.",
|
|
**_success_examples(
|
|
{
|
|
"topics": {
|
|
"summary": "Topic list",
|
|
"value": {
|
|
"total_topics": 36,
|
|
"topics": [
|
|
{
|
|
"name": "faith",
|
|
"slug": "faith",
|
|
"description": "Faith and trust in God",
|
|
"subtopics": ["saving_faith", "living_by_faith"],
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Topic list.",
|
|
),
|
|
},
|
|
"/api/topics/{topic_name}": {
|
|
"tags": ["Study Resources"],
|
|
"summary": "Get topic details",
|
|
"description": "Return overview and subtopics for one topical study.",
|
|
"parameters": [
|
|
_path_param("topic_name", "string", "faith", "Topic slug.")
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"faith": {
|
|
"summary": "Faith topic",
|
|
"value": {
|
|
"name": "faith",
|
|
"description": "Faith and trust in God",
|
|
"overview": "Faith is central to Scripture...",
|
|
"subtopics": {},
|
|
},
|
|
}
|
|
},
|
|
"Topic details.",
|
|
),
|
|
},
|
|
"/api/reading-plans": {
|
|
"tags": ["Study Resources"],
|
|
"summary": "List reading plans",
|
|
"description": "Return available Bible reading plans.",
|
|
**_success_examples(
|
|
{
|
|
"plans": {
|
|
"summary": "Reading plans",
|
|
"value": {
|
|
"total_plans": 12,
|
|
"plans": [
|
|
{
|
|
"id": "chronological",
|
|
"name": "Chronological Bible Reading",
|
|
"description": "Read Scripture in historical order.",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Reading plan list.",
|
|
),
|
|
},
|
|
"/api/resources": {
|
|
"tags": ["Study Resources"],
|
|
"summary": "List resource categories",
|
|
"description": "Return available biblical and theological resource categories.",
|
|
**_success_examples(
|
|
{
|
|
"categories": {
|
|
"summary": "Resource categories",
|
|
"value": {
|
|
"total_categories": 39,
|
|
"categories": [
|
|
{
|
|
"name": "biblical_locations",
|
|
"title": "Biblical Locations",
|
|
"item_count": 15,
|
|
"url": "/api/resources/biblical_locations",
|
|
"html_url": "/biblical-locations",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Resource category list.",
|
|
),
|
|
},
|
|
"/api/resources/{category}": {
|
|
"tags": ["Study Resources"],
|
|
"summary": "List resources in a category",
|
|
"description": "Return all items in one resource category.",
|
|
"parameters": [
|
|
_path_param(
|
|
"category",
|
|
"string",
|
|
"biblical_locations",
|
|
"Resource category key.",
|
|
)
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"biblical_locations": {
|
|
"summary": "Biblical locations",
|
|
"value": {
|
|
"category": "biblical_locations",
|
|
"title": "Biblical Locations",
|
|
"total_items": 15,
|
|
"items": [
|
|
{
|
|
"name": "Garden of Eden",
|
|
"slug": "garden-of-eden",
|
|
"description": "The original home of mankind",
|
|
"verse_count": 2,
|
|
"url": "/api/resources/biblical_locations/garden-of-eden",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Resource category response.",
|
|
),
|
|
},
|
|
"/api/resources/{category}/{slug}": {
|
|
"tags": ["Study Resources"],
|
|
"summary": "Get resource details",
|
|
"description": "Return one resource item with its Scripture references.",
|
|
"parameters": [
|
|
_path_param(
|
|
"category",
|
|
"string",
|
|
"biblical_locations",
|
|
"Resource category key.",
|
|
),
|
|
_path_param("slug", "string", "garden-of-eden", "Resource item slug."),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"garden_of_eden": {
|
|
"summary": "Garden of Eden",
|
|
"value": {
|
|
"name": "Garden of Eden",
|
|
"slug": "garden-of-eden",
|
|
"category": "biblical_locations",
|
|
"description": "The original home of mankind",
|
|
"verses": [
|
|
{
|
|
"reference": "Genesis 2:8",
|
|
"text": "And the LORD God planted a garden eastward in Eden...",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Resource item response.",
|
|
),
|
|
},
|
|
"/api/red-letter": {
|
|
"tags": ["Verses"],
|
|
"summary": "List red-letter verses",
|
|
"description": "Return verses containing words of Christ, optionally filtered by book.",
|
|
"parameters": [
|
|
_query_param(
|
|
"book",
|
|
{"type": "string", "nullable": True},
|
|
"John",
|
|
"Optional canonical book name or abbreviation.",
|
|
),
|
|
_query_param(
|
|
"limit",
|
|
{"type": "integer", "minimum": 1, "maximum": 500, "default": 50},
|
|
25,
|
|
"Maximum number of verses to return.",
|
|
),
|
|
_query_param(
|
|
"offset",
|
|
{"type": "integer", "minimum": 0, "default": 0},
|
|
0,
|
|
"Pagination offset.",
|
|
),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"john": {
|
|
"summary": "Red-letter verses in John",
|
|
"value": {
|
|
"total": 419,
|
|
"verses": [
|
|
{
|
|
"reference": "John 3:16",
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"text": "For God so loved the world...",
|
|
"christ_words": "full",
|
|
"is_full_verse": True,
|
|
}
|
|
],
|
|
"limit": 25,
|
|
"offset": 0,
|
|
},
|
|
}
|
|
},
|
|
"Red-letter verse list.",
|
|
),
|
|
},
|
|
"/api/verse/random": {
|
|
"tags": ["Verses"],
|
|
"summary": "Get a random verse",
|
|
"description": "Return a random KJV verse, optionally filtered by testament or book.",
|
|
"parameters": [
|
|
_query_param(
|
|
"testament",
|
|
{"type": "string", "nullable": True, "enum": ["ot", "nt"]},
|
|
"nt",
|
|
"Optional testament filter.",
|
|
),
|
|
_query_param(
|
|
"book",
|
|
{"type": "string", "nullable": True},
|
|
"Romans",
|
|
"Optional book filter.",
|
|
),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"random_nt": {
|
|
"summary": "Random New Testament verse",
|
|
"value": {
|
|
"book": "Romans",
|
|
"chapter": 8,
|
|
"verse": 28,
|
|
"reference": "Romans 8:28",
|
|
"text": "And we know that all things work together for good...",
|
|
"red_letter": None,
|
|
},
|
|
}
|
|
},
|
|
"Random verse response.",
|
|
),
|
|
},
|
|
"/api/commentary/{book}/{chapter}/{verse}": {
|
|
"tags": ["Commentary"],
|
|
"summary": "Get verse commentary",
|
|
"description": "Return study commentary for a specific verse.",
|
|
"parameters": [BOOK_PARAM, CHAPTER_PARAM, VERSE_PARAM],
|
|
**_success_examples(
|
|
{
|
|
"john_3_16": {
|
|
"summary": "John 3:16 commentary",
|
|
"value": {
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world...",
|
|
"analysis": "<strong>For God so loved...</strong>",
|
|
"historical": "Jesus speaks to Nicodemus...",
|
|
"questions": ["What does this reveal about God's love?"],
|
|
},
|
|
}
|
|
},
|
|
"Verse commentary response.",
|
|
),
|
|
},
|
|
"/api/verses/bulk": {
|
|
"tags": ["Verses"],
|
|
"summary": "Look up verses in bulk",
|
|
"description": "Resolve multiple verse references in one request. Invalid references are skipped.",
|
|
"requestBody": {
|
|
"required": True,
|
|
"content": {
|
|
JSON: {
|
|
"schema": {"$ref": "#/components/schemas/BulkVerseRequest"},
|
|
"examples": {
|
|
"common_verses": {
|
|
"summary": "Three common verses",
|
|
"value": {
|
|
"references": [
|
|
"John 3:16",
|
|
"Romans 8:28",
|
|
"Psalm 23:1",
|
|
]
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
},
|
|
"responses": {
|
|
"200": {
|
|
"description": "Bulk verse response.",
|
|
"content": {
|
|
JSON: {
|
|
"schema": {"$ref": "#/components/schemas/BulkVerseResponse"},
|
|
"examples": {
|
|
"common_verses": {
|
|
"summary": "Bulk lookup response",
|
|
"value": {
|
|
"total": 3,
|
|
"verses": [
|
|
{
|
|
"book": "John",
|
|
"chapter": 3,
|
|
"verse": 16,
|
|
"reference": "John 3:16",
|
|
"text": "For God so loved the world...",
|
|
"red_letter": "full",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
},
|
|
"/api/family-tree/{name}": {
|
|
"tags": ["Family Tree"],
|
|
"summary": "Get biblical biography",
|
|
"description": "Return a short biography and key events for a biblical figure.",
|
|
"parameters": [
|
|
_path_param("name", "string", "Abraham", "Biblical figure name or alias.")
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"abraham": {
|
|
"summary": "Abraham",
|
|
"value": {
|
|
"name": "Abraham",
|
|
"summary": "Originally named Abram...",
|
|
"significance": "Abraham is the father of the Hebrew nation...",
|
|
"key_events": [
|
|
{
|
|
"age": 100,
|
|
"event": "Birth of Isaac",
|
|
"verse": "Genesis 21:5",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Biography response.",
|
|
),
|
|
},
|
|
"/api/strongs": {
|
|
"tags": ["Strong's"],
|
|
"summary": "Search Strong's concordance",
|
|
"description": "Search Strong's entries by definition or KJV usage.",
|
|
"parameters": [
|
|
_query_param("q", {"type": "string", "nullable": True}, "love", "Search text."),
|
|
_query_param(
|
|
"language",
|
|
{"type": "string", "default": "both", "enum": ["both", "hebrew", "greek"]},
|
|
"greek",
|
|
"Language filter.",
|
|
),
|
|
_query_param(
|
|
"limit",
|
|
{"type": "integer", "default": 50},
|
|
10,
|
|
"Maximum number of results.",
|
|
),
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"love": {
|
|
"summary": "Search for love",
|
|
"value": {
|
|
"query": "love",
|
|
"language": "greek",
|
|
"total": 1,
|
|
"results": [
|
|
{
|
|
"strongs": "G25",
|
|
"word": "agapao",
|
|
"definition": "to love",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
},
|
|
"Strong's search results.",
|
|
),
|
|
},
|
|
"/api/strongs/{strongs_number}": {
|
|
"tags": ["Strong's"],
|
|
"summary": "Get Strong's entry",
|
|
"description": "Return a Hebrew or Greek Strong's concordance entry.",
|
|
"parameters": [
|
|
_path_param(
|
|
"strongs_number",
|
|
"string",
|
|
"G25",
|
|
"Strong's number, such as H7225 or G25.",
|
|
)
|
|
],
|
|
**_success_examples(
|
|
{
|
|
"g25": {
|
|
"summary": "G25",
|
|
"value": {
|
|
"strongs": "G25",
|
|
"word": "agapao",
|
|
"language": "greek",
|
|
"definition": "to love",
|
|
},
|
|
}
|
|
},
|
|
"Strong's entry.",
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
def install_openapi_examples(api) -> None:
|
|
"""Attach hand-curated examples to generated OpenAPI operations."""
|
|
if not hasattr(api, "openapi"):
|
|
return
|
|
|
|
api.openapi.add_schema("BulkVerseRequest", BulkVerseRequest, check_existing=False)
|
|
api.openapi.add_schema("BulkVerseResponse", BulkVerseResponse, check_existing=False)
|
|
|
|
for route in api.router.routes:
|
|
path = str(getattr(route, "path_template", getattr(route, "route", "")) or "")
|
|
meta = OPENAPI_OPERATION_EXAMPLES.get(path)
|
|
if not meta:
|
|
continue
|
|
endpoint = getattr(route, "endpoint", None)
|
|
if endpoint is None:
|
|
continue
|
|
existing = getattr(endpoint, "_openapi_meta", {})
|
|
endpoint._openapi_meta = _deep_merge(existing, meta)
|
|
|
|
_install_parameter_example_hook(api)
|
|
api.openapi._spec_cache = None
|
|
|
|
|
|
def _install_parameter_example_hook(api) -> None:
|
|
"""Merge parameter examples after Responder generates operation params."""
|
|
if getattr(api.openapi, "_kjvstudy_examples_hooked", False):
|
|
return
|
|
|
|
original_build = api.openapi._build_apispec
|
|
|
|
def build_with_parameter_examples():
|
|
spec = original_build()
|
|
_apply_parameter_examples(spec._paths)
|
|
return spec
|
|
|
|
api.openapi._build_apispec = build_with_parameter_examples
|
|
api.openapi._kjvstudy_examples_hooked = True
|
|
|
|
|
|
def _apply_parameter_examples(paths: dict[str, Any]) -> None:
|
|
"""Apply documented parameter descriptions/examples to generated paths."""
|
|
for path, meta in OPENAPI_OPERATION_EXAMPLES.items():
|
|
documented_params = meta.get("parameters")
|
|
if not documented_params or path not in paths:
|
|
continue
|
|
|
|
for operation in paths[path].values():
|
|
if not isinstance(operation, dict):
|
|
continue
|
|
|
|
existing_params = list(operation.get("parameters", []))
|
|
merged_params: list[dict[str, Any]] = []
|
|
seen = set()
|
|
|
|
for parameter in existing_params:
|
|
key = (parameter.get("in"), parameter.get("name"))
|
|
documented = _find_parameter(documented_params, *key)
|
|
if documented:
|
|
parameter = _deep_merge(parameter, documented)
|
|
merged_params.append(parameter)
|
|
seen.add(key)
|
|
|
|
for parameter in documented_params:
|
|
key = (parameter.get("in"), parameter.get("name"))
|
|
if key not in seen:
|
|
merged_params.append(deepcopy(parameter))
|
|
|
|
operation["parameters"] = merged_params
|
|
|
|
|
|
def _find_parameter(
|
|
parameters: list[dict[str, Any]],
|
|
location: str | None,
|
|
name: str | None,
|
|
) -> dict[str, Any] | None:
|
|
for parameter in parameters:
|
|
if parameter.get("in") == location and parameter.get("name") == name:
|
|
return parameter
|
|
return None
|