From 998bbd6d663058b02d6a37131ce54ae1b110acd6 Mon Sep 17 00:00:00 2001 From: sistemaSherpa <148259831+sistemaSherpa@users.noreply.github.com> Date: Wed, 22 May 2024 12:52:18 -0600 Subject: [PATCH] Initial commit --- .dockerignore | 4 ++ .github/script/test.ps1 | 27 ++++++++ .github/script/test.sh | 22 ++++++ .github/workflows/docker-image.yml | 18 +++++ .github/workflows/python-app.yml | 29 ++++++++ .gitignore | 4 ++ .gitpod.yml | 2 + Dockerfile | 12 ++++ LICENSE | 21 ++++++ README.md | 106 +++++++++++++++++++++++++++++ api/index.py | 1 + docker-compose.yml | 5 ++ main.py | 1 + requirements.txt | 5 ++ src/__main__.py | 0 src/dtos/ISayHelloDto.py | 4 ++ src/index.py | 20 ++++++ test_index.http | 20 ++++++ test_index.py | 23 +++++++ vercel.json | 15 ++++ 20 files changed, 339 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/script/test.ps1 create mode 100644 .github/script/test.sh create mode 100644 .github/workflows/docker-image.yml create mode 100644 .github/workflows/python-app.yml create mode 100644 .gitignore create mode 100644 .gitpod.yml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 api/index.py create mode 100644 docker-compose.yml create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 src/__main__.py create mode 100644 src/dtos/ISayHelloDto.py create mode 100644 src/index.py create mode 100644 test_index.http create mode 100644 test_index.py create mode 100644 vercel.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f5f4229 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.vercel +venv +.idea +__pycache__ diff --git a/.github/script/test.ps1 b/.github/script/test.ps1 new file mode 100644 index 0000000..fcd0167 --- /dev/null +++ b/.github/script/test.ps1 @@ -0,0 +1,27 @@ +#!/bin/pwsh + +if (Get-Command pip -ErrorAction SilentlyContinue) { + Write-Host "Installing Python dependencies..." + pip install -r requirements.txt +} +else { + Write-Host "pip is not installed" + Write-Host "Installing pip" + Invoke-WebRequest -Uri https://bootstrap.pypa.io/get-pip.py -OutFile get-pip.py + python get-pip.py + Write-Host "Installing Python dependencies..." + pip install -r requirements.txt +} + +if (Get-Command pytest -ErrorAction SilentlyContinue) { + Write-Host "Running tests..." + pytest --version + pytest +} +else { + Write-Host "pytest is not installed" + Write-Host "installing pytest" + pip install pytest + pytest --version + pytest +} \ No newline at end of file diff --git a/.github/script/test.sh b/.github/script/test.sh new file mode 100644 index 0000000..12f3022 --- /dev/null +++ b/.github/script/test.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +if ! [ -x "$(command -v pip)" ]; then + echo 'Error: pip is not installed.' >&2 + echo 'Installing pip...' + curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py + python get-pip.py + rm get-pip.py + echo 'pip installed.' +fi + +pip install -r requirements.txt + +if ! [ -x "$(command -v pytest)" ]; then + echo 'Error: pytest is not installed.' >&2 + echo 'Installing pytest' + pip install pytest + echo 'pytest installed.' +fi + + +pytest \ No newline at end of file diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..d657a63 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 0000000..b528a9e --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,29 @@ +name: Python testing + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: + contents: read + +jobs: + run_tests_on_ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run Bash script file + run: | + chmod +x ./.github/script/test.sh + ./.github/script/test.sh + shell: bash + run_tests_on_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Run PowerShell script file + run: | + .\.github/script/test.ps1 + shell: pwsh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e6eda4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vercel +venv +.idea +__pycache__ \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..57df11a --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,2 @@ +tasks: + - init: docker-compose up diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1373048 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.9 + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --upgrade pip && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9e0c47e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Hebert F. Barros + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..cdb6f47 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +

+ :package: deploy-python-fastapi-in-vercel +

:package: deploy-python-fastapi-in-vercel

+

This example shows how to use FastApi on Vercel with Serverless Functions using the Python Runtime.

+

+ +

+ + Issues + + + GitHub pull requests + +
+ Report Bug + Request Feature +

+

Systems on which it has been tested.

+

+ + Ubuntu + + + Windows + +

+

Did you like the project? Please, considerate a donation to help improve!

+ +

+ +# Getting started + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fhebertcisco%2Fdeploy-python-fastapi-in-vercel%2Ftree%2Fmain%2Fpython%2FFastApi&demo-title=FastApi%20%2B%20Vercel&demo-description=Use%20FastApi%202%20on%20Vercel%20with%20Serverless%20Functions%20using%20the%20Python%20Runtime.&demo-url=https%3A%2F%2FFastApi-python-template.vercel.app%2F&demo-image=https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png) + +# FastApi + Vercel + +This example shows how to use FastApi 0.88.0 on Vercel with Serverless Functions using the [Python Runtime](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/python). + +[![Python testing](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/python-app.yml/badge.svg?branch=main)](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/python-app.yml) +[![Docker Image CI](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/docker-image.yml/badge.svg)](https://github.com/hebertcisco/deploy-python-fastapi-in-vercel/actions/workflows/docker-image.yml) + +## Demo + +[deploy-python-fastapi-in-vercel.vercel.app](https://deploy-python-fastapi-in-vercel.vercel.app) + +## How it Works + +This example uses the Web Server Gateway Interface (WSGI) with FastApi to enable handling requests on Vercel with Serverless Functions. + +## Running Locally + +### With Docker Compose + +```bash +docker-compose up +``` + +### With Docker + +```bash +# Build the Docker image +docker build -t deploy-python-fastapi-in-vercel . + +# Run the Docker container +docker run -p 8000:8000 deploy-python-fastapi-in-vercel + +``` + +### With uvicorn + +#### Install dependencies + +```bash +pip install -r requirements.txt +``` + +```bash +uvicorn main:app --host 0.0.0.0 --port 8000 +``` + +Your FastApi application is now available at `http://localhost:8000`. + +## One-Click Deploy + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=vercel-examples): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fhebertcisco%2Fdeploy-python-fastapi-in-vercel%2Ftree%2Fmain%2Fpython%2FFastApi&demo-title=FastApi%20%2B%20Vercel&demo-description=Use%20FastApi%202%20on%20Vercel%20with%20Serverless%20Functions%20using%20the%20Python%20Runtime.&demo-url=https%3A%2F%2FFastApi-python-template.vercel.app%2F&demo-image=https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png) + +## 🤝 Contributing + +Contributions, issues and feature requests are welcome!
Feel free to check [issues page](issues). + +## Show your support + +Give a ⭐️ if this project helped you! + +Or buy me a coffee 🙌🏾 + + + + + +## 📝 License + +Copyright © 2024 [Hebert F Barros](https://github.com/hebertcisco).
+This project is [MIT](LICENSE) licensed. diff --git a/api/index.py b/api/index.py new file mode 100644 index 0000000..227406a --- /dev/null +++ b/api/index.py @@ -0,0 +1 @@ +from src.index import app \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..677df3b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,5 @@ +services: + web: + build: . + ports: + - "8000:8000" \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f850a29 --- /dev/null +++ b/main.py @@ -0,0 +1 @@ +from api.index import app \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1dec285 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.109.1 +pytest==7.1.3 +pytest-asyncio==0.20.3 +uvicorn==0.20.0 +pydantic~=1.10.4 diff --git a/src/__main__.py b/src/__main__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/dtos/ISayHelloDto.py b/src/dtos/ISayHelloDto.py new file mode 100644 index 0000000..f81f0aa --- /dev/null +++ b/src/dtos/ISayHelloDto.py @@ -0,0 +1,4 @@ +from pydantic import BaseModel + +class ISayHelloDto(BaseModel): + message: str \ No newline at end of file diff --git a/src/index.py b/src/index.py new file mode 100644 index 0000000..246f9fb --- /dev/null +++ b/src/index.py @@ -0,0 +1,20 @@ +from fastapi import FastAPI + +from src.dtos.ISayHelloDto import ISayHelloDto + +app = FastAPI() + + +@app.get("/") +async def root(): + return {"message": "Hello World"} + + +@app.get("/hello/{name}") +async def say_hello(name: str): + return {"message": f"Hello {name}"} + + +@app.post("/hello") +async def hello_message(dto: ISayHelloDto): + return {"message": f"Hello {dto.message}"} diff --git a/test_index.http b/test_index.http new file mode 100644 index 0000000..9053072 --- /dev/null +++ b/test_index.http @@ -0,0 +1,20 @@ +# Test your FastAPI endpoints + +GET http://127.0.0.1:8000/ +Accept: application/json + +### + +GET http://127.0.0.1:8000/hello/User +Accept: application/json + +### + +POST /hello HTTP/1.1 +Host: 127.0.0.1:8000 +Content-Type: application/json +Content-Length: 28 + +{ + "message": "Fulano!" +} \ No newline at end of file diff --git a/test_index.py b/test_index.py new file mode 100644 index 0000000..99258ad --- /dev/null +++ b/test_index.py @@ -0,0 +1,23 @@ +import pytest + +from src.dtos.ISayHelloDto import ISayHelloDto +from src.index import root, say_hello, hello_message + + +@pytest.mark.asyncio +async def test_root(): + result = await root() + assert result == {'message': 'Hello World'} + + +@pytest.mark.asyncio +async def test_say_hello(): + result = await say_hello("John") + assert result == {'message': 'Hello John'} + + +@pytest.mark.asyncio +async def test_hello_message(): + dto = ISayHelloDto(message="Alice") + result = await hello_message(dto) + assert result == {'message': 'Hello Alice'} diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..6172529 --- /dev/null +++ b/vercel.json @@ -0,0 +1,15 @@ +{ + "devCommand": "uvicorn main:app --host 0.0.0.0 --port 3000", + "builds": [ + { + "src": "api/index.py", + "use": "@vercel/python" + } + ], + "routes": [ + { + "src": "/(.*)", + "dest": "api/index.py" + } + ] +} \ No newline at end of file