Example #1
0
def handle_single_transaction(role, program, project, **tx_kwargs):
    """
    Main entry point for single file transactions.

    This function multiplexes on the content-type to call the appropriate
    transaction handler.
    """
    doc = flask.request.get_data().decode("utf-8")
    content_type = flask.request.headers.get("Content-Type", "").lower()
    if content_type == "text/csv":
        doc_format = "csv"
        data, errors = utils.transforms.CSVToJSONConverter().convert(doc)
    elif content_type in ["text/tab-separated-values", "text/tsv"]:
        doc_format = "tsv"
        data, errors = utils.transforms.TSVToJSONConverter().convert(doc)
    else:
        doc_format = "json"
        data = utils.parse.parse_request_json()
        errors = None
    # TODO: use errors value?
    name = flask.request.headers.get("X-Document-Name", None)
    doc_args = [name, doc_format, doc, data]
    is_async = tx_kwargs.pop("is_async", utils.is_flag_set(FLAG_IS_ASYNC))
    db_driver = tx_kwargs.pop("db_driver", flask.current_app.db)
    transaction = UploadTransaction(
        program=program,
        project=project,
        role=role,
        logger=flask.current_app.logger,
        flask_config=flask.current_app.config,
        index_client=flask.current_app.index_client,
        external_proxies=utils.get_external_proxies(),
        db_driver=db_driver,
        **tx_kwargs)
    if is_async:
        session = transaction.db_driver.session_scope(can_inherit=False)
        with session, transaction:
            response = {
                "code": 200,
                "message": "Transaction submitted.",
                "transaction_id": transaction.transaction_id,
            }
        flask.current_app.async_pool.schedule(single_transaction_worker,
                                              transaction, *doc_args)

        # create the resource in arborist
        auth.create_resource(program, project, doc_args[3])

        return flask.jsonify(response)
    else:
        response, code = single_transaction_worker(transaction, *doc_args)

        # create the resource in arborist
        auth.create_resource(program, project, doc_args[3])

        return flask.jsonify(response), code
Example #2
0
def root_create():
    """
    Register a program.

    The content of the request is a JSON containing the information
    describing a program.  Authorization for registering programs is
    limited to administrative users.

    Summary:
        Create a program

    Tags:
        program

    Args:
        body (schema_program): input body

    Responses:
        200: Registered successfully.
        403: Unauthorized request.

    :reqheader Content-Type:
        |reqheader_Content-Type|
    :reqheader Accept:
        |reqheader_Accept|
    :reqheader X-Auth-Token:
        |reqheader_X-Auth-Token|
    :resheader Content-Type:
        |resheader_Content-Type|

    **Example:**

    .. code-block:: http

           POST /v0/submission/CGCI/ HTTP/1.1
           Host: example.com
           Content-Type: application/json
           X-Auth-Token: MIIDKgYJKoZIhvcNAQcC...
           Accept: application/json

    .. code-block:: JavaScript

        {
            "type": "program",
            "name": "CGCI",
            "dbgap_accession_number": "phs000178"
        }
    """
    message = None
    node_id = None
    doc = parse.parse_request_json()
    if not isinstance(doc, dict):
        raise UserError(
            "The program creation endpoint only supports single documents (dict). Received data of type {}"
            .format(type(doc)))
    if doc.get("type") != "program":
        raise UserError("Invalid type in key type='{}'".format(
            doc.get("type")))
    phsid = doc.get("dbgap_accession_number")
    program = doc.get("name")
    if not program:
        raise UserError("No program specified in key 'name'")

    # create the resource in sheepdog DB
    with current_app.db.session_scope(can_inherit=False) as session:
        node = current_app.db.nodes(
            models.Program).props(name=program).scalar()
        if node:
            message = "Program is updated!"
            node_id = node.node_id
            node.props["dbgap_accession_number"] = phsid
        else:
            node_id = str(uuid.uuid5(PROGRAM_SEED, program))
            session.add(
                models.Program(  # pylint: disable=not-callable
                    node_id,
                    name=program,
                    dbgap_accession_number=phsid))
            message = "Program registered."

    # create the resource in arborist
    auth.create_resource(phsid)

    return flask.jsonify({"id": node_id, "name": program, "message": message})
Example #3
0
def create_program():
    """
    Register a program.

    The content of the request is a JSON containing the information
    describing a program.  Authorization for registering programs is
    limited to administrative users.

    Summary:
        Create a program

    Tags:
        program

    Args:
        body (schema_program): input body

    Responses:
        200: Registered successfully.
        403: Unauthorized request.

    :reqheader Content-Type:
        |reqheader_Content-Type|
    :reqheader Accept:
        |reqheader_Accept|
    :reqheader X-Auth-Token:
        |reqheader_X-Auth-Token|
    :resheader Content-Type:
        |resheader_Content-Type|

    **Example:**

    .. code-block:: http

           POST /v0/submission/CGCI/ HTTP/1.1
           Host: example.com
           Content-Type: application/json
           X-Auth-Token: MIIDKgYJKoZIhvcNAQcC...
           Accept: application/json

    .. code-block:: JavaScript

        {
            "type": "program",
            "name": "CGCI",
            "dbgap_accession_number": "phs000178"
        }
    """
    input_doc = flask.request.get_data().decode("utf-8")
    content_type = flask.request.headers.get("Content-Type", "").lower()
    errors = None
    if content_type == "text/csv":
        doc, errors = utils.transforms.CSVToJSONConverter().convert(input_doc)
    elif content_type in ["text/tab-separated-values", "text/tsv"]:
        doc, errors = utils.transforms.TSVToJSONConverter().convert(input_doc)
    else:
        doc = utils.parse.parse_request_json()

    if errors:
        raise UserError("Unable to parse doc '{}': {}".format(
            input_doc, errors))

    if isinstance(doc, list) and len(doc) == 1:
        # handle TSV/CSV submissions that are parsed as lists of 1 element
        doc = doc[0]
    if not isinstance(doc, dict):
        raise UserError(
            "The program creation endpoint only supports single documents (dict). Received data of type {}: {}"
            .format(type(doc), doc))
    if doc.get("type") != "program":
        raise UserError("Invalid type in key type='{}'".format(
            doc.get("type")))
    phsid = doc.get("dbgap_accession_number")
    program = doc.get("name")
    if not program:
        raise UserError("No program specified in key 'name'")

    # create the resource in sheepdog DB
    message = None
    node_id = None
    with current_app.db.session_scope(can_inherit=False) as session:
        node = current_app.db.nodes(
            models.Program).props(name=program).scalar()
        if node:
            message = "Program is updated!"
            node_id = node.node_id
            node.props["dbgap_accession_number"] = phsid
        else:
            node_id = str(uuid.uuid5(PROGRAM_SEED, program))
            session.add(
                models.Program(  # pylint: disable=not-callable
                    node_id,
                    name=program,
                    dbgap_accession_number=phsid))
            message = "Program registered."

    # create the resource in arborist
    auth.create_resource(phsid)

    return flask.jsonify({"id": node_id, "name": program, "message": message})
Example #4
0
def create_project(program):
    """
    Register a project.

    The content of the request is a JSON containing the information describing
    a project. Authorization for registering projects is limited to
    administrative users.

    Summary:
        Create a project

    Tags:
        project

    Args:
        program (str): |program_id|
        body (schema_project): input body

    Responses:
        200: Registered successfully.
        400: User error.
        404: Program not found.
        403: Unauthorized request.

    :reqheader Content-Type: |reqheader_Content-Type|
    :reqheader Accept: |reqheader_Accept|
    :reqheader X-Auth-Token: |reqheader_X-Auth-Token|
    :resheader Content-Type: |resheader_Content-Type|

    Example:

        .. code-block:: http

            POST /v0/submission/CGCI/ HTTP/1.1
            Host: example.com
            Content-Type: application/json
            X-Auth-Token: MIIDKgYJKoZIhvcNAQcC...
            Accept: application/json

        .. code-block:: JavaScript

            {
                "type": "project",
                "code": "BLGSP",
                "disease_type": "Burkitt Lymphoma",
                "name": "Burkitt Lymphoma Genome Sequencing Project",
                "primary_site": "Lymph Nodes",
                "dbgap_accession_number": "phs000527",
                "state": "active"
            }
    """
    res = None
    doc = utils.parse.parse_request_json()
    if not isinstance(doc, dict):
        raise UserError("Program endpoint only supports single documents")
    if doc.get("type") and doc.get("type") not in ["project"]:
        raise UserError(
            "Invalid post to program endpoint with type='{}'".format(doc.get("type"))
        )
    # Parse project.code
    project = doc.get("code")
    if not project:
        raise UserError("No project specified in key 'code'")
    # Parse dbgap accession number.
    phsid = doc.get("dbgap_accession_number")
    if not phsid:
        raise UserError("No dbGaP accesion number specified.")

    # Create base JSON document.
    base_doc = utils.parse.parse_request_json()
    with flask.current_app.db.session_scope() as session:
        program_node = utils.lookup_program(flask.current_app.db, program)
        if not program_node:
            raise NotFoundError("Program {} is not registered".format(program))
        # Look up project node.
        node = utils.lookup_project(flask.current_app.db, program, project)
        if not node:
            # Create a new project node
            node_uuid = str(uuid.uuid5(PROJECT_SEED, project))
            if flask.current_app.db.nodes(models.Project).ids(node_uuid).first():
                raise UserError(
                    "ERROR: Project {} already exists in DB".format(project)
                )

            node = models.Project(node_uuid)  # pylint: disable=not-callable
            node.programs = [program_node]
            action = "create"
            node.props["state"] = "open"
        else:
            action = "update"

        # silently drop system_properties
        base_doc.pop("type", None)
        base_doc.pop("state", None)
        base_doc.pop("released", None)

        node.props.update(base_doc)

        doc = dict(
            {"type": "project", "programs": {"id": program_node.node_id}}, **base_doc
        )

        # Create transaction
        transaction_args = dict(
            program=program,
            project=project,
            role=ROLES["UPDATE"],
            flask_config=flask.current_app.config,
        )
        with UploadTransaction(**transaction_args) as trans:
            node = session.merge(node)
            session.commit()
            entity = UploadEntityFactory.create(
                trans, doc=None, config=flask.current_app.config
            )
            entity.action = action
            entity.doc = doc
            entity.entity_type = "project"
            entity.unique_keys = node._secondary_keys_dicts
            entity.node = node
            entity.entity_id = entity.node.node_id
            trans.entities = [entity]
            res = flask.jsonify(trans.json)

        # create the resource in arborist
        auth.create_resource(program_node.dbgap_accession_number, phsid)

    return res