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
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})
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})
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