Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/mrmat-python-api-fastapi.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/runConfigurations/run.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 13 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
GIT_SHA := $(shell git rev-parse --short HEAD)
VERSION ?= 0.0.0-dev0.${GIT_SHA}
PYTHON_VERSION := $(shell echo "${VERSION}" | sed -e 's/-dev0\./-dev0+/')
WHEEL_VERSION := $(shell echo "${VERSION}" | sed -e 's/-dev0\./.dev0+/')

ROOT_PATH := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

PYTHON_SOURCES := $(shell find src/mrmat_python_api_fastapi -name '*.py')
PYTHON_TARGET := dist/mrmat_python_api_fastapi-${PYTHON_VERSION}-py3-none-any.whl
PYTHON_TARGET := dist/mrmat_python_api_fastapi-${WHEEL_VERSION}-py3-none-any.whl
CONTAINER_SOURCES := $(shell find var/container)
HELM_SOURCES := $(shell find var/helm)
HELM_TARGET := dist/mrmat-python-api-fastapi-$(VERSION).tgz
Expand All @@ -31,16 +34,22 @@ container: $(PYTHON_TARGET) $(CONTAINER_SOURCES)
-f var/container/Dockerfile \
-t localhost:5001/mrmat-python-api-fastapi:$(VERSION) \
--build-arg MRMAT_VERSION=$(VERSION) \
.
--build-arg WHEEL=$(PYTHON_TARGET) \
$(ROOT_PATH)
docker push localhost:5001/mrmat-python-api-fastapi:$(VERSION)

helm-install: $(HELM_TARGET)
kubectl create ns mpafastapi || true
kubectl label --overwrite ns mpafastapi istio-injection=true
helm upgrade \
mrmat-python-api-fastapi \
${HELM_TARGET} \
--install \
--create-namespace \
--namespace mrmat-python-api-fastapi
--force \
--namespace mpafastapi

helm-uninstall:
helm delete -n mpafastapi mrmat-python-api-fastapi

clean:
rm -rf build dist
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
[![Build](https://github.com/MrMatOrg/mrmat-python-api-fastapi/actions/workflows/build.yml/badge.svg)](https://github.com/MrMatOrg/mrmat-python-api-fastapi/actions/workflows/build.yml)


Boilerplate (and playground) for a code-first Python FastAPI API, with all the bells and whistles we've come to expect
Boilerplate (and playground) for a code-first Python FastAPI API, with all the bells and whistles we've come to expect.

33 changes: 29 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
#
# Runtime requirements

fastapi==0.115.11 # MIT
sqlalchemy[asyncio]==2.0.40 # MIT
uvicorn==0.34.0 # BSD 3-Clause
pydantic==2.10.6 # MIT
fastapi==0.115.11 # MIT
sqlalchemy[asyncio]==2.0.40 # MIT
uvicorn==0.34.0 # BSD 3-Clause
pydantic==2.10.6 # MIT
psycopg2-binary==2.9.10 # LGPL with exceptions

prometheus-fastapi-instrumentator==7.1.0 # ISC

# For zero-code-instrumentation
# Check the requirements by using opentelemetry-bootstrap -a requirements
opentelemetry-distro # Apache 2.0
opentelemetry-exporter-otlp
opentelemetry-instrumentation-asyncio==0.53b1
opentelemetry-instrumentation-dbapi==0.53b1
opentelemetry-instrumentation-logging==0.53b1
opentelemetry-instrumentation-sqlite3==0.53b1
opentelemetry-instrumentation-threading==0.53b1
opentelemetry-instrumentation-urllib==0.53b1
opentelemetry-instrumentation-wsgi==0.53b1
opentelemetry-instrumentation-asgi==0.53b1
opentelemetry-instrumentation-click==0.53b1
opentelemetry-instrumentation-fastapi==0.53b1
opentelemetry-instrumentation-grpc==0.53b1
opentelemetry-instrumentation-httpx==0.53b1
opentelemetry-instrumentation-requests==0.53b1
opentelemetry-instrumentation-sqlalchemy==0.53b1
opentelemetry-instrumentation-starlette==0.53b1
opentelemetry-instrumentation-tortoiseorm==0.53b1
opentelemetry-instrumentation-urllib3==0.53b1
3 changes: 2 additions & 1 deletion src/mrmat_python_api_fastapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
# 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.

from pydantic import BaseModel

from .config import Config
app_config = Config.from_json_file()
app_config = Config.from_context()
from sqlalchemy.orm import DeclarativeBase

class ORMBase(DeclarativeBase):
Expand Down
1 change: 1 addition & 0 deletions src/mrmat_python_api_fastapi/apis/greeting/v2/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from typing import Optional
from fastapi import APIRouter

from mrmat_python_api_fastapi.apis.greeting.v2 import GreetingV2Output

router = APIRouter()
Expand Down
1 change: 1 addition & 0 deletions src/mrmat_python_api_fastapi/apis/greeting/v3/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# SOFTWARE.

from fastapi import APIRouter

from mrmat_python_api_fastapi.apis.greeting.v3 import GreetingV3Output

router = APIRouter()
Expand Down
12 changes: 10 additions & 2 deletions src/mrmat_python_api_fastapi/apis/platform/v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import uuid

from fastapi import APIRouter, Depends, status, HTTPException
from fastapi.responses import Response
from sqlalchemy.exc import SQLAlchemyError
Expand Down Expand Up @@ -93,12 +95,13 @@ async def create_resource(data: ResourceInputSchema,
response: Response,
session: Session = Depends(get_db)):
try:
resource = Resource(name=data.name, owner_uid=data.owner_uid)
resource = Resource(uid=str(uuid.uuid4()), name=data.name, owner_uid=data.owner_uid)
session.add(resource)
session.commit()
response.status_code = 201
return resource
except SQLAlchemyError as e:
session.rollback()
raise HTTPException(status_code=500, detail="A database error occurred") from e


Expand Down Expand Up @@ -131,6 +134,7 @@ async def modify_resource(uid: str,
session.commit()
return resource
except SQLAlchemyError as e:
session.rollback()
raise HTTPException(status_code=500, detail="A database error occurred") from e


Expand Down Expand Up @@ -161,6 +165,7 @@ async def remove_resource(uid: str,
response.status_code = 204
return {}
except SQLAlchemyError as e:
session.rollback()
raise HTTPException(status_code=500, detail="A database error occurred") from e


Expand Down Expand Up @@ -216,12 +221,13 @@ async def create_owner(data: OwnerInputSchema,
response: Response,
session: Session = Depends(get_db)):
try:
owner = Owner(name=data.name, client_id='TODO')
owner = Owner(uid=str(uuid.uuid4()), name=data.name)
session.add(owner)
session.commit()
response.status_code = 201
return owner
except SQLAlchemyError as e:
session.rollback()
# Handle the error appropriately, maybe raise an HTTPException
raise HTTPException(status_code=500, detail="A database error occurred") from e

Expand Down Expand Up @@ -255,6 +261,7 @@ async def modify_owner(uid: str,
session.commit()
return owner
except SQLAlchemyError as e:
session.rollback()
raise HTTPException(status_code=500, detail="A database error occurred") from e


Expand Down Expand Up @@ -284,4 +291,5 @@ async def remove_owner(uid: str,
session.commit()
response.status_code = status.HTTP_204_NO_CONTENT
except SQLAlchemyError as e:
session.rollback()
raise HTTPException(status_code=500, detail="A database error occurred") from e
6 changes: 3 additions & 3 deletions src/mrmat_python_api_fastapi/apis/platform/v1/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@
class Owner(ORMBase):
__tablename__ = 'owners'
__schema__ = 'mrmat-python-api-fastapi'
uid: Mapped[str] = mapped_column(String, primary_key=True, default=str(uuid.uuid4()))
uid: Mapped[str] = mapped_column(String, primary_key=True)

client_id: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
client_id: Mapped[str] = mapped_column(String(255), nullable=True, unique=True)
name: Mapped[str] = mapped_column(String(255), nullable=False)
resources: Mapped[list["Resource"]] = relationship('Resource', back_populates='owner')


class Resource(ORMBase):
__tablename__ = 'resources'
__schema__ = 'mrmat-python-api-fastapi'
uid: Mapped[str] = mapped_column(String, primary_key=True, default=str(uuid.uuid4()))
uid: Mapped[str] = mapped_column(String, primary_key=True)
owner_uid: Mapped[str] = mapped_column(String, ForeignKey('owners.uid'), nullable=False)
name: Mapped[str] = mapped_column(String(255), nullable=False)

Expand Down
3 changes: 3 additions & 0 deletions src/mrmat_python_api_fastapi/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

from fastapi import FastAPI

from prometheus_fastapi_instrumentator import Instrumentator

from mrmat_python_api_fastapi.apis.healthz import api_healthz
from mrmat_python_api_fastapi.apis.greeting import api_greeting_v1, api_greeting_v2, api_greeting_v3
from mrmat_python_api_fastapi.apis.platform import api_platform_v1
Expand All @@ -33,6 +35,7 @@
app.include_router(api_greeting_v3, prefix='/api/greeting/v3', tags=['greeting'])
app.include_router(api_platform_v1, prefix='/api/platform/v1', tags=['platform'])

Instrumentator().instrument(app).expose(app)

@app.get('/')
def index():
Expand Down
12 changes: 8 additions & 4 deletions src/mrmat_python_api_fastapi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,18 @@ class Config:
A class to deal with application configuration
"""
secret_key: str = secrets.token_urlsafe(16)
db_url: str = 'sqlite://'
db_url: str = 'sqlite:///'

@staticmethod
def from_json_file(file: str | None = os.getenv('APP_CONFIG')):
def from_context(file: str | None = os.getenv('APP_CONFIG')):
runtime_config = Config()
if file and os.path.exists(file):
with open(file, 'r', encoding='UTF-8') as c:
file_config = json.load(c)
runtime_config.secret_key = file_config.get_owner('secret_key', secrets.token_urlsafe(16))
runtime_config.db_url = file_config.get_owner('db_url', 'sqlite://')
runtime_config.secret_key = file_config.get('secret_key', secrets.token_urlsafe(16))
runtime_config.db_url = file_config.get('db_url', 'sqlite:///')
if 'APP_CONFIG_SECRET_KEY' in os.environ:
runtime_config.secret_key = os.getenv('APP_CONFIG_SECRET_KEY', secrets.token_urlsafe(16))
if 'APP_CONFIG_DB_URL' in os.environ:
runtime_config.db_url = os.getenv('APP_CONFIG_DB_URL', '')
return runtime_config
3 changes: 2 additions & 1 deletion src/mrmat_python_api_fastapi/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ def get_db() -> Session:
else:
engine = create_engine(url=app_config.db_url)
session_local = sessionmaker(autocommit=False, autoflush=False, bind=engine)
ORMBase.metadata.create_all(bind=engine)
with session_local():
ORMBase.metadata.create_all(bind=engine)
return session_local()
19 changes: 19 additions & 0 deletions var/client-k8s.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### GET Owners
GET https://mpafastapi.covenant.local/api/platform/v1/owners

### GET resources
GET https://mpafastapi.covenant.local/api/platform/v1/resources

### Create Owner
POST https://mpafastapi.covenant.local/api/platform/v1/owners
Content-Type: application/json

{
"name": "MrMat"
}

> {%
const owner_uid = jsonPath(response.body, ".owner_uid")
%}

###
19 changes: 19 additions & 0 deletions var/client-localhost.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
### GET Owners
GET http://localhost:8000/api/platform/v1/owners

### GET resources
GET http://localhost:8000/api/platform/v1/resources

### Create Owner
POST http://localhost:8000/api/platform/v1/owners
Content-Type: application/json

{
"name": "MrMat"
}

> {%
const owner_uid = jsonPath(response.body, ".owner_uid")
%}

###
9 changes: 5 additions & 4 deletions var/container/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
FROM python:3.12-alpine AS build
ARG MRMAT_VERSION="0.0.0.dev0"
ADD dist/mrmat_python_api_fastapi-*-py3-none-any.whl /
RUN pip install --user /mrmat_python_api_fastapi-*-py3-none-any.whl
ARG WHEEL=""
ADD "$WHEEL" /
RUN pip install --user /mrmat_python_api_fastapi-*.whl

FROM python:3.12-alpine
ARG MRMAT_VERSION="0.0.0.dev0"
Expand All @@ -13,5 +14,5 @@ RUN chown -R 1000:1000 /home/app/.local

USER app:app
EXPOSE 8000
#CMD ["/home/app/.local/bin/uvicorn", "--host", "0.0.0.0", "--port", "8000", "mrmat_python_api_fastapi.app:app"]
CMD ["/home/app/.local/bin/mrmat-python-api-fastapi"]
CMD ["/home/app/.local/bin/opentelemetry-instrument", \
"/home/app/.local/bin/uvicorn", "--host", "0.0.0.0", "--port", "8000", "mrmat_python_api_fastapi.app:app"]
9 changes: 0 additions & 9 deletions var/helm/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,5 @@ apiVersion: v2
name: mrmat-python-api-fastapi
description: A Helm chart for MrMat Python API FastAPI
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: "0.0.0"

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.0.0.dev0"
8 changes: 8 additions & 0 deletions var/helm/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

{{/* Common labels */}}
{{ define "common.labels" }}
app: mpafastapi
version: {{ .Chart.AppVersion }}
app.kubernetes.io/part-of: mpafastapi
app.kubernetes.io/version: {{ .Chart.AppVersion }}
{{ end }}
Loading
Loading