Newer
Older
# 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/>.

François Ferry
committed
import logging

François Ferry
committed
from cubicweb import AuthenticationError
from cubicweb.schema_exporters import JSONSchemaExporter
from pyramid.config import Configurator
from pyramid.request import Request

Arnaud Vergnet
committed
from pyramid.response import Response

François Ferry
committed
from pyramid.view import view_config, view_defaults
from cubicweb_api.api_transaction import ApiTransactionsRepository

François Ferry
committed
from cubicweb_api.auth.jwt_auth import setup_jwt

François Ferry
committed
from cubicweb_api.httperrors import get_http_error
from cubicweb_api.openapi.openapi import setup_openapi
from cubicweb_api.util import get_cw_repo, get_transactions_repository
log = logging.getLogger(__name__)
class ApiRoutes(Enum):
"""
All the available routes as listed in the openapi/openapi_template.yml file.
"""
schema = "schema"
rql = "rql"
login = "login"
transaction_begin = "transaction/begin"
transaction_execute = "transaction/execute"
transaction_commit = "transaction/commit"
transaction_rollback = "transaction/rollback"
help = "help"
def get_route_name(route_name: ApiRoutes) -> str:
"""
Generates a unique route name using the api prefix to prevent clashes with routes
from other cubes.
:param route_name: The route name base
:return: The generated route name
"""
return f"{API_ROUTE_NAME_PREFIX}{route_name.value}"

François Ferry
committed
@view_defaults(
request_method="POST",
renderer="cubicweb_api_json",
require_csrf=False,
openapi=True,

François Ferry
committed
use_api_exceptions=True,

François Ferry
committed
class ApiViews:
def __init__(self, request: Request):
self.request = request

François Ferry
committed
@view_config(

François Ferry
committed
route_name=get_route_name(ApiRoutes.schema),
request_method="GET",
anonymous_or_connected=True,

François Ferry
committed
)
def schema_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
repo = get_cw_repo(self.request)
exporter = JSONSchemaExporter()
exported_schema = exporter.export_as_dict(repo.schema)
return exported_schema

François Ferry
committed
@view_config(route_name=get_route_name(ApiRoutes.rql), anonymous_or_connected=True)

François Ferry
committed
def rql_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
request_params = self.request.openapi_validated.body
query: str = request_params["query"]
params: dict = request_params["params"]

François Ferry
committed
return self.request.cw_cnx.execute(query, params).rows

François Ferry
committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@view_config(
route_name=get_route_name(ApiRoutes.login),
)
def login_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
request_params = self.request.openapi_validated.body
login: str = request_params["login"]
pwd: str = request_params["password"]
repo = get_cw_repo(self.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:
headers = self.request.authentication_policy.remember(
self.request,
cwuser.eid,
login=cwuser.login,
firstname=cwuser.firstname,
lastname=cwuser.surname,
)
return Response(headers=headers, status=204)

François Ferry
committed
@view_config(

François Ferry
committed
route_name=get_route_name(ApiRoutes.current_user),
request_method="GET",
anonymous_or_connected=True,

François Ferry
committed
)
def current_user(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
user = self.request.cw_cnx.user
return {"eid": user.eid, "login": user.login, "dcTitle": user.dc_title()}

François Ferry
committed
@view_config(
route_name=get_route_name(ApiRoutes.transaction_begin),

François Ferry
committed
anonymous_or_connected=True,

François Ferry
committed
)
def transaction_begin_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
transactions = get_transactions_repository(self.request)
user = self.request.cw_cnx.user
return transactions.begin_transaction(user)

François Ferry
committed
@view_config(
route_name=get_route_name(ApiRoutes.transaction_execute),

François Ferry
committed
anonymous_or_connected=True,

François Ferry
committed
)
def transaction_execute_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
transactions = get_transactions_repository(self.request)
request_params = self.request.openapi_validated.body
uuid: str = request_params["uuid"]
query: str = request_params["query"]
params: dict = request_params["params"]

François Ferry
committed
return transactions[uuid].execute(query, params).rows

François Ferry
committed
@view_config(
route_name=get_route_name(ApiRoutes.transaction_commit),

François Ferry
committed
anonymous_or_connected=True,

François Ferry
committed
)
def transaction_commit_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
transactions = get_transactions_repository(self.request)
request_params = self.request.openapi_validated.body
uuid: str = request_params["uuid"]

François Ferry
committed
return transactions[uuid].commit()

François Ferry
committed
@view_config(
route_name=get_route_name(ApiRoutes.transaction_rollback),

François Ferry
committed
anonymous_or_connected=True,

François Ferry
committed
)
def transaction_rollback_route(self):
"""
See the openapi/openapi_template.yml file for more information on this route.
"""
transactions = get_transactions_repository(self.request)
request_params = self.request.openapi_validated.body
uuid: str = request_params["uuid"]
rollback_result = transactions[uuid].rollback()
transactions.end_transaction(uuid)
return rollback_result
def includeme(config: Configurator):
repo = get_cw_repo(config)
repo.api_transactions = ApiTransactionsRepository(repo)
config.pyramid_openapi3_register_routes()
config.scan()