Skip to content
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
4 changes: 1 addition & 3 deletions aiida_restapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"""AiiDA REST API for data queries and workflow managment."""
"""AiiDA REST API for data queries and workflow management."""

__version__ = '0.1.0a1'

from .main import app # noqa: F401
File renamed without changes.
31 changes: 31 additions & 0 deletions aiida_restapi/cli/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os

import click
import uvicorn


@click.group()
def cli() -> None:
"""AiiDA REST API management CLI."""


@cli.command()
@click.option('--host', default='127.0.0.1', show_default=True)
@click.option('--port', default=8000, show_default=True, type=int)
@click.option('--read-only', is_flag=True)
@click.option('--watch', is_flag=True)
def start(read_only: bool, watch: bool, host: str, port: int) -> None:
"""Start the AiiDA REST API service."""

os.environ['AIIDA_RESTAPI_READ_ONLY'] = '1' if read_only else '0'

click.echo(f'Starting REST API (read_only={read_only}, watch={watch}) on {host}:{port}')

uvicorn.run(
'aiida_restapi.main:create_app',
host=host,
port=port,
reload=watch,
reload_dirs=['aiida_restapi'],
factory=True,
)
Empty file.
19 changes: 19 additions & 0 deletions aiida_restapi/common/pagination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Pagination utilities."""

from __future__ import annotations

import typing as t

import pydantic as pdt
from aiida.orm import Entity

ResultType = t.TypeVar('ResultType', bound=Entity.Model)

__all__ = ('PaginatedResults',)


class PaginatedResults(pdt.BaseModel, t.Generic[ResultType]):
total: int
page: int
page_size: int
results: list[ResultType]
90 changes: 90 additions & 0 deletions aiida_restapi/common/query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""REST API query utilities."""

from __future__ import annotations

import json
import typing as t

import pydantic as pdt
from fastapi import HTTPException, Query


class QueryParams(pdt.BaseModel):
filters: dict[str, t.Any] = pdt.Field(
default_factory=dict,
description='AiiDA QueryBuilder filters',
examples=[
{'node_type': {'==': 'data.core.int.Int.'}},
{'attributes.value': {'>': 42}},
],
)
order_by: str | list[str] | dict[str, t.Any] | None = pdt.Field(
None,
description='Fields to sort by',
examples=[
{'attributes.value': 'desc'},
],
)
page_size: pdt.PositiveInt = pdt.Field(
10,
description='Number of results per page',
examples=[10],
)
page: pdt.PositiveInt = pdt.Field(
1,
description='Page number',
examples=[1],
)


def query_params(
filters: str | None = Query(
None,
description='AiiDA QueryBuilder filters as JSON string',
),
order_by: str | None = Query(
None,
description='Comma-separated list of fields to sort by',
),
page_size: pdt.PositiveInt = Query(
10,
description='Number of results per page',
),
page: pdt.PositiveInt = Query(
1,
description='Page number',
),
) -> QueryParams:
"""Parse query parameters into a structured object.

:param filters: AiiDA QueryBuilder filters as JSON string.
:param order_by: Comma-separated string of fields to sort by.
:param page_size: Number of results per page.
:param page: Page number.
:return: Structured query parameters.
:raises HTTPException: If filters cannot be parsed as JSON.
"""
query_filters: dict[str, t.Any] = {}
query_order_by: str | list[str] | dict[str, t.Any] | None = None
if filters:
try:
query_filters = json.loads(filters)
except Exception as exception:
raise HTTPException(
status_code=400,
detail=f'Could not parse filters as JSON: {exception}',
) from exception
if order_by:
try:
query_order_by = json.loads(order_by)
except Exception as exception:
raise HTTPException(
status_code=400,
detail=f'Could not parse order_by as JSON: {exception}',
) from exception
return QueryParams(
filters=query_filters,
order_by=query_order_by,
page_size=page_size,
page=page,
)
13 changes: 13 additions & 0 deletions aiida_restapi/common/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Common type variables."""

from __future__ import annotations

import typing as t

from aiida import orm

EntityType = t.TypeVar('EntityType', bound='orm.Entity')
EntityModelType = t.TypeVar('EntityModelType', bound='orm.Entity.Model')

NodeType = t.TypeVar('NodeType', bound='orm.Node')
NodeModelType = t.TypeVar('NodeModelType', bound='orm.Node.Model')
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 import __version__

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

API_CONFIG = {
'PREFIX': '/api/v0',
'VERSION': __version__,
}
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import graphene as gr
from aiida.orm import Comment

from aiida_restapi.filter_syntax import parse_filter_str
from aiida_restapi.graphql.filter_syntax import parse_filter_str

from .orm_factories import (
ENTITY_DICT_TYPE,
Expand Down
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/computers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import graphene as gr
from aiida.orm import Computer

from aiida_restapi.filter_syntax import parse_filter_str
from aiida_restapi.graphql.filter_syntax import parse_filter_str
from aiida_restapi.graphql.plugins import QueryPlugin

from .nodes import NodesQuery
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

from lark import Lark, Token, Tree

from . import static
from .utils import parse_date
from .. import static
from ..utils import parse_date

FILTER_GRAMMAR = resources.open_text(static, 'filter_grammar.lark')

Expand Down
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import graphene as gr
from aiida.orm import Group

from aiida_restapi.filter_syntax import parse_filter_str
from aiida_restapi.graphql.filter_syntax import parse_filter_str
from aiida_restapi.graphql.nodes import NodesQuery
from aiida_restapi.graphql.plugins import QueryPlugin

Expand Down
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import graphene as gr
from aiida.orm import Log

from aiida_restapi.filter_syntax import parse_filter_str
from aiida_restapi.graphql.filter_syntax import parse_filter_str

from .orm_factories import (
ENTITY_DICT_TYPE,
Expand Down
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import graphene as gr
from aiida import orm

from aiida_restapi.filter_syntax import parse_filter_str
from aiida_restapi.graphql.filter_syntax import parse_filter_str
from aiida_restapi.graphql.plugins import QueryPlugin

from .comments import CommentsQuery
Expand Down
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/orm_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from graphql import GraphQLError
from pydantic import Json

from aiida_restapi.aiida_db_mappings import ORM_MAPPING, get_model_from_orm
from aiida_restapi.graphql.aiida_db_mappings import ORM_MAPPING, get_model_from_orm

from .config import ENTITY_LIMIT
from .utils import JSON, selected_field_names_naive
Expand Down
2 changes: 1 addition & 1 deletion aiida_restapi/graphql/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import graphene as gr
from aiida.orm import User

from aiida_restapi.filter_syntax import parse_filter_str
from aiida_restapi.graphql.filter_syntax import parse_filter_str

from .nodes import NodesQuery
from .orm_factories import (
Expand Down
Loading