Skip to content

Commit

Permalink
Add /server/* endpoint group to REST API
Browse files Browse the repository at this point in the history
This includes the implementation of the endpoints `/server/info` and
`/server/endpoints`. There are some slight changes over the old REST
API:

* The endpoint `/server/` has been moved to `/server/info`
* The key name in the response of endpoint `/server/endpoints` has been changed
  from `available_endpoints` to `endpoints`
* The strings in the response of endpoint `/server/endpoints` has been
  splitted up into a dictionary with keys "methods", "group", "path" that
  contain the same information as the strings but structured
* Updated API version to the version of the repo. This required a new
  file to prevent circular imports
  • Loading branch information
agoscinski committed Nov 21, 2024
1 parent 366f036 commit 0458868
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 23 deletions.
4 changes: 3 additions & 1 deletion aiida_restapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""AiiDA REST API for data queries and workflow managment."""

__version__ = '0.1.0a1'
from aiida_restapi._version import VERSION

__version__ = VERSION

from .main import app # noqa: F401
1 change: 1 addition & 0 deletions aiida_restapi/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VERSION = '0.1.0a1'
7 changes: 7 additions & 0 deletions aiida_restapi/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Configuration of API"""

from aiida_restapi._version import VERSION

# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = '09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7'
Expand All @@ -20,3 +22,8 @@

# The chunks size for streaming data for download
DOWNLOAD_CHUNK_SIZE = 1024

API_CONFIG = {
'PREFIX': '', # prefix for all URLs
'VERSION': VERSION,
}
33 changes: 32 additions & 1 deletion aiida_restapi/main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""Declaration of FastAPI application."""

from typing import Any

from fastapi import FastAPI

from aiida_restapi.config import API_CONFIG
from aiida_restapi.graphql import main
from aiida_restapi.routers import auth, computers, daemon, groups, nodes, process, users
from aiida_restapi.routers import auth, computers, daemon, groups, nodes, process, server, users

app = FastAPI()
app.include_router(auth.router)
Expand All @@ -14,3 +17,31 @@
app.include_router(users.router)
app.include_router(process.router)
app.add_route('/graphql', main.app, name='graphql')


# We need to create this endpoint here instead of in the server to avoid circular imports, since it is dependent on the
# app
@server.router.get('/server/endpoints', response_model=dict[str, Any])
async def get_server_endpoints() -> dict[str, Any]:
"""List available routes"""
import re

from fastapi.routing import APIRoute

routes = []
for route in app.routes:
if isinstance(route, APIRoute):
full_path = API_CONFIG['PREFIX'] + route.path
match = re.search(r'^\/([^\/]+)', route.path)
endpoint_group = None if match is None else match.group(1)
routes.append(
{
'path': full_path,
'group': endpoint_group,
'methods': route.methods,
}
)
return {'endpoints': routes}


app.include_router(server.router)
26 changes: 26 additions & 0 deletions aiida_restapi/routers/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Declaration of FastAPI application."""

from typing import Any

from aiida import __version__ as aiida_version
from fastapi import APIRouter

from aiida_restapi.config import API_CONFIG

router = APIRouter()


@router.get('/server/info', response_model=dict[str, Any])
async def get_server_info() -> dict[str, Any]:
"""Get the API version information"""
response = {}
api_version = API_CONFIG['VERSION'].split('.')
response['API_major_version'] = api_version[0]
response['API_minor_version'] = api_version[1]
response['API_revision_version'] = api_version[2]
# Add Rest API prefix
response['API_prefix'] = API_CONFIG['PREFIX']

# Add AiiDA version
response['AiiDA_version'] = aiida_version
return response
49 changes: 28 additions & 21 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,32 +317,39 @@ def _func(
return _func


def _create_node(
*,
label: str = '',
description: str = '',
attributes: Optional[dict] = None,
extras: Optional[dict] = None,
process_type: Optional[str] = None,
computer: Optional[orm.Computer] = None,
store: bool = True,
) -> orm.nodes.Node:
node = orm.CalcJobNode(computer=computer) if process_type else orm.Data(computer=computer)
node.label = label
node.description = description
node.base.attributes.reset(attributes or {})
node.base.extras.reset(extras or {})
if process_type is not None:
node.process_type = process_type
if store:
node.store()
return node


@pytest.fixture
def create_node():
"""Create and store an AiiDA Node."""
return _create_node

def _func(
*,
label: str = '',
description: str = '',
attributes: Optional[dict] = None,
extras: Optional[dict] = None,
process_type: Optional[str] = None,
computer: Optional[orm.Computer] = None,
store: bool = True,
) -> orm.nodes.Node:
node = orm.CalcJobNode(computer=computer) if process_type else orm.Data(computer=computer)
node.label = label
node.description = description
node.base.attributes.reset(attributes or {})
node.base.extras.reset(extras or {})
if process_type is not None:
node.process_type = process_type
if store:
node.store()
return node

return _func
@pytest.fixture
def calcjob_nodes():
"""Populate database with calcjob nodes."""
nodes = [_create_node(label=f'node {i}', process_type='', store=False) for i in range(4)]
return nodes


@pytest.fixture
Expand Down

0 comments on commit 0458868

Please sign in to comment.