From dce390f0359300a269778767f86412d1ccdf097f Mon Sep 17 00:00:00 2001 From: Jeremy Dyer Date: Thu, 20 Feb 2025 12:04:30 -0500 Subject: [PATCH] Add support for including openapi docs in the documentation output --- .github/workflows/build-docs.yml | 5 ++ docs/requirements.txt | 1 + docs/sphinx_docs/source/conf.py | 1 + docs/sphinx_docs/source/user_guide/index.rst | 1 + .../source/user_guide/openapi_docs/index.rst | 5 ++ .../user_guide/openapi_docs/openapi.json | 19 +++++++ scripts/generate_openapi_docs.py | 29 +++++++++++ src/nv_ingest/api/v1/ingest.py | 50 +++++++++++++++++-- 8 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 docs/sphinx_docs/source/user_guide/openapi_docs/index.rst create mode 100644 docs/sphinx_docs/source/user_guide/openapi_docs/openapi.json create mode 100644 scripts/generate_openapi_docs.py diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 119ebb90..f87e0d11 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -43,6 +43,11 @@ jobs: . venv/bin/activate pip install -r docs/requirements.txt + - name: Generate OpenAPI JSON + run: | + . venv/bin/activate + PYTHONPATH=$(pwd)/src:$(pwd)/client/src python ./scripts/generate_openapi.py + - name: Build Sphinx API Docs run: | . venv/bin/activate diff --git a/docs/requirements.txt b/docs/requirements.txt index 30460298..1f543c48 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -11,3 +11,4 @@ mkdocs-site-urls sphinx sphinx-markdown-builder sphinx-rtd-theme +sphinxcontrib-openapi diff --git a/docs/sphinx_docs/source/conf.py b/docs/sphinx_docs/source/conf.py index adcbdc25..c4ac1987 100644 --- a/docs/sphinx_docs/source/conf.py +++ b/docs/sphinx_docs/source/conf.py @@ -20,6 +20,7 @@ "sphinx.ext.autosummary", "sphinx.ext.napoleon", "sphinx.ext.viewcode", + "sphinxcontrib.openapi", ] templates_path = ["_templates"] diff --git a/docs/sphinx_docs/source/user_guide/index.rst b/docs/sphinx_docs/source/user_guide/index.rst index b7f1a832..b52643b5 100644 --- a/docs/sphinx_docs/source/user_guide/index.rst +++ b/docs/sphinx_docs/source/user_guide/index.rst @@ -4,3 +4,4 @@ :caption: nv-ingest User Guide api_docs/index + openapi_docs/index diff --git a/docs/sphinx_docs/source/user_guide/openapi_docs/index.rst b/docs/sphinx_docs/source/user_guide/openapi_docs/index.rst new file mode 100644 index 00000000..9f6e26f7 --- /dev/null +++ b/docs/sphinx_docs/source/user_guide/openapi_docs/index.rst @@ -0,0 +1,5 @@ +API Reference +============= + +.. openapi:: openapi.json + :encoding: utf-8 diff --git a/docs/sphinx_docs/source/user_guide/openapi_docs/openapi.json b/docs/sphinx_docs/source/user_guide/openapi_docs/openapi.json new file mode 100644 index 00000000..eeecd0d3 --- /dev/null +++ b/docs/sphinx_docs/source/user_guide/openapi_docs/openapi.json @@ -0,0 +1,19 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "NV-Ingest Microservice", + "description": "Service for ingesting heterogenous datatypes", + "contact": { + "name": "NVIDIA Corporation", + "url": "https://nvidia.com/" + }, + "version": "0.1.0" + }, + "paths": {}, + "tags": [ + { + "name": "Health", + "description": "Health checks" + } + ] +} diff --git a/scripts/generate_openapi_docs.py b/scripts/generate_openapi_docs.py new file mode 100644 index 00000000..bbb62e4a --- /dev/null +++ b/scripts/generate_openapi_docs.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. +# All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# syntax=docker/dockerfile:1.3 + +# The easiest way to run this script without having to install nv-ingest is adjust +# your PYTHONPATH to include the NV_INGEST_REPO_ROOT/src directory like so ... +# This script is intended to only be ran from NV_INGEST_REPO_ROOT +# PYTHONPATH=$(pwd)/src:$(pwd)/client/src scripts/generated_openapi_docs.py + +import json +import os +from nv_ingest.main import app + +# Define output directory and file +OUTPUT_DIR = "./docs/sphinx_docs/source/user_guide/openapi_docs" +OUTPUT_FILE = os.path.join(OUTPUT_DIR, "openapi.json") + +# Ensure the output directory exists +os.makedirs(OUTPUT_DIR, exist_ok=True) + +# Generate OpenAPI schema +openapi_schema = app.openapi() + +# Save OpenAPI schema to JSON file +with open(OUTPUT_FILE, "w") as f: + json.dump(openapi_schema, f, indent=2) + +print(f"✅ OpenAPI schema saved to {OUTPUT_FILE}") diff --git a/src/nv_ingest/api/v1/ingest.py b/src/nv_ingest/api/v1/ingest.py index 696a0482..aef8bd80 100644 --- a/src/nv_ingest/api/v1/ingest.py +++ b/src/nv_ingest/api/v1/ingest.py @@ -13,10 +13,8 @@ import traceback import uuid -from fastapi import APIRouter, Request, Response -from fastapi import Depends +from fastapi import APIRouter, Depends, HTTPException, Request, Response, status from fastapi import File, UploadFile, Form -from fastapi import HTTPException from fastapi.responses import JSONResponse from nv_ingest_client.primitives.jobs.job_spec import JobSpec from nv_ingest_client.primitives.tasks.extract import ExtractTask @@ -48,6 +46,52 @@ async def _get_ingest_service() -> IngestServiceMeta: INGEST_SERVICE_T = Annotated[IngestServiceMeta, Depends(_get_ingest_service)] +# Things needed for this rework .... +# 1. Support for a CURL file upload +# 2. Full support for all operations that are supported by the client codebase +# 3. Flag for format that should be returned to the calling client. +# 4. Accept an actual Pydantic model as the parameter. This will ensure docs are more clear as well + + +@router.post( + "/ingest_docs", + deprecated=False, + responses={ + 200: {"description": "Submission was successful"}, + 500: {"description": "Error encountered during submission"}, + }, + tags=["Ingestion"], + summary="submit document to the core nv ingestion service for processing", + description=""" + ## Create a New Item + This endpoint allows you to create an item. + - **Request Body:** JSON containing `name` and `price` + - **Response:** Returns the created item with an `id` + - **Example Request:** + ```json + { + "name": "Laptop", + "price": 999.99 + } + ``` + - **Example Response:** + ```json + { + "id": 1, + "name": "Laptop", + "price": 999.99 + } + ``` + """, + operation_id="ingest_docs", + status_code=status.HTTP_200_OK, +) +async def ingest_docs_base64_payload( + ingest_service: INGEST_SERVICE_T, +): + print("welcome") + return "welcome" + # POST /submit @router.post(