"""
Describes an endpoint where detailed information about the service can be
obtained
"""
from datetime import datetime
from flask import Response, jsonify
from topchef.api.abstract_endpoints import AbstractEndpointForService
from topchef.api.abstract_endpoints import AbstractEndpointForServiceMeta
from topchef.models import Service
from topchef.models.errors import RequestNotJSONError
from topchef.models.errors import DeserializationError
from topchef.serializers import JSONSchema
from topchef.serializers import ServiceDetail as ServiceSerializer
from topchef.serializers import ServiceModification as ModifyServiceSerializer
[docs]class ServiceDetail(AbstractEndpointForService):
"""
Describes the endpoint that returns the details for a particular service
"""
[docs] def get(self, service: Service) -> Response:
r"""
Return details for a given service
.. :quickref: Service; Get service data
**Example Response**
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"description": "A quick testing service",
"has_timed_out": true,
"id": "495d76fd-044c-4f02-8815-5ec6e7634330",
"is_service_available": false,
"job_registration_schema": {
"type": "object"
},
"job_result_schema": {
"type": "object"
},
"jobs": [
{
"date_submitted": "2017-08-15T18:29:07.902093+00:00",
"id": "42094fe4-9c71-4d6e-94fd-7ed6e2b46ce7",
"status": "REGISTERED"
}
],
"name": "Testing Service",
"timeout": 30
},
"links": {
"self":
"/services/495d76fd-044c-4f02-8815-5ec6e7634330"
},
"meta": {
"service_schema": {
"$schema":
"http://json-schema.org/draft-04/schema#",
"description":
"A comprehensive schema for services",
"properties": {
"description": {
"readonly": true,
"title": "description",
"type": "string"
},
"has_timed_out": {
"readonly": true,
"title": "has_timed_out",
"type": "boolean"
},
"id": {
"format": "uuid",
"readonly": true,
"title": "id",
"type": "string"
},
"is_service_available": {
"readonly": true,
"title": "is_service_available",
"type": "boolean"
},
"job_registration_schema": {
"readonly": true,
"title": "job_registration_schema",
"type": "object"
},
"job_result_schema": {
"readonly": true,
"title": "job_result_schema",
"type": "object"
},
"jobs": {
"items": {
"type": "object"
},
"type": "array"
},
"name": {
"readonly": true,
"title": "name",
"type": "string"
},
"timeout": {
"readonly": true,
"title": "timeout",
"type": "string"
}
},
"required": [
"description",
"has_timed_out",
"id",
"is_service_available",
"job_registration_schema",
"job_result_schema",
"name",
"timeout"
],
"title": "Detailed Service Schema",
"type": "object"
}
}
}
:statuscode 200: The request completed successfully
:statuscode 404: A service with the ID was not found
:param service: The service for which a response is to be retrieved
:return: A flask response with the appropriate data
"""
return self._get_detailed_response_for_service(service)
[docs] def patch(self, service: Service) -> Response:
"""
Change the mutable parameters of the service
.. :quickref: Service; Change service parameters or check in
**Example Request**
.. sourcecode:: http
PATCH /services/<service_id> HTTP/1.1
Content-Type: application/json
{
"is_available": false,
"name": "Changed name",
"description": "Changed description",
"timeout": 20
}
**Example Response**
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"description": "A quick testing service",
"has_timed_out": true,
"id": "495d76fd-044c-4f02-8815-5ec6e7634330",
"is_service_available": false,
"job_registration_schema": {
"type": "object"
},
"job_result_schema": {
"type": "object"
},
"jobs": [
{
"date_submitted": "2017-08-15T18:29:07.902093+00:00",
"id": "42094fe4-9c71-4d6e-94fd-7ed6e2b46ce7",
"status": "REGISTERED"
}
],
"name": "Testing Service",
"timeout": 30
}
:statuscode 200: The request completed successfully
:statuscode 400: If an attempt is made to provide JSON as a request
body, and the JSON is either syntactically or semantically
incorrect.
:statuscode 404: A service with that ID was not found in the database
:param service: The service to patch
:return: Reset the service's timeout
"""
service.check_in()
try:
request_body = self.request_json
except RequestNotJSONError:
return self._handle_request_not_json()
else:
return self._handle_service_modification(request_body, service)
@staticmethod
def _handle_request_not_json() -> Response:
response = jsonify({
'meta':
"Service checked in at %s" % datetime.utcnow().isoformat()
})
response.status_code = 200
return response
def _handle_service_modification(
self, request_body: dict, service: Service
) -> Response:
serializer = ModifyServiceSerializer()
deserialized_body, errors = serializer.load(request_body)
if errors:
self._report_deserialization_errors(errors)
raise self.Abort()
self._modify_service(deserialized_body, service)
return self._get_detailed_response_for_service(service)
def _report_deserialization_errors(self, errors: dict):
self.errors.extend(
(DeserializationError(key, errors[key]) for key in errors.keys())
)
def _get_detailed_response_for_service(self, service: Service) -> Response:
serializer = ServiceSerializer()
serializer_schema = JSONSchema(
title='Detailed Service Schema',
description='A comprehensive schema for displaying services'
)
response = jsonify({
'data': serializer.dump(service, many=False).data,
'meta': {'service_schema': serializer_schema.dump(serializer)},
'links': {'self': self.self_url(service)}
})
response.status_code = 200
return response
@staticmethod
def _modify_service(request_body: dict, service: Service) -> None:
request_body_keys = request_body.keys()
if 'is_available' in request_body_keys:
service.is_service_available = request_body['is_available']
if 'description' in request_body_keys:
service.description = request_body['description']
if 'name' in request_body_keys:
service.name = request_body['name']
if 'timeout' in request_body_keys:
service.timeout = request_body['timeout']
[docs]class ServiceDetailForServiceID(
ServiceDetail, metaclass=AbstractEndpointForServiceMeta
):
"""
Contains a mixin to help the web endpoints parse ``service_id`` string
as they come raw from the HTTP endpoint
"""