Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e2d7868
Add structure for challenges
takos22 Sep 13, 2021
15a4763
Add piston language API client
takos22 Sep 14, 2021
ffea92f
Add piston language and version checker
takos22 Sep 14, 2021
0215802
Add challenge language models
takos22 Sep 14, 2021
08d07fe
Add piston service closing in app shutdown
takos22 Sep 14, 2021
930363b
Add challenge language routes
takos22 Sep 15, 2021
ec1949b
Update models submodule version
takos22 Sep 16, 2021
ebb549a
Fix piston service
takos22 Sep 20, 2021
08aa58d
Pin aiohttp to 3.x
takos22 Sep 20, 2021
304d216
Add some tests for challenges
takos22 Sep 20, 2021
2073024
Fix bug
takos22 Sep 30, 2021
a1fa157
Add tests for PATCH endpoint
takos22 Sep 30, 2021
5d429df
Merge branch 'staging' into challenges
takos22 Sep 30, 2021
db67b0e
Fix bug
takos22 Sep 30, 2021
29551c1
Update Pipfile.lock
takos22 Sep 30, 2021
0b6e100
Update services
takos22 Oct 5, 2021
7504b8c
Update routes
takos22 Oct 5, 2021
4da5b77
Update tests
takos22 Oct 5, 2021
72c0f40
Update PISTON_URL env var
takos22 Oct 7, 2021
1e3f683
Fix linting
takos22 Oct 7, 2021
41668ce
Merge branch 'staging' of https://github.com/Tech-With-Tim/API into c…
takos22 Oct 7, 2021
a6a65da
Update Pipfile.lock
takos22 Oct 7, 2021
5de6515
Update routes
takos22 Oct 8, 2021
38e328e
Add tests for delete endpoint
takos22 Oct 8, 2021
b1bee8a
Update UPDATE route
takos22 Oct 8, 2021
77b0b2c
Update challenge manager role fixture
takos22 Oct 8, 2021
064c6f7
Update query formatting
SylteA Oct 9, 2021
8c95501
piston_url is no longer optional.
SylteA Oct 9, 2021
be4f676
Update PISTON_URL config
takos22 Oct 9, 2021
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
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pytest-asyncio = "*"
[packages]
pyjwt = "*"
postdb = "*"
aiohttp = "*"
aiohttp = "~=3.7"
fastapi = "*"
aioredis = "*"
fakeredis = "*"
Expand Down
149 changes: 116 additions & 33 deletions Pipfile.lock

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

15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pipenv install --dev

### Environment variables

#### Required

Start by writing this in a file named `.env`:

```prolog
Expand All @@ -85,15 +87,22 @@ DISCORD_CLIENT_SECRET=
And fill in the variables with the values below:

- `REDIS_URI` is the Redis server URI.
- `POSTGRES_URI` is the PostgreSQL database URI.
- `SECRET_KEY` is the key used for JWT token encoding.
- `TEST_REDIS_URU` is the Connection URI for Redis testing server.
- `TEST_POSTGRES_URI` is the PostgreSQL database URI for tests.
- `POSTGRES_URI` is the PostgreSQL database URI.
- `DISCORD_CLIENT_ID` is the Discord application ID. Copy it from your Discord application page (see below).
- `DISCORD_CLIENT_SECRET` is the Discord application secret. Copy it from your Discord application page (see below).

![Client ID and secret](https://cdn.discordapp.com/attachments/721750194797936823/794646777840140298/unknown.png)

#### Optional

For testing you need to add these environment variables:

- `TEST_REDIS_URI` is the Connection URI for Redis testing server.
- `TEST_POSTGRES_URI` is the PostgreSQL database URI for tests.

If you are self hosting the Piston API, you need to set the `PISTON_URL` environment variable.

### Running

Run the API and initialise the database:
Expand Down
4 changes: 2 additions & 2 deletions api/services/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
from typing import Optional


session: Optional[ClientSession] = None
__all__ = ("session",)

__all__ = (session,)
session: Optional[ClientSession] = None
68 changes: 68 additions & 0 deletions api/services/piston.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import logging
from typing import Any, Dict, List

import config
from api.services import http


__all__ = ("get_runtimes", "get_runtimes_dict", "get_runtime", "Runtime")

log = logging.getLogger()

_base_url: str = (
config.piston_url().rstrip("/") + "/"
) # make sure there's a / at the end


async def _make_request(method: str, endpoint: str, data: Any = None) -> Any:
async with http.session.request(
method,
_base_url + endpoint,
json=data,
raise_for_status=True,
) as response:
return await response.json()


async def get_runtimes() -> List["Runtime"]:
"""Get a list of all available runtimes."""
runtimes = await _make_request("GET", "runtimes")
return [Runtime(runtime) for runtime in runtimes]


async def get_runtimes_dict() -> Dict[str, List["Runtime"]]:
"""Get a dictionary of language names and aliases mapped to a list of
all the runtimes with that name or alias.
"""

runtimes = await get_runtimes()
runtimes_dict = {}

for runtime in runtimes:
if runtime.language in runtimes_dict:
runtimes_dict[runtime.language].append(runtime)
else:
runtimes_dict[runtime.language] = [runtime]

for alias in runtime.aliases:
if alias in runtimes_dict:
runtimes_dict[alias].append(runtime)
else:
runtimes_dict[alias] = [runtime]

return runtimes_dict


async def get_runtime(language: str) -> List["Runtime"]:
"""Get a runtime with a language or an alias."""

runtimes_dict = await get_runtimes_dict()
return runtimes_dict.get(language, [])


class Runtime:
def __init__(self, data: dict):
self.language = data["language"]
self.version = data["version"]
self.aliases = data["aliases"]
self.runtime = data.get("runtime")
5 changes: 2 additions & 3 deletions api/services/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import json


__all__ = ("pool",)

log = logging.getLogger(__name__)


Expand All @@ -22,6 +24,3 @@ async def dispatch(channel: ChannelT, message: Union[EncodableT, list, dict]) ->


pool: Optional[Union[FakeRedis, Redis]] = None


__all__ = (pool,)
6 changes: 6 additions & 0 deletions api/versions/v1/routers/challenges/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from . import languages
from .routes import router

router.include_router(languages.router)

__all__ = (router,)
3 changes: 3 additions & 0 deletions api/versions/v1/routers/challenges/languages/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .routes import router

__all__ = (router,)
18 changes: 18 additions & 0 deletions api/versions/v1/routers/challenges/languages/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from fastapi import HTTPException

from api.services import piston


async def check_piston_language_version(language: str, version: str):
"""Checks if a language and its version are installed on the Piston service.

Raises an :class:`fastapi.HTTPException` otherwise with a 404 status code.
"""

runtimes = await piston.get_runtime(language)
if not runtimes:
raise HTTPException(404, "Piston language not found")

versions = [runtime.version for runtime in runtimes]
if version not in versions:
raise HTTPException(404, "Piston language version not found")
28 changes: 28 additions & 0 deletions api/versions/v1/routers/challenges/languages/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional

from pydantic import BaseModel, Field, HttpUrl


class ChallengeLanguageResponse(BaseModel):
id: str
name: str
download_url: Optional[HttpUrl]
disabled: bool = False
piston_lang: str
piston_lang_ver: str


class NewChallengeLanguageBody(BaseModel):
name: str = Field(..., min_length=4, max_length=32)
download_url: Optional[HttpUrl] = None
disabled: bool = False
piston_lang: str
piston_lang_ver: str


class UpdateChallengeLanguageBody(BaseModel):
name: str = Field("", min_length=4, max_length=32)
download_url: Optional[HttpUrl] = None
disabled: bool = False
piston_lang: str = ""
piston_lang_ver: str = ""
Loading