-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
195 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
name: Template API tests | ||
on: [push, pull_request] | ||
|
||
jobs: | ||
testing: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: ["3.10", "3.11"] | ||
steps: | ||
- name: Checkout repository with submodules | ||
uses: actions/checkout@v3 | ||
|
||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
|
||
- name: Install dependencies | ||
run: pip3 install -r requirements.txt | ||
|
||
- name: Test code | ||
run: pytest -vv tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
FROM ghcr.io/seravo/flask:latest | ||
|
||
FROM ghcr.io/seravo/fastapi:latest | ||
ARG APT_PROXY | ||
USER user | ||
|
||
COPY requirements.txt /app/requirements.txt | ||
COPY --chown=user:user requirements.txt . | ||
RUN pip3 install -r requirements.txt | ||
|
||
RUN pip install -r /app/requirements.txt | ||
COPY seravo $APPDIR/seravo/ | ||
|
||
ENV FLASK_APP hello:app | ||
COPY /app/ /app/ | ||
CMD ["seravo.main:app"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,28 @@ | ||
APT_PROXY ?= | ||
DOCKER ?= docker | ||
IMAGE ?= ypcs/FIXME:latest | ||
CONTAINER ?= FIXME | ||
SUDO ?= | ||
|
||
all: | ||
|
||
clean: | ||
$(DOCKER) rm -f "$(CONTAINER)" || : | ||
purge: | ||
$(SUDO) docker-compose down -v --remove-orphans | ||
|
||
stop: | ||
$(SUDO) docker-compose down | ||
|
||
build: | ||
$(DOCKER) build --build-arg APT_PROXY="$(APT_PROXY)" -t $(IMAGE) . | ||
$(SUDO) docker-compose build | ||
|
||
run: build | ||
$(SUDO) docker-compose up -d | ||
|
||
develop: run | ||
$(SUDO) docker-compose logs --follow | ||
|
||
run: clean | ||
$(DOCKER) run --name "$(CONTAINER)" --rm --volume "$(shell pwd)/app/:/app/:ro" -p 127.0.42.1:8080:8080 -d $(IMAGE) | ||
test: | ||
@$(SUDO) docker-compose exec api pytest tests -vv | ||
|
||
reload: | ||
$(DOCKER) exec "$(CONTAINER)" touch /tmp/reload-app | ||
@$(SUDO) docker-compose exec api reload-gunicorn | ||
|
||
logs: | ||
$(DOCKER) logs "$(CONTAINER)" | ||
exec: | ||
@$(SUDO) docker-compose exec api bash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1,107 @@ | ||
# Flask template | ||
# Seravo development team recruit template | ||
|
||
Flask project template | ||
_We are currently not hiring, but you can still clone the repository, give the tasks a spin, and send us an open application at [email protected]._ | ||
|
||
## Usage | ||
## Using the supplied Docker environment with Makefile | ||
|
||
To run hello world app, :8080 provides HTTP endpoint (to uWSGI), and :9000 provides uWSGI endpoint. So, let's try accessing via HTTP: | ||
If you have Docker installed, and are familiar with the `make` command, you can use the following commands to start the development environment. | ||
|
||
docker run --rm -it -v $(pwd)/app:/app -e FLASK_APP=hello:app -p 127.0.0.1:8080:8080 ghcr.io/seravo/flask:latest | ||
### Usage | ||
|
||
now try to open http://127.0.0.1:8080/ , you should see familiar greeting. | ||
Run `make develop` to start the container. You can access the API at `localhost:8080`. | ||
|
||
To publish your own app, mount your app to `/app`, and provide FLASK_APP environment variable with correct value. | ||
### Reloading changes | ||
|
||
Eg. | ||
Run `make reload` to reload the gunicorn workers. If you think that your changes are not reflected in the application after the reload, you can run `make develop` again. | ||
|
||
docker run --rm -it -v $(pwd)/app:/app -e FLASK_APP=app:app -p 127.0.0.1:8080:8080 ghcr.io/seravo/flask:latest | ||
### Cleanin up | ||
|
||
if you had app code like | ||
Run `make purge` to remove the container. | ||
|
||
app/app.py: | ||
### Tests | ||
|
||
from flask import Flask | ||
app = Flask(__name__) | ||
You can use `make test` to execute tests inside the container. | ||
|
||
@app.route("/") | ||
def some_view(): | ||
return "Hello myapp!" | ||
## Running FastAPI from the terminal | ||
|
||
Usually you shouldn't expose this HTTP endpoint directly to internet, but use eg. `ypcs/nginx:latest` as a reverse proxy. | ||
Alternatively, you can also run the application from your terminal with `uvicorn`. See [FastAPI docs](https://fastapi.tiangolo.com/#installation) on how to install the required packages. | ||
|
||
## Live reload | ||
Remember to also install the dependencies defined in `requirements.txt`! | ||
|
||
By default, you can manually reload the application by touching `/tmp/reload-app`. If you want your application to reload automatically after every save, set `FLASK_RELOAD` as `true`. | ||
```console | ||
pip install -r requirements.txt | ||
``` | ||
|
||
docker run --rm -it -v $(pwd)/app:/app -e FLASK_APP=app:app -e FLASK_RELOAD=true -p 127.0.0.1:8080:8080 ghcr.io/seravo/flask:latest | ||
Finally, run `uvicorn seravo.main:app --reload` to start our base application. | ||
|
||
Now, if you modify the code in `app.py` and save, the app will reload and your changes will be in effect. | ||
You can run the tests with `pytest -vv tests`, granted you have `pytest` installed. | ||
|
||
## Tests | ||
## The tasks | ||
|
||
Tests can be ran by executing `pytest-3` inside the container. For example, to run the tests for `hello.py` you would do it like this: | ||
The tasks should be possible to implement with the given packages in `requirements.txt`. If you want to use a new package (e.g. `requests` instead of `httpx`), you can just add it to the requirements file and use it in your code. | ||
|
||
docker exec -t {CONTAINER_NAME} pytest-3 hello.py -vv | ||
1. Create a new endpoint `/pokemon/{name}`, which can be accessed with a GET request. | ||
|
||
Test output should look something like this: | ||
- When triggered, the application queries `https://pokeapi.co/api/v2/pokemon/{name}/` for a JSON blob about the chosen pokemon | ||
- Application should handle possible errors | ||
- Data should be modified to match the following structure: | ||
|
||
platform linux -- Python 3.9.2, pytest-6.0.2, py-1.10.0, pluggy-0.13.0 -- /usr/bin/python3 | ||
cachedir: .pytest_cache | ||
rootdir: /app | ||
collected 1 item | ||
```python | ||
name: str | ||
height: int | ||
weight: int | ||
types: List[str] | ||
``` | ||
|
||
hello.py::test_hello PASSED | ||
- Application returns the modifed JSON blob to the user | ||
- So, for example, `/pokemon/pikachu` would return this: | ||
|
||
```json | ||
{ | ||
"name": "pikachu", | ||
"height": 4, | ||
"weight": 60, | ||
"types": ["electric"] | ||
} | ||
``` | ||
|
||
2. Create a new endpoint `/caps/`, which can be accessed with a POST request. | ||
|
||
- Endpoint can be queried with the following body: | ||
|
||
```json | ||
{ | ||
"message": "string" | ||
} | ||
``` | ||
|
||
- The API will return the content of the `message` field in all CAPS. | ||
- The API should reject the request if the body does not match the one above | ||
- The API should reject the request if there already is a capitalized letter in the data | ||
|
||
### Grading | ||
|
||
We will give points based on the following criteria: | ||
|
||
1. Code readability, code documentation, version control | ||
2. Code structure: is everything bundled in a single file or distributed | ||
3. Error handling: HTTP status codes | ||
4. Test coverage: Were the tests extended to test the new features. | ||
|
||
### Helpful links | ||
|
||
[Path parameters](https://fastapi.tiangolo.com/tutorial/path-params/) | ||
|
||
[Request body](https://fastapi.tiangolo.com/tutorial/body/) | ||
|
||
[HTTPX QuickStart](https://www.python-httpx.org/quickstart/) | ||
|
||
[HTTPX Async](https://www.python-httpx.org/async/) | ||
|
||
[Pydantic validators](https://docs.pydantic.dev/usage/validators/) | ||
|
||
[Test mocking](https://docs.pytest.org/en/6.2.x/monkeypatch.html#monkeypatching-returned-objects-building-mock-classes) | ||
|
||
## Submitting your work | ||
|
||
Set up a private repository with your modifications, share it to @seravo-hrm and notify us at [email protected] with your application and we will get back to you as soon as possible! |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
version: "3.7" | ||
|
||
services: | ||
api: | ||
image: seravo-api | ||
build: . | ||
environment: | ||
- PYTHONPATH=/app | ||
healthcheck: | ||
disable: true | ||
ports: ["8080:8000"] | ||
volumes: | ||
- "./seravo/:/app/seravo:rw" | ||
- "./tests/:/app/tests:rw" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
# your packages here | ||
fastapi==0.88 | ||
pytest==7.1 | ||
httpx==0.23 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
from fastapi import FastAPI | ||
|
||
|
||
def create_app() -> FastAPI: | ||
"""Create a FastAPI application""" | ||
app = FastAPI() | ||
|
||
@app.get("/") | ||
async def root(): | ||
"""Return API information""" | ||
return {'detail': "Seravo API"} | ||
|
||
return app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from seravo.common.app import create_app | ||
|
||
app = create_app() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from collections.abc import Generator | ||
|
||
from fastapi.testclient import TestClient | ||
from pytest import fixture | ||
|
||
from seravo.common.app import create_app | ||
|
||
|
||
class BaseTestSuite: | ||
"""Common operations to be used in testing""" | ||
|
||
@fixture | ||
def client(self) -> Generator[TestClient, None, None]: | ||
"""Return an API client for testing purposes""" | ||
app = create_app() | ||
yield TestClient(app) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from .test_00_base import BaseTestSuite | ||
|
||
|
||
class TestCommon(BaseTestSuite): | ||
"""Test the common operations of the API""" | ||
|
||
def test_api_root(self, client) -> None: | ||
"""Test that the API root returns content""" | ||
res = client.get("/") | ||
assert res.status_code == 200 | ||
assert res.json().get('detail') == "Seravo API" |