From 29f717cbcc45bb7a10f4f2283b1862e45d61e6b2 Mon Sep 17 00:00:00 2001 From: Kumaran Rajendhiran Date: Fri, 6 Sep 2024 15:17:52 +0530 Subject: [PATCH] Update version (#124) * Update version to 0.0.0b1 * Update security schemas and security handling --- .secrets.baseline | 12 +- docs/docs/SUMMARY.md | 1 + .../fastagency/openapi/security/HTTPBearer.md | 11 + examples/openapi/giphy_openapi.json | 1147 +++++++++++++++++ fastagency/__about__.py | 2 +- fastagency/openapi/security.py | 38 +- fastagency/openapi/security_schema_visitor.py | 10 +- templates/main.jinja2 | 2 + 8 files changed, 1216 insertions(+), 7 deletions(-) create mode 100644 docs/docs/en/api/fastagency/openapi/security/HTTPBearer.md create mode 100644 examples/openapi/giphy_openapi.json diff --git a/.secrets.baseline b/.secrets.baseline index 614fe00e4..b942520a3 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -136,7 +136,17 @@ "line_number": 914, "is_secret": false } + ], + "examples/openapi/giphy_openapi.json": [ + { + "type": "Hex High Entropy String", + "filename": "examples/openapi/giphy_openapi.json", + "hashed_secret": "e3ddfbda4fafddc6ce61a11ef9e9c0149bbfcc48", + "is_verified": false, + "line_number": 1066, + "is_secret": false + } ] }, - "generated_at": "2024-09-05T10:06:48Z" + "generated_at": "2024-09-06T09:35:21Z" } diff --git a/docs/docs/SUMMARY.md b/docs/docs/SUMMARY.md index 946d7a98a..f00d22fe1 100644 --- a/docs/docs/SUMMARY.md +++ b/docs/docs/SUMMARY.md @@ -108,6 +108,7 @@ search: - [APIKeyQuery](api/fastagency/openapi/security/APIKeyQuery.md) - [BaseSecurity](api/fastagency/openapi/security/BaseSecurity.md) - [BaseSecurityParameters](api/fastagency/openapi/security/BaseSecurityParameters.md) + - [HTTPBearer](api/fastagency/openapi/security/HTTPBearer.md) - security_schema_visitor - [custom_visitor](api/fastagency/openapi/security_schema_visitor/custom_visitor.md) - studio diff --git a/docs/docs/en/api/fastagency/openapi/security/HTTPBearer.md b/docs/docs/en/api/fastagency/openapi/security/HTTPBearer.md new file mode 100644 index 000000000..3b2a1f221 --- /dev/null +++ b/docs/docs/en/api/fastagency/openapi/security/HTTPBearer.md @@ -0,0 +1,11 @@ +--- +# 0.5 - API +# 2 - Release +# 3 - Contributing +# 5 - Template Page +# 10 - Default +search: + boost: 0.5 +--- + +::: fastagency.openapi.security.HTTPBearer diff --git a/examples/openapi/giphy_openapi.json b/examples/openapi/giphy_openapi.json new file mode 100644 index 000000000..c364874cf --- /dev/null +++ b/examples/openapi/giphy_openapi.json @@ -0,0 +1,1147 @@ +{ + "openapi": "3.0.0", + "servers": [ + { + "url": "https://api.giphy.com/v1" + } + ], + "info": { + "contact": { + "email": "support@giphy.com" + }, + "description": "Giphy API", + "termsOfService": "https://developers.giphy.com/", + "title": "Giphy API", + "version": "1.0", + "x-apisguru-categories": [ + "media" + ], + "x-logo": { + "url": "https://api.apis.guru/v2/cache/logo/https_twitter.com_GIPHY_profile_image.png" + }, + "x-origin": [ + { + "format": "openapi", + "url": "https://raw.githubusercontent.com/faragorn/open-api-specs/master/specs/giphy_api/1.0/index.yml", + "version": "3.0" + } + ], + "x-providerName": "giphy.com" + }, + "externalDocs": { + "description": "Official Giphy Documentation", + "url": "https://developers.giphy.com/docs/" + }, + "security": [ + { + "api_key": [] + } + ], + "tags": [ + { + "name": "gifs" + }, + { + "name": "stickers" + } + ], + "paths": { + "/gifs": { + "get": { + "description": "A multiget version of the get GIF by ID endpoint.", + "operationId": "getGifsById", + "parameters": [ + { + "$ref": "#/components/parameters/gifIds" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Gif" + }, + "type": "array" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + }, + "pagination": { + "$ref": "#/components/schemas/Pagination" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Get GIFs by ID", + "tags": [ + "gifs" + ] + } + }, + "/gifs/random": { + "get": { + "description": "Returns a random GIF, limited by tag. Excluding the tag parameter will return a random GIF from the GIPHY catalog.", + "operationId": "randomGif", + "parameters": [ + { + "$ref": "#/components/parameters/tag" + }, + { + "$ref": "#/components/parameters/rating" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Gif" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Random GIF", + "tags": [ + "gifs" + ] + } + }, + "/gifs/search": { + "get": { + "description": "Search all GIPHY GIFs for a word or phrase. Punctuation will be stripped and ignored. Use a plus or url encode for phrases. Example paul+rudd, ryan+gosling or american+psycho.", + "operationId": "searchGifs", + "parameters": [ + { + "$ref": "#/components/parameters/query" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/rating" + }, + { + "$ref": "#/components/parameters/lang" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Gif" + }, + "type": "array" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + }, + "pagination": { + "$ref": "#/components/schemas/Pagination" + } + }, + "type": "object" + } + } + }, + "description": "Search results" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Search GIFs", + "tags": [ + "gifs" + ] + } + }, + "/gifs/translate": { + "get": { + "description": "The translate API draws on search, but uses the GIPHY `special sauce` to handle translating from one vocabulary to another. In this case, words and phrases to GIF", + "operationId": "translateGif", + "parameters": [ + { + "$ref": "#/components/parameters/term" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Gif" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Translate phrase to GIF", + "tags": [ + "gifs" + ] + } + }, + "/gifs/trending": { + "get": { + "description": "Fetch GIFs currently trending online. Hand curated by the GIPHY editorial team. The data returned mirrors the GIFs showcased on the GIPHY homepage. Returns 25 results by default.", + "operationId": "trendingGifs", + "parameters": [ + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/rating" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Gif" + }, + "type": "array" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + }, + "pagination": { + "$ref": "#/components/schemas/Pagination" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Trending GIFs", + "tags": [ + "gifs" + ] + } + }, + "/gifs/{gifId}": { + "get": { + "description": "Returns a GIF given that GIF's unique ID", + "operationId": "getGifById", + "parameters": [ + { + "$ref": "#/components/parameters/gifId" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Gif" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Get GIF by Id", + "tags": [ + "gifs" + ] + } + }, + "/stickers/random": { + "get": { + "description": "Returns a random GIF, limited by tag. Excluding the tag parameter will return a random GIF from the GIPHY catalog.", + "operationId": "randomSticker", + "parameters": [ + { + "$ref": "#/components/parameters/tag" + }, + { + "$ref": "#/components/parameters/rating" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Gif" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Random Sticker", + "tags": [ + "stickers" + ] + } + }, + "/stickers/search": { + "get": { + "description": "Replicates the functionality and requirements of the classic GIPHY search, but returns animated stickers rather than GIFs.", + "operationId": "searchStickers", + "parameters": [ + { + "$ref": "#/components/parameters/query" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/rating" + }, + { + "$ref": "#/components/parameters/lang" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Gif" + }, + "type": "array" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + }, + "pagination": { + "$ref": "#/components/schemas/Pagination" + } + }, + "type": "object" + } + } + }, + "description": "Search results" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Search Stickers", + "tags": [ + "stickers" + ] + } + }, + "/stickers/translate": { + "get": { + "description": "The translate API draws on search, but uses the GIPHY `special sauce` to handle translating from one vocabulary to another. In this case, words and phrases to GIFs.", + "operationId": "translateSticker", + "parameters": [ + { + "$ref": "#/components/parameters/term" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "$ref": "#/components/schemas/Gif" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Translate phrase to Sticker", + "tags": [ + "stickers" + ] + } + }, + "/stickers/trending": { + "get": { + "description": "Fetch Stickers currently trending online. Hand curated by the GIPHY editorial team. Returns 25 results by default.", + "operationId": "trendingStickers", + "parameters": [ + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/rating" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "items": { + "$ref": "#/components/schemas/Gif" + }, + "type": "array" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + }, + "pagination": { + "$ref": "#/components/schemas/Pagination" + } + }, + "type": "object" + } + } + }, + "description": "" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + } + }, + "summary": "Trending Stickers", + "tags": [ + "stickers" + ] + } + } + }, + "components": { + "parameters": { + "gifId": { + "description": "Filters results by specified GIF ID.", + "in": "path", + "name": "gifId", + "required": true, + "schema": { + "format": "int32", + "type": "integer" + } + }, + "gifIds": { + "description": "Filters results by specified GIF IDs, separated by commas.", + "in": "query", + "name": "ids", + "schema": { + "type": "string" + } + }, + "lang": { + "description": "Specify default language for regional content; use a 2-letter ISO 639-1 language code.", + "in": "query", + "name": "lang", + "schema": { + "type": "string" + } + }, + "limit": { + "description": "The maximum number of records to return.", + "in": "query", + "name": "limit", + "schema": { + "default": 25, + "format": "int32", + "type": "integer" + } + }, + "offset": { + "description": "An optional results offset.", + "in": "query", + "name": "offset", + "schema": { + "default": 0, + "format": "int32", + "type": "integer" + } + }, + "query": { + "description": "Search query term or prhase.", + "in": "query", + "name": "q", + "required": true, + "schema": { + "type": "string" + } + }, + "rating": { + "description": "Filters results by specified rating.", + "in": "query", + "name": "rating", + "schema": { + "type": "string" + } + }, + "tag": { + "description": "Filters results by specified tag.", + "in": "query", + "name": "tag", + "schema": { + "type": "string" + } + }, + "term": { + "description": "Search term.", + "in": "query", + "name": "s", + "required": true, + "schema": { + "type": "string" + } + } + }, + "responses": { + "BadRequest": { + "description": "Your request was formatted incorrectly or missing required parameters." + }, + "Forbidden": { + "description": "You weren't authorized to make your request; most likely this indicates an issue with your API Key." + }, + "NotFound": { + "description": "The particular GIF you are requesting was not found. This occurs, for example, if you request a GIF by an id that does not exist." + }, + "TooManyRequests": { + "description": "Your API Key is making too many requests. Read about [requesting a Production Key](https://developers.giphy.com/docs/#access) to upgrade your API Key rate limits." + } + }, + "schemas": { + "Gif": { + "properties": { + "bitly_url": { + "description": "The unique bit.ly URL for this GIF", + "example": "http://gph.is/1gsWDcL", + "type": "string" + }, + "content_url": { + "description": "Currently unused", + "type": "string" + }, + "create_datetime": { + "description": "The date this GIF was added to the GIPHY database.", + "example": "2013-08-01 12:41:48", + "format": "date-time", + "type": "string" + }, + "embded_url": { + "description": "A URL used for embedding this GIF", + "example": "http://giphy.com/embed/YsTs5ltWtEhnq", + "type": "string" + }, + "featured_tags": { + "description": "An array of featured tags for this GIF (Note: Not available when using the Public Beta Key)", + "items": { + "description": "Tag name", + "type": "string" + }, + "type": "array" + }, + "id": { + "description": "This GIF's unique ID", + "example": "YsTs5ltWtEhnq", + "type": "string" + }, + "images": { + "description": "An object containing data for various available formats and sizes of this GIF.", + "properties": { + "downsized": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF downsized to be under 2mb." + } + ] + }, + "downsized_large": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF downsized to be under 8mb." + } + ] + }, + "downsized_medium": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF downsized to be under 5mb." + } + ] + }, + "downsized_small": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF downsized to be under 200kb." + } + ] + }, + "downsized_still": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a static preview image of the downsized version of this GIF." + } + ] + }, + "fixed_height": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding versions of this GIF with a fixed height of 200 pixels. Good for mobile use." + } + ] + }, + "fixed_height_downsampled": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding versions of this GIF with a fixed height of 200 pixels and the number of frames reduced to 6." + } + ] + }, + "fixed_height_small": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding versions of this GIF with a fixed height of 100 pixels. Good for mobile keyboards." + } + ] + }, + "fixed_height_small_still": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a static image of this GIF with a fixed height of 100 pixels." + } + ] + }, + "fixed_height_still": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a static image of this GIF with a fixed height of 200 pixels." + } + ] + }, + "fixed_width": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding versions of this GIF with a fixed width of 200 pixels. Good for mobile use." + } + ] + }, + "fixed_width_downsampled": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding versions of this GIF with a fixed width of 200 pixels and the number of frames reduced to 6." + } + ] + }, + "fixed_width_small": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding versions of this GIF with a fixed width of 100 pixels. Good for mobile keyboards." + } + ] + }, + "fixed_width_small_still": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a static image of this GIF with a fixed width of 100 pixels." + } + ] + }, + "fixed_width_still": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a static image of this GIF with a fixed width of 200 pixels." + } + ] + }, + "looping": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF set to loop for 15 seconds." + } + ] + }, + "original": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding the original version of this GIF. Good for desktop use." + } + ] + }, + "original_still": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a static preview image of the original GIF." + } + ] + }, + "preview": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF in .MP4 format limited to 50kb that displays the first 1-2 seconds of the GIF." + } + ] + }, + "preview_gif": { + "allOf": [ + { + "$ref": "#/components/schemas/Image" + }, + { + "description": "Data surrounding a version of this GIF limited to 50kb that displays the first 1-2 seconds of the GIF." + } + ] + } + }, + "type": "object" + }, + "import_datetime": { + "description": "The creation or upload date from this GIF's source.", + "example": "2013-08-01 12:41:48", + "format": "date-time", + "type": "string" + }, + "rating": { + "description": "The MPAA-style rating for this content. Examples include Y, G, PG, PG-13 and R", + "example": "g", + "type": "string" + }, + "slug": { + "description": "The unique slug used in this GIF's URL", + "example": "confused-flying-YsTs5ltWtEhnq", + "type": "string" + }, + "source": { + "description": "The page on which this GIF was found", + "example": "http://www.reddit.com/r/reactiongifs/comments/1xpyaa/superman_goes_to_hollywood/", + "type": "string" + }, + "source_post_url": { + "description": "The URL of the webpage on which this GIF was found.", + "example": "http://cheezburger.com/5282328320", + "type": "string" + }, + "source_tld": { + "description": "The top level domain of the source URL.", + "example": "cheezburger.com", + "type": "string" + }, + "tags": { + "description": "An array of tags for this GIF (Note: Not available when using the Public Beta Key)", + "items": { + "description": "Tag name", + "type": "string" + }, + "type": "array" + }, + "trending_datetime": { + "description": "The date on which this gif was marked trending, if applicable.", + "example": "2013-08-01 12:41:48", + "format": "date-time", + "type": "string" + }, + "type": { + "default": "gif", + "description": "Type of the gif. By default, this is almost always gif", + "enum": [ + "gif" + ], + "type": "string" + }, + "update_datetime": { + "description": "The date on which this GIF was last updated.", + "example": "2013-08-01 12:41:48", + "format": "date-time", + "type": "string" + }, + "url": { + "description": "The unique URL for this GIF", + "example": "http://giphy.com/gifs/confused-flying-YsTs5ltWtEhnq", + "type": "string" + }, + "user": { + "$ref": "#/components/schemas/User" + }, + "username": { + "description": "The username this GIF is attached to, if applicable", + "example": "JoeCool4000", + "type": "string" + } + }, + "type": "object" + }, + "Image": { + "properties": { + "frames": { + "description": "The number of frames in this GIF.", + "example": "15", + "type": "string" + }, + "height": { + "description": "The height of this GIF in pixels.", + "example": "200", + "type": "string" + }, + "mp4": { + "description": "The URL for this GIF in .MP4 format.", + "example": "https://media1.giphy.com/media/cZ7rmKfFYOvYI/giphy.mp4", + "type": "string" + }, + "mp4_size": { + "description": "The size in bytes of the .MP4 file corresponding to this GIF.", + "example": "25123", + "type": "string" + }, + "size": { + "description": "The size of this GIF in bytes.", + "example": "32381", + "type": "string" + }, + "url": { + "description": "The publicly-accessible direct URL for this GIF.", + "example": "https://media1.giphy.com/media/cZ7rmKfFYOvYI/200.gif", + "type": "string" + }, + "webp": { + "description": "The URL for this GIF in .webp format.", + "example": "https://media1.giphy.com/media/cZ7rmKfFYOvYI/giphy.webp", + "type": "string" + }, + "webp_size": { + "description": "The size in bytes of the .webp file corresponding to this GIF.", + "example": "12321", + "type": "string" + }, + "width": { + "description": "The width of this GIF in pixels.", + "example": "320", + "type": "string" + } + }, + "type": "object" + }, + "Meta": { + "description": "The Meta Object contains basic information regarding the request, whether it was successful, and the response given by the API. Check `responses` to see a description of types of response codes the API might give you under different cirumstances.", + "properties": { + "msg": { + "description": "HTTP Response Message", + "example": "OK", + "type": "string" + }, + "response_id": { + "description": "A unique ID paired with this response from the API.", + "example": "57eea03c72381f86e05c35d2", + "type": "string" + }, + "status": { + "description": "HTTP Response Code", + "example": 200, + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "Pagination": { + "description": "The Pagination Object contains information relating to the number of total results available as well as the number of results fetched and their relative positions.", + "properties": { + "count": { + "description": "Total number of items returned.", + "example": 25, + "format": "int32", + "type": "integer" + }, + "offset": { + "description": "Position in pagination.", + "example": 75, + "format": "int32", + "type": "integer" + }, + "total_count": { + "description": "Total number of items available.", + "example": 250, + "format": "int32", + "type": "integer" + } + }, + "type": "object" + }, + "User": { + "description": "The User Object contains information about the user associated with a GIF and URLs to assets such as that user's avatar image, profile, and more.", + "properties": { + "avatar_url": { + "description": "The URL for this user's avatar image.", + "example": "https://media1.giphy.com/avatars/election2016/XwYrZi5H87o6.gif", + "type": "string" + }, + "banner_url": { + "description": "The URL for the banner image that appears atop this user's profile page.", + "example": "https://media4.giphy.com/avatars/cheezburger/XkuejOhoGLE6.jpg", + "type": "string" + }, + "display_name": { + "description": "The display name associated with this user (contains formatting the base username might not).", + "example": "JoeCool4000", + "type": "string" + }, + "profile_url": { + "description": "The URL for this user's profile.", + "example": "https://giphy.com/cheezburger/", + "type": "string" + }, + "twitter": { + "description": "The Twitter username associated with this user, if applicable.", + "example": "@joecool4000", + "type": "string" + }, + "username": { + "description": "The username associated with this user.", + "example": "joecool4000", + "type": "string" + } + }, + "type": "object" + } + }, + "securitySchemes": { + "api_key": { + "in": "query", + "name": "api_key", + "type": "apiKey" + } + } + } +} diff --git a/fastagency/__about__.py b/fastagency/__about__.py index 1d50cdd45..d113c9c3f 100644 --- a/fastagency/__about__.py +++ b/fastagency/__about__.py @@ -1,3 +1,3 @@ """The fastest way to bring multi-agent workflows to production.""" -__version__ = "0.0.0b0" +__version__ = "0.0.0b1" diff --git a/fastagency/openapi/security.py b/fastagency/openapi/security.py index 0ef9ff321..601ff3d26 100644 --- a/fastagency/openapi/security.py +++ b/fastagency/openapi/security.py @@ -1,7 +1,12 @@ -from typing import Any, ClassVar, Literal, Protocol +import logging +from typing import Any, ClassVar, Literal, Optional, Protocol from pydantic import BaseModel, model_validator +# Get the logger +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + class BaseSecurity(BaseModel): """Base class for security classes.""" @@ -35,16 +40,17 @@ def is_supported(cls, type: str, in_value: str) -> bool: return type == cls.type and in_value == cls.in_value @classmethod - def get_security_class(cls, type: str, in_value: str) -> str: + def get_security_class(cls, type: str, in_value: str) -> Optional[str]: sub_classes = cls.__subclasses__() for sub_class in sub_classes: if sub_class.is_supported(type, in_value): return sub_class.__name__ else: - raise NotImplementedError( + logger.error( f"Unsupported type '{type}' and in_value '{in_value}' combination" ) + return None class BaseSecurityParameters(Protocol): @@ -139,3 +145,29 @@ def apply( def get_security_class(self) -> type[BaseSecurity]: return APIKeyCookie + + +class HTTPBearer(BaseSecurity): + """HTTP Bearer security class.""" + + type: ClassVar[Literal["http"]] = "http" + in_value: ClassVar[Literal["bearer"]] = "bearer" + + class Parameters(BaseModel): # BaseSecurityParameters + """HTTP Bearer security parameters class.""" + + value: str + + def apply( + self, + q_params: dict[str, Any], + body_dict: dict[str, Any], + security: BaseSecurity, + ) -> None: + if "headers" not in body_dict: + body_dict["headers"] = {} + + body_dict["headers"]["Authorization"] = f"Bearer {self.value}" + + def get_security_class(self) -> type[BaseSecurity]: + return HTTPBearer diff --git a/fastagency/openapi/security_schema_visitor.py b/fastagency/openapi/security_schema_visitor.py index dac246119..7a5d76a59 100644 --- a/fastagency/openapi/security_schema_visitor.py +++ b/fastagency/openapi/security_schema_visitor.py @@ -17,11 +17,17 @@ def custom_visitor(parser: OpenAPIParser, model_path: Path) -> dict[str, object] security_classes = [] security_parameters = {} for k, v in security_schemes.items(): + if "in" not in v: + in_value = v["scheme"] if "scheme" in v and v["type"] == "http" else None + else: + in_value = v["in"] security_class = BaseSecurity.get_security_class( - type=v["type"], in_value=v["in"] + type=v["type"], in_value=in_value ) + if security_class is None: + continue security_classes.append(security_class) - name = v["name"] + name = v.get("name", None) security_parameters[k] = f'{security_class}(name="{name}")' return { diff --git a/templates/main.jinja2 b/templates/main.jinja2 index 6b22902e2..26d82c51a 100644 --- a/templates/main.jinja2 +++ b/templates/main.jinja2 @@ -10,7 +10,9 @@ app = Client( {% if info %} {% for key,value in info.items() %} {% set info_value= value.__repr__() %} + {% if not key.startswith('x-') %} {{ key }} = {{info_value}}, + {% endif %} {% endfor %} {% endif %} )