Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Started REST API v1 #658

Merged
merged 59 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
96bdfc4
Moved frontend-vuetify into repo root
TheMrSheldon May 2, 2024
52eba1d
added configurable REST and gRPC base url and removed old references …
TheMrSheldon May 2, 2024
219db03
removed application/src/tira/static/tira/frontend-vuetify
TheMrSheldon May 2, 2024
272ce27
Configured Authelia and placed TIRA dev frontend behind it
TheMrSheldon May 3, 2024
c078ea1
removed legacy authentication
TheMrSheldon May 3, 2024
ffc64d0
Removed leftover code from legacy endpoint and configured discourse d…
TheMrSheldon May 3, 2024
8622108
configured tira api behind authentication and proxy and added login b…
TheMrSheldon May 3, 2024
666326b
authelia now sets the X-Disraptor-User and X-Disraptor-Groups header
TheMrSheldon May 3, 2024
2e47472
cleaned up some code
TheMrSheldon May 3, 2024
d302789
added some documentation and minor reverted changes
TheMrSheldon May 3, 2024
56ae6dd
Added mock-secret to Unit Test workflow
TheMrSheldon May 3, 2024
269f061
fixed unit tests for backend
TheMrSheldon May 3, 2024
c181254
I dont get it
TheMrSheldon May 3, 2024
3b76987
fixed workflow
TheMrSheldon May 3, 2024
5bf192c
create the client-api-key file
TheMrSheldon May 3, 2024
28b3fa7
now
TheMrSheldon May 3, 2024
8c25167
fixed backend unit tests
TheMrSheldon May 6, 2024
7b25ad8
removed the unused index.html view
TheMrSheldon May 6, 2024
b967c2a
Introduced frontend tests to github actions
TheMrSheldon May 6, 2024
353173b
that was silly
TheMrSheldon May 6, 2024
80bba21
Documented the new dev environment
TheMrSheldon May 6, 2024
84e7357
Removed build-docker-image.yml workflow since it is not used currently
TheMrSheldon May 6, 2024
4ab50b5
made devcontainer privileged :(
TheMrSheldon May 6, 2024
8d54c23
reformatted README and added automatic installation of yarn dependencies
TheMrSheldon May 6, 2024
d5cac18
New publish CI for creating a production image of the frontend
TheMrSheldon May 7, 2024
9d43476
move livePreview to port 3080
TheMrSheldon May 7, 2024
5471c11
Merge branch 'development' into split-frontend-backend
TheMrSheldon May 7, 2024
09bd5ec
added launch configurations for frontend and backend
TheMrSheldon May 7, 2024
d74d01f
Updated the documentation
TheMrSheldon May 23, 2024
02845c6
merge
TheMrSheldon May 23, 2024
bba83dd
fixed actions
TheMrSheldon May 23, 2024
52c1314
fixed actions times two
TheMrSheldon May 23, 2024
c3eb6c5
removed unused imports, added todos, removed unused file
TheMrSheldon May 24, 2024
11ee9bd
merged from development
TheMrSheldon May 24, 2024
f81969e
merge
TheMrSheldon May 28, 2024
517f8e3
implemented basic api structure
TheMrSheldon May 28, 2024
afac97e
added /v1/evaluations endpoint and removed /host-list
TheMrSheldon May 28, 2024
691f711
added rudimentary authentication and further endpoints
TheMrSheldon May 29, 2024
2cb15ff
current state
TheMrSheldon May 31, 2024
3b2ee5e
merged
TheMrSheldon Aug 1, 2024
d56248e
applied formatting
TheMrSheldon Aug 1, 2024
dc33c0a
implemented permission handling for the rest api
TheMrSheldon Aug 1, 2024
3e5142a
reverted accidental changes
TheMrSheldon Aug 1, 2024
e2c075a
reverted changes made elsewhere
TheMrSheldon Aug 1, 2024
0790271
added todo notes
TheMrSheldon Aug 1, 2024
79cd890
added health and info endpoint
TheMrSheldon Aug 8, 2024
70d0bff
configured workspace and test-job for vscode; added api_access_matrix…
TheMrSheldon Aug 23, 2024
dbf8de2
added better values in diffir test
TheMrSheldon Aug 23, 2024
fc97bf8
added mixins for startsWith test case assertions
TheMrSheldon Aug 29, 2024
885ea50
fixed python-terrier to 0.10.*
TheMrSheldon Aug 29, 2024
7d5fe6b
fixed permissions of /v1/runs and refactoring
TheMrSheldon Aug 29, 2024
ae036fd
fixed?
TheMrSheldon Aug 29, 2024
40e1316
yay debugging in the CI
TheMrSheldon Aug 29, 2024
e130150
use setup.cfg for application
Aug 29, 2024
82fc014
updated pipeline
Aug 29, 2024
f3b6e82
updated pipeline
Aug 29, 2024
20f5fff
Merge branch 'development' into rest-v1-datasets-api
TheMrSheldon Aug 29, 2024
d0345b9
documented outstanding TODOs in the access matrix
Aug 30, 2024
a298d01
merged
Aug 30, 2024
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
22 changes: 17 additions & 5 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// The optional 'workspaceFolder' property is the path VS Code should open by default when
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

Expand Down Expand Up @@ -50,7 +50,7 @@
// "shutdownAction": "none",

// Uncomment the next line to run commands after the container is created.
"postCreateCommand": "cd frontend; yarn; cd ../application; make setup; make import-mock-data",
"postCreateCommand": "cd frontend; yarn; cd ../application/; pip install -e .[test,dev]; make setup; make import-mock-data",

// Configure tool-specific properties.
"customizations": {
Expand All @@ -68,7 +68,7 @@
"type": "node",
"request": "launch",
"runtimeExecutable": "yarn",
"cwd": "${workspaceFolder}/frontend",
"cwd": "${workspaceFolder:Frontend}/",
"runtimeArgs": [
"dev"
]
Expand All @@ -82,7 +82,18 @@
],
"django": true,
"autoStartBrowser": false,
"program": "${workspaceFolder}/application/src/manage.py"
"program": "${workspaceFolder:Backend}/src/manage.py"
},
{
"name": "TIRA Backend Tests",
"type": "python",
"request": "launch",
"program": "${workspaceFolder:Backend}/src/manage.py",
"cwd": "${workspaceFolder:Backend}/test",
"args": [ "test", "--failfast", "--settings=settings_test" ],
"django": true,
"env": { "PYTHONPATH": ":../src:.", "DJANGO_SETTINGS_MODULE": "settings_test" },
"justMyCode": false
}
],
"compounds": [
Expand All @@ -109,7 +120,8 @@
"ms-python.isort",
"ms-python.black-formatter",
"ms-python.flake8",
"ms-python.mypy-type-checker"
"ms-python.mypy-type-checker",
"42Crunch.vscode-openapi",
]
}
},
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-all-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
run: |
# Create a dummy DISRAPTOR_API_KEY
sudo bash -c 'mkdir -p "/etc/discourse/" && echo "I am so secret" > "/etc/discourse/client-api-key"'
pip3 install -r requirements.txt
pip3 install -e .[dev,test]
make setup
make tests

Expand Down
4 changes: 0 additions & 4 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,9 @@ apt-get update && apt-get install -y python3 python3-pip python3-dev pkg-config
libpcre3-dev
pip3 install black flake8 isort mypy
EOF
COPY application/requirements.txt /tmp/application/requirements.txt
RUN <<EOF
# Create a dummy secret
mkdir -p "/etc/discourse/" && echo "I am so secret" > "/etc/discourse/client-api-key"
# Setup the application
cd /tmp/application
pip3 install -r requirements.txt
EOF

########################################################################################################################
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<img alt="Current Release" src="https://img.shields.io/github/release/tira-io/tira.svg"/>
</a>
<a href="https://tira.io">
<img alt="Deployment" src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.tira.io%2Fapi%2Fv1%2F&query=%24.version&prefix=v.&label=tira.io"/>
<img alt="Deployment" src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.tira.io%2Finfo&query=%24.version&prefix=v.&label=tira.io"/>
</a>
<a href="https://github.com/tira-io/tira/actions/workflows/run-all-tests.yml">
<img alt="Tests" src="https://github.com/tira-io/tira/actions/workflows/run-all-tests.yml/badge.svg"/>
Expand Down
30 changes: 0 additions & 30 deletions application/requirements.txt

This file was deleted.

45 changes: 45 additions & 0 deletions application/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,3 +1,48 @@

[options]
python_requires = >=3.9
include_package_data = True
packages = find:
install_requires =
grpcio>=1.53.2
# grpcio-tools==1.36.1 # still needed?
protobuf<4.0dev
pyuwsgi
Django
pyyaml
requests
randomname
tqdm
mysql
mysqlclient
django-webpack-loader==0.6.0
python-gitlab
GitPython
python-slugify
ir-datasets@git+https://github.com/allenai/ir_datasets
diffir@git+https://github.com/mam10eks/diffir
pandas
markdown
PyGithub==1.59.1
ghapi
django-extensions
discourse-client-in-disraptor==0.0.8
tira>=0.0.97
huggingface-hub
djangorestframework==3.15.1
django-filter==24.2
djangorestframework-jsonapi==7.0.0

[options.extras_require]
test =
mockito
parameterized
approvaltests==7.3.0
dev =
coverage
coverage-badge


[flake8]
max-line-length = 120
extend-ignore = E203
Expand Down
9 changes: 8 additions & 1 deletion application/src/django_admin/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@

INSTALLED_APPS = [
"tira.apps.TiraConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_filters",
"rest_framework",
"rest_framework_json_api",
"webpack_loader",
]

Expand All @@ -89,6 +91,11 @@
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]

REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ("tira.authentication.TrustedHeaderAuthentication",),
"DEFAULT_FILTER_BACKENDS": ("rest_framework_json_api.django_filters.DjangoFilterBackend",),
}

ROOT_URLCONF = "django_admin.urls"

TEMPLATES = [
Expand Down
2 changes: 0 additions & 2 deletions application/src/django_admin/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
path("admin/", admin.site.urls),
path("", include("tira.urls")),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
69 changes: 69 additions & 0 deletions application/src/tira/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,3 +475,72 @@ def user_is_organizer_for_endpoint(


auth = Authentication(authentication_source=settings.DEPLOYMENT)


"""
Trusted Header Authentication implementation to integrate with Django
"""

from typing import NamedTuple

from django.contrib.auth.models import AnonymousUser
from rest_framework import authentication

_DISRAPTOR_APP_SECRET_KEY = os.getenv("DISRAPTOR_APP_SECRET_KEY")


class User(NamedTuple):
username: str
is_staff: bool


class TiraGuest(AnonymousUser):
def __init__(self) -> None:
super().__init__()
self.username = "guest"
self._groups: list[str] = []
self.is_staff = False

def __str__(self) -> str:
return self.username

@property
def is_anonymous(self):
return True

@property
def is_authenticated(self):
return False


class TiraUser(AnonymousUser):
def __init__(self, username: str, groups: list[str]) -> None:
super().__init__()
self.username = username
self._groups = groups
self.is_staff = "admins" in groups or "tira_reviewer" in groups

def __str__(self) -> str:
return self.username

@property
def is_anonymous(self):
return False

@property
def is_authenticated(self):
return True


class TrustedHeaderAuthentication(authentication.BaseAuthentication):

def authenticate(self, request) -> tuple[User, None]:
if not request.headers.get("X-Disraptor-App-Secret-Key", None) == _DISRAPTOR_APP_SECRET_KEY:
return HttpResponseNotAllowed("Access forbidden.")
username = request.headers.get("X-Disraptor-User")
groups = request.headers.get("X-Disraptor-Groups")
grouplist = [] if not groups else groups.split(",")
if not username:
return (TiraGuest(), None)

return (TiraUser(username, grouplist), None)
44 changes: 44 additions & 0 deletions application/src/tira/endpoints/misc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
This file contains miscellaneous and **unversioned** endpoints (e.g., the /health or /info).
"""

from django.urls import path
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.request import Request
from rest_framework.response import Response

# TODO: this does not work so I hardcoded for now
# from tira import __version__ as tira_version

tira_version = "0.0.136"
rest_api_version = "v1.0.0-draft"


@api_view(["GET"])
def health_endpoint(request: Request) -> Response:
"""
The /health endpoint returns 2xx on success (currently 204 because we don't respond with any content). It can be
used to check if the REST-API is served.
"""
return Response(status=status.HTTP_204_NO_CONTENT)


@api_view(["GET"])
def info_endpoint(request: Request) -> Response:
"""
The /info endpoint contains general information about the running server (e.g., the version of TIRA that is
running). Do not add any sensitive information to this endpoint as it is **public**!
"""
return Response(
{
"version": tira_version,
"restApiVersion": rest_api_version,
}
)


endpoints = [
path("health", health_endpoint),
path("info", info_endpoint),
]
17 changes: 17 additions & 0 deletions application/src/tira/endpoints/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.urls import include, path

from ._datasets import endpoints as dataset_endpoints
from ._evaluations import endpoints as evaluation_endpoints
from ._organizers import endpoints as organizer_endpoints
from ._runs import endpoints as run_endpoints
from ._tasks import endpoints as task_endpoints
from ._user import endpoints as user_endpoints

endpoints = [
path("datasets/", include(dataset_endpoints)),
path("evaluations/", include(evaluation_endpoints)),
path("organizers/", include(organizer_endpoints)),
path("runs/", include(run_endpoints)),
path("tasks/", include(task_endpoints)),
path("user/", include(user_endpoints)),
]
43 changes: 43 additions & 0 deletions application/src/tira/endpoints/v1/_datasets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.urls import path
from rest_framework import pagination
from rest_framework.permissions import IsAdminUser
from rest_framework.serializers import CharField, ModelSerializer
from rest_framework_json_api.views import ModelViewSet

from ... import model as modeldb
from ._tasks import TaskSerializer


class DatasetSerializer(ModelSerializer):
id = CharField(source="dataset_id")
default_task = TaskSerializer()

class Meta:
model = modeldb.Dataset
fields = [
"id",
"default_task",
"display_name",
"evaluator",
"is_confidential",
"is_deprecated",
"data_server",
"released",
"default_upload_name",
"created",
"last_modified",
]


class _DatasetView(ModelViewSet):
queryset = modeldb.Dataset.objects.all()
serializer_class = DatasetSerializer
pagination_class = pagination.CursorPagination
lookup_field = "dataset_id"
permission_classes = [IsAdminUser] # TODO: set to something sensible


endpoints = [
path("", _DatasetView.as_view({"get": "list"})),
path("<str:dataset_id>/", _DatasetView.as_view({"get": "retrieve", "delete": "destroy"})),
]
Loading
Loading