Skip to content
Snippets Groups Projects
routes.py 6.47 KiB
Newer Older
# -*- coding: utf-8 -*-
# copyright 2022 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact https://www.logilab.fr -- mailto:contact@logilab.fr
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from typing import Union
from cubicweb import AuthenticationError
from cubicweb.pyramid.core import CubicWebPyramidRequest
from cubicweb.schema_exporters import JSONSchemaExporter
from cubicweb.server.repository import Repository
from pyramid.config import Configurator
from pyramid.request import Request
from pyramid.view import view_config
Frank Bessou's avatar
Frank Bessou committed
from cubicweb_api.api_transaction import ApiTransactionsRepository
from cubicweb_api.httperrors import get_http_error
from cubicweb_api.jwt_auth import setup_jwt
from marshmallow_dataclass import class_schema

from cubicweb_api.request_params import (
    RqlParams,
    get_request_params,
    LoginParams,
    TransactionExecuteParams,
    TransactionParams,
)

log = logging.getLogger(__name__)
API_PATTERN_PREFIX = "v1_"
API_ROUTE_PREFIX = "/api/v1/"
DEFAULT_ROUTE_PARAMS = {
    "request_method": "POST",
    "renderer": "json",
    "require_csrf": False,
def get_cw_request(request: Request) -> CubicWebPyramidRequest:
    return request.cw_request


def get_cw_repo(req_or_conf: Union[Request, Configurator]) -> Repository:
    return req_or_conf.registry["cubicweb.repository"]


def cw_view_config(route_name: str, **kwargs):
    return view_config(
        route_name=f"{API_PATTERN_PREFIX}{route_name}",
        **dict(DEFAULT_ROUTE_PARAMS, **kwargs),
    )


@cw_view_config(route_name="schema", request_method="GET")
def schema_route(request: Request):
    repo = get_cw_repo(request)
    exporter = JSONSchemaExporter()
    try:
        exported_schema = exporter.export_as_dict(repo.schema)
    except Exception as e:
        log.error(e)
        raise get_http_error(400, e.__class__.__name__, str(e))
    else:
        return exported_schema


@cw_view_config(route_name="rql")
def rql_route(request: Request):
    schema = class_schema(RqlParams)()
    request_params: RqlParams = get_request_params(request, schema)
    query = request_params.query
    params = request_params.params
        raise get_http_error(400, "ValidationError", "Query should not be empty.")
    try:
        result = get_cw_request(request).execute(query, params)
        return result.rows
    except Exception as e:
        log.error(e)
        raise get_http_error(400, e.__class__.__name__, str(e))


@cw_view_config(route_name="login")
def login_route(request: Request):
    schema = class_schema(LoginParams)()
    request_params: LoginParams = get_request_params(request, schema)
    login = request_params.login
    pwd = request_params.password

    repo = get_cw_repo(request)
    with repo.internal_cnx() as cnx:
        try:
            cwuser = repo.authenticate_user(cnx, login, password=pwd)
        except AuthenticationError:
            raise get_http_error(
                401, "AuthenticationFailure", "Login and/or password invalid."
            )
        else:
            request.authentication_policy.remember(
                request,
                cwuser.eid,
                login=cwuser.login,
                firstname=cwuser.firstname,
                lastname=cwuser.surname,
            )
            request.response.status_code = 204
@cw_view_config(route_name="transaction/begin")
def transaction_begin_route(request: Request):
    transactions = get_cw_repo(request).api_transactions
    user = get_cw_request(request).user
    return transactions.begin_transaction(user)


@cw_view_config(route_name="transaction/execute")
def transaction_execute_route(request: Request):
    transactions = get_cw_repo(request).api_transactions
    schema = class_schema(TransactionExecuteParams)()
    params: TransactionExecuteParams = get_request_params(request, schema)
    try:
        result = transactions[params.uuid].execute(params.query, params.params)
        return result.rows
    except Exception as e:
        log.error(e)
        raise get_http_error(400, e.__class__.__name__, str(e))
@cw_view_config(route_name="transaction/commit")
def transaction_commit_route(request: Request):
    transactions = get_cw_repo(request).api_transactions
    schema = class_schema(TransactionParams)()
    params: TransactionParams = get_request_params(request, schema)
    uuid = params.uuid
    try:
        commit_result = transactions[uuid].commit()
        transactions[uuid].rollback()
        return commit_result
    except Exception as e:
        log.error(e)
        raise get_http_error(400, e.__class__.__name__, str(e))
@cw_view_config(route_name="transaction/rollback")
def transaction_rollback_route(request: Request):
    transactions = get_cw_repo(request).api_transactions
    schema = class_schema(TransactionParams)()
    params: TransactionParams = get_request_params(request, schema)
    uuid = params.uuid
    try:
        rollback_result = transactions[uuid].rollback()
        transactions.end_transaction(uuid)
        return rollback_result
    except Exception as e:
        log.error(e)
        raise get_http_error(400, e.__class__.__name__, str(e))


@cw_view_config(route_name="help", request_method="GET")
def help_route(_request: Request):
    """TO IMPLEMENT"""
    return


def add_route(config: Configurator, name: str):
    config.add_route(f"{API_PATTERN_PREFIX}{name}", f"/{name}")


def add_cw_routes(config: Configurator):
    add_route(config, "schema")
    add_route(config, "rql")
    add_route(config, "login")
    add_route(config, "transaction/begin")
    add_route(config, "transaction/execute")
    add_route(config, "transaction/commit")
    add_route(config, "transaction/rollback")
    add_route(config, "help")
    config.scan()
def includeme(config: Configurator):
    repo = get_cw_repo(config)
Frank Bessou's avatar
Frank Bessou committed
    repo.api_transactions = ApiTransactionsRepository(repo)
    config.include(add_cw_routes, route_prefix=API_ROUTE_PREFIX)