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

Add /server/* endpoint group to REST API #76

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
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 importlib.metadata 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': version('aiida_restapi'), # prefix for all URLs
'VERSION': '0.1.0a',
}
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():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for? it's not used, right?

"""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
Loading