Beispiel #1
0
def items_put_check_support(id_, class_path, path, is_collection):
    """Check if class_type supports PUT operation"""
    object_ = json.loads(request.data.decode('utf-8'))
    collections, parsed_classes = get_collections_and_parsed_classes()
    if path in parsed_classes:
        class_path = path
        obj_type = getType(path, "PUT")
    elif path in collections:
        collection = collections[path]["collection"]
        class_path = collection.path
        obj_type = collection.name
    link_props, link_type_check = get_link_props(class_path, object_)
    # Load new object and type
    if (validate_object(object_, obj_type, class_path) and link_type_check):
        if is_collection:
            object_ = parse_collection_members(object_)
        try:
            # Add the object with given ID
            object_id = crud.insert(object_=object_, id_=id_,
                                    session=get_session(), collection=is_collection)
            headers_ = [{"Location": f"{get_hydrus_server_url()}"
                                     f"{get_api_name()}/{path}/{object_id}"}]
            status_description = f"Object with ID {object_id} successfully added"
            status = HydraStatus(code=201, title="Object successfully added.",
                                 desc=status_description)
            return set_response_headers(
                jsonify(status.generate()), headers=headers_,
                status_code=status.code)
        except (ClassNotFound, InstanceExists, PropertyNotFound) as e:
            error = e.get_HTTP()
            return error_response(error)
    else:
        error = HydraError(code=400, title="Data is not valid")
        return error_response(error)
Beispiel #2
0
def items_delete_check_support(id_, class_type, path, is_collection):
    """Check if class_type supports PUT operation"""
    try:
        # Delete the Item with ID == id_
        # for colletions, id_ is corresponding to their collection_id and not the id_
        # primary key
        crud.delete(id_,
                    class_type,
                    session=get_session(),
                    collection=is_collection)
        method = "DELETE"
        resource_url = f"{get_hydrus_server_url()}{get_api_name()}/{path}/{id_}"
        last_job_id = crud.get_last_modification_job_id(session=get_session())
        new_job_id = crud.insert_modification_record(method,
                                                     resource_url,
                                                     session=get_session())
        send_sync_update(socketio=socketio,
                         new_job_id=new_job_id,
                         last_job_id=last_job_id,
                         method=method,
                         resource_url=resource_url)
        status_description = f"Object with ID {id_} successfully deleted"
        status = HydraStatus(code=200,
                             title="Object successfully deleted.",
                             desc=status_description)
        return set_response_headers(jsonify(status.generate()))

    except (ClassNotFound, InstanceNotFound) as e:
        error = e.get_HTTP()
        return error_response(error)
Beispiel #3
0
def items_post_check_support(id_, object_, class_path, path, is_collection):
    """Check if class_type supports POST operation"""
    collections, parsed_classes = get_collections_and_parsed_classes()
    if path in parsed_classes:
        class_path = path
        obj_type = getType(path, "PUT")
    elif path in collections:
        collection = collections[path]["collection"]
        class_path = collection.path
        obj_type = collection.name
    link_props, link_type_check = get_link_props(class_path, object_)
    # Load new object and type
    if (validate_object(object_, obj_type, class_path) and link_type_check):
        if is_collection:
            object_ = parse_collection_members(object_)
        try:
            # Update the right ID if the object is valid and matches
            # type of Item
            object_id = crud.update(object_=object_,
                                    id_=id_,
                                    type_=object_["@type"],
                                    session=get_session(),
                                    api_name=get_api_name(),
                                    collection=is_collection)
            method = "POST"
            resource_url = f"{get_hydrus_server_url()}{get_api_name()}/{path}/{object_id}"
            last_job_id = crud.get_last_modification_job_id(
                session=get_session())
            new_job_id = crud.insert_modification_record(method,
                                                         resource_url,
                                                         session=get_session())
            send_sync_update(socketio=socketio,
                             new_job_id=new_job_id,
                             last_job_id=last_job_id,
                             method=method,
                             resource_url=resource_url)
            headers_ = [{"Location": resource_url}]
            status_description = (f"Object with ID {object_id} successfully "
                                  "updated")
            status = HydraStatus(code=200,
                                 title="Object updated",
                                 desc=status_description)
            return set_response_headers(jsonify(status.generate()),
                                        headers=headers_)

        except (ClassNotFound, InstanceNotFound, InstanceExists,
                PropertyNotFound) as e:
            error = e.get_HTTP()
            return error_response(error)
    else:
        error = HydraError(code=400, title="Data is not valid")
        return error_response(error)
Beispiel #4
0
def item_collection_put_response(path: str) -> Response:
    """
    Handles PUT operation on item collection classes.

    :param path: Path for Item Collection
    :type path: str
    :return: Appropriate response for the PUT operation.
    :rtype: Response
    """
    object_ = json.loads(request.data.decode('utf-8'))
    collections, parsed_classes = get_collections_and_parsed_classes()
    is_collection = False
    if path in parsed_classes:
        class_path = path
        is_collection = False
        obj_type = getType(path, "PUT")
    elif path in collections:
        collection = collections[path]["collection"]
        class_path = collection.path
        obj_type = collection.name
        is_collection = True
    if validate_object(object_, obj_type, class_path):
        # If Item in request's JSON is a valid object ie. @type is a key in object_
        # and the right Item type is being added to the collection
        if is_collection:
            object_ = parse_collection_members(object_)
        try:
            # Insert object and return location in Header
            object_id = crud.insert(object_=object_,
                                    session=get_session(),
                                    collection=is_collection)
            headers_ = [{
                "Location":
                f"{get_hydrus_server_url()}{get_api_name()}/{path}/{object_id}"
            }]
            status_description = f"Object with ID {object_id} successfully added"
            status = HydraStatus(code=201,
                                 title="Object successfully added",
                                 desc=status_description)
            return set_response_headers(jsonify(status.generate()),
                                        headers=headers_,
                                        status_code=status.code)
        except (ClassNotFound, InstanceExists, PropertyNotFound,
                PropertyNotGiven) as e:
            error = e.get_HTTP()
            return error_response(error)
    else:
        error = HydraError(code=400, title="Data is not valid")
        return error_response(error)
Beispiel #5
0
def create_status(possible_status: List[Any]) -> List[HydraStatus]:
    """
    Creates instance of HydraStatus from expanded API doc

    :param possible_status: possible status from the expanded API doc
    :return: List of instances of HydraStatus
    """
    status_list = []

    for status in possible_status:
        status_id = None
        status_title = "The default title for status"
        status_desc = "The default description of status"
        if hydra['description'] in status:
            status_desc = status[hydra['description']][0]['@value']
        status_code = status[hydra['statusCode']][0]['@value']

        if '@id' in status:
            status_id = status['@id']

        if hydra['title'] in status:
            status_title = status[hydra['title']][0]['@value']

        status_ = HydraStatus(status_code, status_id, status_title,
                              status_desc)
        status_list.append(status_)

    return status_list
Beispiel #6
0
def items_delete_response(path: str, int_list="") -> Response:
    """
    Handles DELETE operation to insert multiple items.

    :param path: Path for Item Collection
    :type path: str
    :param int_list: Optional String containing ',' separated ID's
    :type int_list: List
    :return: Appropriate response for the DELETE operation on multiple items.
    :rtype: Response
    """
    _, parsed_classes = get_collections_and_parsed_classes()
    if path in parsed_classes:
        class_type = getType(path, "DELETE")

    if checkClassOp(class_type, "DELETE"):
        # Check if class_type supports PUT operation
        try:
            # Delete the Item with ID == id_
            crud.delete_multiple(int_list, class_type, session=get_session())
            method = "DELETE"
            path_url = f"{get_hydrus_server_url()}{get_api_name()}/{path}"
            last_job_id = crud.get_last_modification_job_id(session=get_session())
            id_list = int_list.split(',')
            for item in id_list:
                resource_url = path_url + item
                new_job_id = crud.insert_modification_record(method,
                                                             resource_url,
                                                             session=get_session())
                send_sync_update(socketio=socketio, new_job_id=new_job_id,
                                 last_job_id=last_job_id, method=method,
                                 resource_url=resource_url)
                last_job_id = new_job_id
            status_description = f"Objects with ID {id_list} successfully deleted"
            status = HydraStatus(code=200,
                                 title="Objects successfully deleted",
                                 desc=status_description)
            return set_response_headers(jsonify(status.generate()))

        except (ClassNotFound, InstanceNotFound) as e:
            error = e.get_HTTP()
            return error_response(error)

    abort(405)
Beispiel #7
0
def create_status(possible_status: Dict[str, Any]) -> HydraStatus:
    """Create a HydraStatus object from the possibleStatus."""
    # Syntax checks
    doc_keys = {"title": False, "statusCode": False, "description": True}
    result = {}
    for k, literal in doc_keys.items():
        result[k] = input_key_check(possible_status, k, "possible_status",
                                    literal)
    # Create the HydraStatus object
    status = HydraStatus(result["statusCode"], result["title"],
                         result["description"])
    return status
Beispiel #8
0
def items_put_response(path: str, int_list="") -> Response:
    """
    Handles PUT operation to insert multiple items.

    :param path: Path for Item Collection
    :type path: str
    :param int_list: Optional String containing ',' separated ID's
    :type int_list: List
    :return: Appropriate response for the PUT operation on multiple items.
    :rtype: Response
    """
    object_ = json.loads(request.data.decode('utf-8'))
    object_ = object_["data"]
    _, parsed_classes = get_collections_and_parsed_classes()
    if path in parsed_classes:
        class_path = path
        obj_type = getType(path, "PUT")
        incomplete_objects = []
        for obj in object_:
            if not check_required_props(class_path, obj):
                incomplete_objects.append(obj)
                object_.remove(obj)
        link_props_list, link_type_check = get_link_props_for_multiple_objects(class_path,
                                                                               object_)
        if validObjectList(object_) and link_type_check:
            type_result = type_match(object_, obj_type)
            # If Item in request's JSON is a valid object
            # ie. @type is one of the keys in object_
            if type_result:
                # If the right Item type is being added to the
                # collection
                try:
                    # Insert object and return location in Header
                    object_id = crud.insert_multiple(
                        objects_=object_, session=get_session(), id_=int_list)
                    headers_ = [{"Location": f"{get_hydrus_server_url()}"
                                             f"{get_api_name()}/{path}/{object_id}"}]
                    if len(incomplete_objects) > 0:
                        status = HydraStatus(code=202,
                                             title="Object(s) missing required property")
                        response = status.generate()
                        response["objects"] = incomplete_objects
                        return set_response_headers(
                            jsonify(response), headers=headers_,
                            status_code=status.code)
                    else:
                        status_description = f"Objects with ID {object_id} successfully added"
                        status = HydraStatus(code=201, title="Objects successfully added",
                                             desc=status_description)
                        return set_response_headers(
                            jsonify(status.generate()), headers=headers_,
                            status_code=status.code)
                except (ClassNotFound, InstanceExists, PropertyNotFound) as e:
                    error = e.get_HTTP()
                    return error_response(error)

        error = HydraError(code=400, title="Data is not valid")
        return error_response(error)
                            read=False,
                            write=True)
# NOTE: Properties that are required=True must be added during class object creation
#       Properties that are read=True are read only
#       Properties that are write=True are writable

# Create operations for the class
op_name = "UpdateClass"  # The name of the operation
op_method = "POST"  # The method of the Operation [GET, POST, PUT, DELETE]
# URI of the object that is expected for the operation
op_expects = class_.id_
op_returns = None  # URI of the object that is returned by the operation
op_returns_header = ["Content-Type", "Content-Length"]
op_expects_header = []
# List of statusCode for the operation
op_status = [HydraStatus(code=200, desc="dummyClass updated.")]

op1 = HydraClassOp(op_name, op_method, op_expects, op_returns,
                   op_expects_header, op_returns_header, op_status)

# Same way add DELETE, PUT and GET operations
op2_status = [HydraStatus(code=200, desc="dummyClass deleted.")]
op2 = HydraClassOp("DeleteClass", "DELETE", None, None, [], [], op2_status)
op3_status = [HydraStatus(code=201, desc="dummyClass successfully added.")]
op3 = HydraClassOp("AddClass", "PUT", class_.id_, None, [], [], op3_status)
op4_status = [HydraStatus(code=200, desc="dummyClass returned.")]
op4 = HydraClassOp("GetClass", "GET", None, class_.id_, [], [], op4_status)

# Operations for non collection class
class_2_op1_status = [HydraStatus(code=200, desc="singleClass changed.")]
class_2_op1 = HydraClassOp("UpdateClass", "POST", class_2.id_, None, [], [],
Beispiel #10
0
def doc_gen(API: str, BASE_URL: str) -> HydraDoc:
    """Generate API Doc for server."""
    # Main API Doc
    api_doc = HydraDoc(API, "API Doc for the server side API",
                       "API Documentation for the server side system", API,
                       BASE_URL, "vocab")

    # State Class
    state = HydraClass("State", "Class for drone state objects", endpoint=True)
    # Properties
    state.add_supported_prop(
        HydraClassProp("http://auto.schema.org/speed", "Speed", False, False,
                       True))
    state.add_supported_prop(
        HydraClassProp("http://schema.org/geo", "Position", False, False,
                       True))
    state.add_supported_prop(
        HydraClassProp("http://schema.org/Property", "Direction", False, False,
                       True))
    state.add_supported_prop(
        HydraClassProp("http://schema.org/fuelCapacity", "Battery", False,
                       False, True))
    state.add_supported_prop(
        HydraClassProp("https://schema.org/status", "SensorStatus", False,
                       False, True))
    state.add_supported_prop(
        HydraClassProp("http://schema.org/identifier", "DroneID", False, False,
                       True))
    # Operations
    state.add_supported_op(
        HydraClassOp("GetState",
                     "GET",
                     None,
                     state.id_,
                     possible_status=[
                         HydraStatus(code=404, desc="State not found"),
                         HydraStatus(code=200, desc="State Returned")
                     ]))
    state_collection_manages = {"property": "rdfs:type", "object": state.id_}
    state_collection = HydraCollection(
        collection_name="StateCollection",
        collection_description="A collection of states",
        manages=state_collection_manages)
    # Drone Class
    drone = HydraClass("Drone", "Class for a drone", endpoint=True)
    # Properties
    drone.add_supported_prop(
        HydraClassProp(state.id_, "DroneState", False, False, True))
    drone.add_supported_prop(
        HydraClassProp("http://schema.org/name", "name", False, False, True))
    drone.add_supported_prop(
        HydraClassProp("http://schema.org/model", "model", False, False, True))
    drone.add_supported_prop(
        HydraClassProp("http://auto.schema.org/speed", "MaxSpeed", False,
                       False, True))
    drone.add_supported_prop(
        HydraClassProp("http://schema.org/device", "Sensor", False, False,
                       True))
    # Operations
    # Drones will submit their state to the server at certain intervals or
    # when some event happens
    drone.add_supported_op(
        HydraClassOp(
            "SubmitDrone",
            "POST",
            drone.id_,
            None,
            possible_status=[HydraStatus(code=200, desc="Drone updated")]))
    drone.add_supported_op(
        HydraClassOp(
            "CreateDrone",
            "PUT",
            drone.id_,
            None,
            possible_status=[HydraStatus(code=200, desc="Drone added")]))
    drone.add_supported_op(
        HydraClassOp("GetDrone",
                     "GET",
                     None,
                     drone.id_,
                     possible_status=[
                         HydraStatus(code=404, desc="Drone not found"),
                         HydraStatus(code=200, desc="Drone Returned")
                     ]))
    drone_collection_manages = {"property": "rdfs:type", "object": drone.id_}
    drone_collection = HydraCollection(
        collection_name="DroneCollection",
        collection_description="A collection of drones",
        manages=drone_collection_manages)

    # NOTE: Commands are stored in a collection. You may GET a command or you
    # may DELETE it, there is not UPDATE.
    command = HydraClass("Command", "Class for drone commands", endpoint=True)
    command.add_supported_prop(
        HydraClassProp("http://schema.org/identifier", "DroneID", False, False,
                       True))
    command.add_supported_prop(
        HydraClassProp(state.id_, "State", False, False, True))
    # Used by mechanics to get newly added commands
    command.add_supported_op(
        HydraClassOp("GetCommand",
                     "GET",
                     None,
                     command.id_,
                     possible_status=[
                         HydraStatus(code=404, desc="Command not found"),
                         HydraStatus(code=200, desc="Command Returned")
                     ]))
    # Used by server to add new commands
    command.add_supported_op(
        HydraClassOp(
            "AddCommand",
            "PUT",
            command.id_,
            None,
            possible_status=[HydraStatus(code=201, desc="Command added")]))

    command.add_supported_op(
        HydraClassOp(
            "DeleteCommand",
            "DELETE",
            None,
            None,
            possible_status=[HydraStatus(code=201, desc="Command deleted")]))
    command_collection_manages = {
        "property": "rdfs:type",
        "object": command.id_
    }
    command_collection = HydraCollection(
        collection_name="CommandCollection",
        collection_description="A collection of commands",
        manages=command_collection_manages)

    # Data is stored as a collection. Each data object can be read.
    # New data added to the collection
    datastream = HydraClass("Datastream",
                            "Class for a datastream entry",
                            endpoint=True)
    datastream.add_supported_prop(
        HydraClassProp("http://schema.org/QuantitativeValue", "Temperature",
                       False, False, True))
    datastream.add_supported_prop(
        HydraClassProp("http://schema.org/identifier", "DroneID", False, False,
                       True))
    datastream.add_supported_prop(
        HydraClassProp("http://schema.org/geo", "Position", False, False,
                       True))
    datastream.add_supported_op(
        HydraClassOp("ReadDatastream",
                     "GET",
                     None,
                     datastream.id_,
                     possible_status=[
                         HydraStatus(code=404, desc="Data not found"),
                         HydraStatus(code=200, desc="Data returned")
                     ]))
    datastream.add_supported_op(
        HydraClassOp(
            "UpdateDatastream",
            "POST",
            datastream.id_,
            None,
            possible_status=[HydraStatus(code=200, desc="Data updated")]))
    datastream.add_supported_op(
        HydraClassOp(
            "DeleteDatastream",
            "DELETE",
            None,
            None,
            possible_status=[HydraStatus(code=200, desc="Data deleted")]))
    datastream_collection_manages = {
        "property": "rdfs:type",
        "object": datastream.id_
    }
    datastream_collection = HydraCollection(
        collection_name="DatastreamCollection",
        collection_description="A collection of datastream",
        manages=datastream_collection_manages)

    # Logs to be accessed mostly by the GUI. Mechanics should add logs for
    # every event.
    log = HydraClass("LogEntry", "Class for a log entry", endpoint=True)
    # Subject
    log.add_supported_prop(
        HydraClassProp("http://schema.org/identifier", "DroneID", True, True,
                       False))
    # Predicate
    log.add_supported_prop(
        HydraClassProp("http://schema.org/UpdateAction", "Update", False, True,
                       False))
    log.add_supported_prop(
        HydraClassProp("http://schema.org/ReplyAction", "Get", False, True,
                       False))
    log.add_supported_prop(
        HydraClassProp("http://schema.org/SendAction", "Send", False, True,
                       False))
    # Objects
    log.add_supported_prop(
        HydraClassProp(state.id_, "State", False, True, False))
    log.add_supported_prop(
        HydraClassProp(datastream.id_, "Data", False, True, False))
    log.add_supported_prop(
        HydraClassProp(command.id_, "Command", False, True, False))
    # GUI will get a certain log entry.
    log.add_supported_op(
        HydraClassOp("GetLog",
                     "GET",
                     None,
                     log.id_,
                     possible_status=[
                         HydraStatus(code=404, desc="Log entry not found"),
                         HydraStatus(code=200, desc="Log entry returned")
                     ]))
    log.add_supported_op(
        HydraClassOp(
            "AddLog",
            "PUT",
            log.id_,
            None,
            possible_status=[HydraStatus(code=201, desc="Log entry created")]))
    log_collection_manages = {"property": "rdfs:type", "object": log.id_}
    log_collection = HydraCollection(
        collection_name="LogEntryCollection",
        collection_description="A collection of logs",
        manages=log_collection_manages)

    # Single object representing the area of interest. No collections.
    area = HydraClass("Area",
                      "Class for Area of Interest of the server",
                      endpoint=True)
    # Using two positions to have a bounding box
    area.add_supported_prop(
        HydraClassProp("http://schema.org/geo", "TopLeft", False, False, True))
    area.add_supported_prop(
        HydraClassProp("http://schema.org/geo", "BottomRight", False, False,
                       True))
    # Allowing updation of the area of interest
    area.add_supported_op(
        HydraClassOp("UpdateArea",
                     "POST",
                     area.id_,
                     None,
                     possible_status=[
                         HydraStatus(code=200, desc="Area of interest changed")
                     ]))
    area.add_supported_op(
        HydraClassOp("GetArea",
                     "GET",
                     None,
                     area.id_,
                     possible_status=[
                         HydraStatus(code=200,
                                     desc="Area of interest not found"),
                         HydraStatus(code=200,
                                     desc="Area of interest returned")
                     ]))

    message = HydraClass("Message",
                         "Class for messages received by the GUI interface",
                         endpoint=True)
    message.add_supported_prop(
        HydraClassProp("http://schema.org/Text", "MessageString", False, False,
                       True))
    message.add_supported_op(
        HydraClassOp("GetMessage",
                     "GET",
                     None,
                     message.id_,
                     possible_status=[
                         HydraStatus(code=200, desc="Message not found"),
                         HydraStatus(code=200, desc="Message returned")
                     ]))
    message.add_supported_op(
        HydraClassOp(
            "DeleteMessage",
            "DELETE",
            None,
            None,
            possible_status=[HydraStatus(code=200, desc="Message deleted")]))
    message_collection_manages = {
        "property": "rdfs:type",
        "object": message.id_
    }
    message_collection = HydraCollection(
        collection_name="MessageCollection",
        collection_description="A collection of messages",
        manages=message_collection_manages)

    api_doc.add_supported_class(drone)
    api_doc.add_supported_collection(drone_collection)
    api_doc.add_supported_class(state)
    api_doc.add_supported_collection(state_collection)
    api_doc.add_supported_class(datastream)
    api_doc.add_supported_collection(datastream_collection)
    api_doc.add_supported_class(log)
    api_doc.add_supported_collection(log_collection)
    api_doc.add_supported_class(area)
    api_doc.add_supported_class(command)
    api_doc.add_supported_collection(command_collection)
    api_doc.add_supported_class(message)
    api_doc.add_supported_collection(message_collection)

    api_doc.add_baseResource()
    api_doc.add_baseCollection()
    api_doc.gen_EntryPoint()
    return api_doc