├── .gitignore ├── LICENSE ├── README.md └── template └── python3-fastapi ├── Dockerfile ├── function ├── __init__.py ├── handler.py └── requirements.txt ├── index.py ├── requirements.txt └── template.yml /.gitignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alex Ellis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenFaas Python3 FastAPI Template 2 | 3 | ## NOT MAINTAINED 4 | 5 | ***This project is not actively maintained. If you are considering using it please take a look at https://github.com/JustinGuese/openfaas-python3-fastapi-template as a possible alternative.*** 6 | 7 | ## Overview 8 | 9 | A Python [FastAPI](https://github.com/tiangolo/fastapi) template that make use of the incubator project [of-watchdog](https://github.com/openfaas-incubator/of-watchdog). 10 | 11 | Templates available in this repository: 12 | - python3-fastapi 13 | 14 | **With a function created from this template:** 15 | 16 | - An HTTP POST will execute the function (as usual) 17 | - An HTTP GET will render a SwaggerUI page that tells you how to use the function (response/request schemas are generated from the pydantic models and constants the user defines in their function handler) 18 | 19 | ## Downloading the templates 20 | ``` 21 | $ faas template pull https://github.com/loudsquelch/openfaas-python3-fastapi-template 22 | ``` 23 | 24 | ## Using the python3-fastapi templates 25 | Create a new function 26 | 27 | ``` 28 | $ faas new --lang python3-fastapi 29 | ``` 30 | 31 | Build, push, and deploy 32 | 33 | ``` 34 | $ faas up -f .yml 35 | ``` 36 | 37 | Set your OpenFaaS gateway URL. For example: 38 | 39 | ``` 40 | $ OPENFAAS_URL=http://127.0.0.1:8080 41 | ``` 42 | 43 | Test the new function 44 | 45 | ``` 46 | $ curl -i $OPENFAAS_URL/function/ 47 | ``` 48 | 49 | ## Usage 50 | 51 | ### Request Body 52 | The function handler is passed one argument - *req* which contains the request body. 53 | 54 | ### Response Bodies 55 | By default, the template will automatically attempt to set the correct Content-Type header for you based on the type of response. 56 | -------------------------------------------------------------------------------- /template/python3-fastapi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openfaas/of-watchdog:0.7.2 as watchdog 2 | FROM python:3.7-alpine3.12 3 | 4 | COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog 5 | RUN chmod +x /usr/bin/fwatchdog 6 | 7 | ARG ADDITIONAL_PACKAGE 8 | RUN apk --no-cache add musl-dev gcc make ${ADDITIONAL_PACKAGE} 9 | 10 | # Add non root user 11 | RUN addgroup -S app && adduser app -S -G app 12 | RUN chown app /home/app 13 | 14 | USER app 15 | 16 | ENV PATH=$PATH:/home/app/.local/bin 17 | 18 | WORKDIR /home/app/ 19 | 20 | COPY requirements.txt . 21 | USER root 22 | RUN pip install -r requirements.txt 23 | USER app 24 | COPY index.py . 25 | 26 | RUN mkdir -p function 27 | RUN touch ./function/__init__.py 28 | WORKDIR /home/app/function/ 29 | COPY function/requirements.txt . 30 | RUN pip install --user -r requirements.txt 31 | 32 | WORKDIR /home/app/ 33 | 34 | USER root 35 | COPY function function 36 | RUN chown -R app:app ./ 37 | USER app 38 | 39 | ENV fprocess="uvicorn index:app --host 0.0.0.0 --port 8000" 40 | 41 | ENV cgi_headers="true" 42 | ENV mode="http" 43 | ENV upstream_url="http://127.0.0.1:8000" 44 | 45 | HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 46 | 47 | CMD ["fwatchdog"] 48 | -------------------------------------------------------------------------------- /template/python3-fastapi/function/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loudsquelch/openfaas-python3-fastapi-template/bd4772331acb007802bc63b9b8e008e24507192d/template/python3-fastapi/function/__init__.py -------------------------------------------------------------------------------- /template/python3-fastapi/function/handler.py: -------------------------------------------------------------------------------- 1 | from fastapi import HTTPException 2 | from pydantic import BaseModel 3 | from typing import Dict 4 | 5 | FUNCTION_NAME = 'templatefn' 6 | FUNCTION_VERSION = '1.0.0' 7 | FUNCTION_SUMMARY = "A function that does this" 8 | FUNCTION_RESPONSE_DESC = "Definition of object returned by function" 9 | 10 | 11 | class RequestModel(BaseModel): 12 | data: Dict 13 | 14 | 15 | class ResponseModel(BaseModel): 16 | data: Dict 17 | 18 | 19 | def handle(req): 20 | """handle a request to the function 21 | Args: 22 | req (dict): request body 23 | """ 24 | try: 25 | res = ResponseModel(data=req.data) 26 | except Exception: 27 | raise HTTPException(status_code=500, detail=f"An API Error occurred") 28 | return res 29 | -------------------------------------------------------------------------------- /template/python3-fastapi/function/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loudsquelch/openfaas-python3-fastapi-template/bd4772331acb007802bc63b9b8e008e24507192d/template/python3-fastapi/function/requirements.txt -------------------------------------------------------------------------------- /template/python3-fastapi/index.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Craig Dalton 2019. All rights reserved. 2 | # Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | import json 4 | 5 | from fastapi import FastAPI 6 | from fastapi.openapi.utils import get_openapi 7 | from function import handler 8 | from starlette import status 9 | from starlette.responses import HTMLResponse 10 | 11 | 12 | app = FastAPI(docs_url=None, redoc_url=None) 13 | 14 | 15 | def get_swagger_ui_html( 16 | *, 17 | openapi_spec: str, 18 | title: str, 19 | swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui-bundle.js", 20 | swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui.css", 21 | swagger_favicon_url: str = "https://raw.githubusercontent.com/openfaas/docs/master/docs/images/favicon.ico", 22 | ) -> HTMLResponse: 23 | """A tweaked fastapi.openapi.docs.get_swagger_ui_html to generate from raw JSON as opposed to using a url""" 24 | 25 | html = f""" 26 | 27 | 28 | 29 | 30 | 31 | {title} 32 | 33 | 34 |
35 |
36 | 37 | 38 | 53 | 54 | 55 | """ 56 | return HTMLResponse(html) 57 | 58 | 59 | def custom_openapi(): 60 | """Generates schema from OpenFaas function particulars""" 61 | if app.openapi_schema: 62 | return app.openapi_schema 63 | openapi_schema = get_openapi( 64 | title=handler.FUNCTION_NAME, 65 | version=f"v{handler.FUNCTION_VERSION}", 66 | routes=app.routes, 67 | ) 68 | paths = openapi_schema["paths"] 69 | upd_paths = {} 70 | # Supplement path specs 71 | for key in paths: 72 | path = paths[key] 73 | for method in path: 74 | path[method]["tags"] = ["Function Definitions"] 75 | # Modify path(s) to account for function being exposed 76 | # behind OpenFaas gateway 77 | rel_path = f"/function/{handler.FUNCTION_NAME}" 78 | if key.startswith(rel_path): 79 | upd_paths[key] = path 80 | else: 81 | rel_path = f"{rel_path}{key}" 82 | upd_paths[rel_path] = path 83 | openapi_schema["paths"] = upd_paths 84 | app.openapi_schema = openapi_schema 85 | return app.openapi_schema 86 | 87 | 88 | app.openapi = custom_openapi 89 | 90 | 91 | @app.get( 92 | "/", 93 | response_class=HTMLResponse, 94 | summary="Function Swagger UI Doc", 95 | response_description="Swagger UI documentation rendered as HTML (for consumption directly in a web browser)", 96 | ) 97 | async def swagger_ui_html(): 98 | openapi_html = get_swagger_ui_html( 99 | openapi_spec=json.dumps(app.openapi()), 100 | title=f"OpenFaas function: {handler.FUNCTION_NAME.title()}", 101 | ) 102 | return openapi_html 103 | 104 | 105 | @app.post( 106 | "/", 107 | status_code=status.HTTP_200_OK, 108 | response_model=handler.ResponseModel, 109 | summary=handler.FUNCTION_SUMMARY, 110 | response_description=handler.FUNCTION_RESPONSE_DESC, 111 | ) 112 | def handle_request( 113 | *, 114 | req: handler.RequestModel, 115 | ): 116 | return handler.handle(req) 117 | 118 | 119 | @app.get("/swagger.json", include_in_schema=False) 120 | async def swagger_json(): 121 | return app.openapi() 122 | -------------------------------------------------------------------------------- /template/python3-fastapi/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn -------------------------------------------------------------------------------- /template/python3-fastapi/template.yml: -------------------------------------------------------------------------------- 1 | language: python3-fastapi 2 | fprocess: uvicorn index:app --host 0.0.0.0 --port 8000 3 | --------------------------------------------------------------------------------