Source code for topchef.api.jobs_for_service

"""
Maps the ``/services/<service_id>/jobs`` endpoint
"""
from flask import Response, jsonify, url_for, Request, request
from topchef.api.abstract_endpoints import AbstractEndpointForService
from topchef.api.abstract_endpoints import AbstractEndpointForServiceMeta
from topchef.api.job_detail import JobDetailForJobID as JobDetail
from topchef.models import Service, ServiceList
from topchef.models.errors import DeserializationError, ValidationError
from topchef.serializers import JSONSchema
from topchef.serializers import JobDetail as JobDetailSerializer
from topchef.serializers.new_job import NewJob as NewJobSerializer
from jsonschema import Draft4Validator as JSONSchemaValidator
from typing import Iterable, Optional
from jsonschema import ValidationError as JSONSchemaError
from sqlalchemy.orm import Session


[docs]class JobsForServiceEndpoint(AbstractEndpointForService): """ Describes the endpoint. A ``GET`` request to this endpoint returns all the jobs registered for a particular service. A ``POST`` request to this endpoint will allow the user to create new jobs for the service. A ``PATCH`` request here will reset the time since the service was last polled """ def __init__( self, session: Session, flask_request: Request=request, service_list: Optional[ServiceList]=None, validator_factory: Optional[type]=None ): super(JobsForServiceEndpoint, self).__init__( session, flask_request, service_list=service_list ) if validator_factory is None: self._validator_factory = JSONSchemaValidator else: self._validator_factory = validator_factory
[docs] def get(self, service: Service) -> Response: """ Get the list of jobs available for a service .. :quickref: Service; Get jobs for the service **Example Response** .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "data": [ { "date_submitted": "2017-08-15T18:29:07.902093+00:00", "id": "42094fe4-9c71-4d6e-94fd-7ed6e2b46ce7", "parameters": { "foo": "bar" }, "results": { "foo": "bar" }, "status": "REGISTERED" } ], "links": { "self": "http://localhost:5000/services/495d76fd-044c-4f02-8815-5ec6e7634330/jobs" }, "meta": { "data_schema": { "$schema": "http://json-schema.org/draft-04/schema#", "description": "The schema for reading data contained in the data key of this response", "items": { "$schema": "http://json-schema.org/draft-04/schema#", "description": "The schema for reading data contained in the data key of this response", "properties": { "date_submitted": { "format": "date-time", "title": "date_submitted", "type": "string" }, "id": { "format": "uuid", "title": "id", "type": "string" }, "parameters": { "title": "parameters", "type": "object" }, "results": { "title": "results", "type": "object" }, "status": { "enum": [ "REGISTERED", "WORKING", "COMPLETED", "ERROR" ], "type": "string" } }, "required": [ "id", "parameters", "results", "status" ], "title": "Data Schema", "type": "object" }, "title": "Data Schema", "type": "array" }, "new_job_schema": { "$schema": "http://json-schema.org/draft-04/schema#", "description": "The schema that a POST request must satisfy in order to create a new job", "properties": { "parameters": { "type": "object" } }, "title": "New Job Schema", "type": "object" } } } :statuscode 200: The request completed successfully :statuscode 404: A service with that ID could not be found :param service: The service for which jobs are to be retrieved :return: A flask response containing the data to display to the user """ serializer = JobDetailSerializer() response = jsonify({ 'data': serializer.dump(service.jobs, many=True).data, 'meta': { 'new_job_schema': self._new_job_schema(service), 'data_schema': self._data_schema }, 'links': {'self': self.self_url(service)} }) response.status_code = 200 return response
[docs] def post(self, service: Service) -> Response: """ Create a new job. The request must satisfy the schema specified in the key ``meta/new_job_schema``. A request indicating a successful response will also include a ``Location`` header indicating where the new job is located. .. :quickref: Service; create a new job **Example Request** .. sourcecode:: http POST /services/668ac2ea-063d-4122-ba7a-97a3e8e46a8a/jobs HTTP/1.1 Content-Type: application/json { "parameters": { "experiment_type": "RABI", "wait_time": 500e-9 } } **Example Response** .. sourcecode:: http HTTP/1.1 201 CREATED Content-Type: application/json Location: http://localhost:5000/jobs/42094fe4-9c71-4d6e-94fd-7ed6e2b46ce7 { "data": { "date_submitted": "2017-08-15T18:29:07.902093+00:00", "id": "42094fe4-9c71-4d6e-94fd-7ed6e2b46ce7", "parameters": { "experiment_type": "RABI", "wait_time": 500e-9, }, "results": null, "status": "REGISTERED" } "meta": "new job ID is b0b58425-165f-4add-97b0-86da6b38757f" } :statuscode 201: The job was successfully created :statuscode 400: The job could not be created. :statuscode 404: A service with the ID could not be found :param service: The service for which the new job is to be made :return: A flask response with the appropriate response as per the documentation in this endpoint """ data, errors = NewJobSerializer().load(self.request_json) if errors: self.errors.extend( DeserializationError(key, errors[key]) for key in errors.keys() ) raise self.Abort() validator = self._validator_factory(service.job_registration_schema) if not validator.is_valid(data['parameters']): self._report_json_schema_errors( validator.iter_errors(data['parameters']) ) raise self.Abort() new_job = service.new_job(data['parameters']) job_data_serializer = JobDetailSerializer() response = jsonify({ 'data': job_data_serializer.dump(new_job).data, 'meta': 'new job ID is %s' % new_job.id }) response.status_code = 201 response.headers['Location'] = url_for( JobDetail.__name__, job_id=new_job.id, _external=True ) return response
@staticmethod def _new_job_schema(service: Service) -> dict: json_schema = JSONSchema( title='New Job Schema', description='The schema that a POST request must satisfy in ' 'order to create a new job') schema = { 'title': json_schema.title, 'description': json_schema.description, '$schema': json_schema.schema, 'type': 'object', 'properties': { 'parameters': service.job_registration_schema } } return schema @property def _data_schema(self) -> dict: json_schema = JSONSchema( title='Data Schema', description='The schema for reading data contained in the data ' 'key of this response' ) schema = { '$schema': json_schema.schema, 'title': json_schema.title, 'description': json_schema.description, 'type': 'array', 'items': json_schema.dump(JobDetailSerializer()) } return schema def _report_json_schema_errors( self, errors: Iterable[JSONSchemaError] ) -> None: self.errors.extend(ValidationError(error) for error in errors)
[docs]class JobsForServiceID( JobsForServiceEndpoint, metaclass=AbstractEndpointForServiceMeta ): """ Endpoint that maps the web endpoints defined in the superclass to match service ids """