def update_dashboard(id_, dashboard=None, token_info=None, user=None):
    """Update a dashboard

    :param id: ID of test dashboard
    :type id: str
    :param body: Dashboard
    :type body: dict | bytes

    :rtype: Dashboard
    """
    if not connexion.request.is_json:
        return "Bad request, JSON required", 400
    dashboard_dict = connexion.request.get_json()
    if dashboard_dict.get("metadata", {}).get("project") and not project_has_user(
        dashboard_dict["metadata"]["project"], user
    ):
        return "Forbidden", 403
    dashboard = Dashboard.query.get(id_)
    if not dashboard:
        return "Dashboard not found", 404
    if project_has_user(dashboard.project, user):
        return "Forbidden", 403
    dashboard.update(connexion.request.get_json())
    session.add(dashboard)
    session.commit()
    return dashboard.to_dict()
示例#2
0
def update_run(id_, run=None, token_info=None, user=None):
    """Updates a single run

    :param id: ID of run to update
    :type id: int
    :param body: Run
    :type body: dict

    :rtype: Run
    """
    if not connexion.request.is_json:
        return "Bad request, JSON required", 400
    run_dict = connexion.request.get_json()
    if run_dict.get("metadata", {}).get("project"):
        run_dict["project_id"] = get_project_id(
            run_dict["metadata"]["project"])
        if not project_has_user(run_dict["project_id"], user):
            return "Forbidden", 403
    run = Run.query.get(id_)
    if run and not project_has_user(run.project, user):
        return "Forbidden", 403
    if not run:
        return "Run not found", 404
    run.update(run_dict)
    session.add(run)
    session.commit()
    update_run_task.apply_async((id_, ), countdown=5)
    return run.to_dict()
示例#3
0
def view_artifact(id_, token_info=None, user=None):
    """Stream an artifact directly to the client/browser

    :param id: ID of the artifact to download
    :type id: str

    :rtype: file
    """
    artifact, response = _build_artifact_response(id_)
    if artifact.result and not project_has_user(artifact.result.project, user):
        return "Forbidden", 403
    elif artifact.run and not project_has_user(artifact.run.project, user):
        return "Forbidden", 403
    return response
示例#4
0
def add_run(run=None, token_info=None, user=None):
    """Create a new run

    :param body: Run object
    :type body: dict | bytes

    :rtype: Run
    """
    if not connexion.request.is_json:
        return "Bad request, JSON is required", 400
    run = Run.from_dict(**connexion.request.get_json())

    if run.data and not (run.data.get("project") or run.project_id):
        return "Bad request, project or project_id is required", 400

    project = get_project(run.data["project"])
    if not project_has_user(project, user):
        return "Forbidden", 403
    run.project = project
    run.env = run.data.get("env") if run.data else None
    run.component = run.data.get("component") if run.data else None
    # allow start_time to be set by update_run task if no start_time present
    run.start_time = run.start_time if run.start_time else datetime.utcnow()
    # if not present, created is the time at which the run is added to the DB
    run.created = run.created if run.created else datetime.utcnow()

    session.add(run)
    session.commit()
    update_run_task.apply_async((run.id, ), countdown=5)
    return run.to_dict(), 201
def update_widget_config(id_, token_info=None, user=None):
    """Updates a single widget config

    :param id: ID of widget to update
    :type id: int
    :param body: Result
    :type body: dict

    :rtype: Result
    """
    if not connexion.request.is_json:
        return "Bad request, JSON required", 400
    data = connexion.request.get_json()
    if data.get("widget") and data["widget"] not in WIDGET_TYPES.keys():
        return "Bad request, widget type does not exist", 400
    # Look up the project id
    if data.get("project"):
        project = get_project(data.pop("project"))
        if not project_has_user(project, user):
            return "Forbidden", 403
        data["project_id"] = project.id
    widget_config = WidgetConfig.query.get(id_)
    # add default weight of 10
    if not widget_config.weight:
        widget_config.weight = 10
    # default to make views navigable
    if data.get("navigable") and isinstance(data["navigable"], str):
        data["navigable"] = data["navigable"][0] in ALLOWED_TRUE_BOOLEANS
    if data.get("type") and data["type"] == "view" and data.get("navigable") is None:
        data["navigable"] = True
    widget_config.update(data)
    session.add(widget_config)
    session.commit()
    return widget_config.to_dict()
def add_widget_config(widget_config=None, token_info=None, user=None):
    """Create a new widget config

    :param widget_config: The widget_config to save
    :type widget_config: dict | bytes

    :rtype: WidgetConfig
    """
    if not connexion.request.is_json:
        return "Bad request, JSON required", 400
    data = connexion.request.json
    if data["widget"] not in WIDGET_TYPES.keys():
        return "Bad request, widget type does not exist", 400
    # add default weight of 10
    if not data.get("weight"):
        data["weight"] = 10
    # Look up the project id
    if data.get("project"):
        project = get_project(data.pop("project"))
        if not project_has_user(project, user):
            return "Forbidden", 403
        data["project_id"] = project.id
    # default to make views navigable
    if data.get("navigable") and isinstance(data["navigable"], str):
        data["navigable"] = data["navigable"][0] in ALLOWED_TRUE_BOOLEANS
    if data.get("type") == "view" and data.get("navigable") is None:
        data["navigable"] = True
    widget_config = WidgetConfig.from_dict(**data)
    session.add(widget_config)
    session.commit()
    return widget_config.to_dict(), 201
示例#7
0
def add_import(
    import_file: Optional[FileStorage] = None,
    project: Optional[str] = None,
    metadata: Optional[str] = None,
    source: Optional[str] = None,
    token_info: Optional[str] = None,
    user: Optional[str] = None,
):
    """Imports a JUnit XML file and creates a test run and results from it.

    :param import_file: file to upload
    :type import_file: werkzeug.datastructures.FileStorage
    :param project: the project to add this test run to
    :type project: str
    :param metadata: extra metadata to add to the run and the results, in a JSON string
    :type metadata: str
    :param source: the source of the test run
    :type source: str

    :rtype: Import
    """
    if "importFile" in connexion.request.files:
        import_file = connexion.request.files["importFile"]
    if not import_file:
        return "Bad request, no file uploaded", 400
    data = {}
    if connexion.request.form.get("project"):
        project = connexion.request.form["project"]
    if project:
        project = get_project(project)
        if not project_has_user(project, user):
            return "Forbidden", 403
        data["project_id"] = project.id
    if connexion.request.form.get("metadata"):
        metadata = json.loads(connexion.request.form.get("metadata"))
    data["metadata"] = metadata
    if connexion.request.form.get("source"):
        data["source"] = connexion.request.form["source"]
    new_import = Import.from_dict(
        **{
            "status": "pending",
            "filename": import_file.filename,
            "format": "",
            "data": data
        })
    session.add(new_import)
    session.commit()
    new_file = ImportFile(import_id=new_import.id, content=import_file.read())
    session.add(new_file)
    session.commit()
    if import_file.filename.endswith(".xml"):
        run_junit_import.delay(new_import.to_dict())
    elif import_file.filename.endswith(".tar.gz"):
        run_archive_import.delay(new_import.to_dict())
    else:
        return "Unsupported Media Type", 415
    return new_import.to_dict(), 202
示例#8
0
def download_artifact(id_, token_info=None, user=None):
    """Download an artifact

    :param id: ID of artifact to download
    :type id: str

    :rtype: file
    """
    artifact, response = _build_artifact_response(id_)
    if not project_has_user(artifact.result.project, user):
        return "Forbidden", 403
    response.headers["Content-Disposition"] = "attachment; filename={}".format(artifact.filename)
    return response
示例#9
0
def get_run(id_, token_info=None, user=None):
    """Get a run

    :param id: The ID of the run
    :type id: str

    :rtype: Run
    """
    run = Run.query.get(id_)
    if not run:
        return "Run not found", 404
    if not project_has_user(run.project, user):
        return "Forbidden", 403
    return run.to_dict()
示例#10
0
def get_artifact(id_, token_info=None, user=None):
    """Return a single artifact

    :param id: ID of the artifact
    :type id: str

    :rtype: Artifact
    """
    artifact = Artifact.query.get(id_)
    if not artifact:
        return "Not Found", 404
    if not project_has_user(artifact.result.project, user):
        return "Forbidden", 403
    return artifact.to_dict()
def get_dashboard(id_, token_info=None, user=None):
    """Get a single dashboard by ID

    :param id: ID of test dashboard
    :type id: str

    :rtype: Dashboard
    """
    dashboard = Dashboard.query.get(id_)
    if not dashboard:
        return "Dashboard not found", 404
    if dashboard and dashboard.project and not project_has_user(dashboard.project, user):
        return "Forbidden", 403
    return dashboard.to_dict()
示例#12
0
def get_import(id_, token_info=None, user=None):
    """Get a run

    :param id: The ID of the run
    :type id: str

    :rtype: Run
    """
    import_ = Import.query.get(id_)
    if import_ and import_.data.get("project_id"):
        project = get_project(import_.data["project_id"])
        if project and not project_has_user(project, user):
            return "Forbidden", 403
    if not import_:
        return "Not Found", 404
    return import_.to_dict()
示例#13
0
def delete_artifact(id_, token_info=None, user=None):
    """Deletes an artifact

    :param id: ID of the artifact to delete
    :type id: str

    :rtype: tuple
    """
    artifact = Artifact.query.get(id_)
    if not artifact:
        return "Not Found", 404
    if not project_has_user(artifact.result.project, user):
        return "Forbidden", 403
    session.delete(artifact)
    session.commit()
    return "OK", 200
def add_dashboard(dashboard=None, token_info=None, user=None):
    """Create a dashboard

    :param body: Dashboard
    :type body: dict | bytes

    :rtype: Dashboard
    """
    if not connexion.request.is_json:
        return "Bad request, JSON required", 400
    dashboard = Dashboard.from_dict(**connexion.request.get_json())
    if dashboard.project_id and not project_has_user(dashboard.project_id, user):
        return "Forbidden", 403
    session.add(dashboard)
    session.commit()
    return dashboard.to_dict(), 201
示例#15
0
def bulk_update(filter_=None, page_size=1, token_info=None, user=None):
    """Updates multiple runs with common metadata

    Note: can only be used to update metadata on runs, limited to 25 runs

    :param filter_: A list of filters to apply
    :param page_size: Limit the number of runs updated, defaults to 1

    :rtype: List[Run]
    """
    if not connexion.request.is_json:
        return "Bad request, JSON required", 400

    run_dict = connexion.request.get_json()

    if not run_dict.get("metadata"):
        return "Bad request, can only update metadata", 401

    # ensure only metadata is updated
    run_dict = {"metadata": run_dict.pop("metadata")}

    if page_size > 25:
        return "Bad request, cannot update more than 25 runs at a time", 405

    if run_dict.get("metadata", {}).get("project"):
        project = get_project(run_dict["metadata"]["project"])
        if not project_has_user(project, user):
            return "Forbidden", 403
        run_dict["project_id"] = project.id

    runs = get_run_list(filter_=filter_, page_size=page_size,
                        estimate=True).get("runs")

    if not runs:
        return f"No runs found with {filter_}", 404

    model_runs = []
    for run_json in runs:
        run = Run.query.get(run_json.get("id"))
        # update the json dict of the run with the new metadata
        merge_dicts(run_dict, run_json)
        run.update(run_json)
        session.add(run)
        model_runs.append(run)
    session.commit()

    return [run.to_dict() for run in model_runs]
def get_dashboard_list(
    filter_=None, project_id=None, page=1, page_size=25, token_info=None, user=None
):
    """Get a list of dashboards

    :param project_id: Filter dashboards by project ID
    :type project_id: str
    :param user_id: Filter dashboards by user ID
    :type user_id: str
    :param limit: Limit the dashboards
    :type limit: int
    :param offset: Offset the dashboards
    :type offset: int

    :rtype: DashboardList
    """
    query = Dashboard.query
    project = None
    if "project_id" in connexion.request.args:
        project = Project.query.get(connexion.request.args["project_id"])
    if project:
        if not project_has_user(project, user):
            return "Forbidden", 403
        query = query.filter(Dashboard.project_id == project_id)

    if filter_:
        for filter_string in filter_:
            filter_clause = convert_filter(filter_string, Dashboard)
            if filter_clause is not None:
                query = query.filter(filter_clause)

    query = query.order_by(Dashboard.title.asc())
    offset = (page * page_size) - page_size
    total_items = query.count()
    total_pages = (total_items // page_size) + (1 if total_items % page_size > 0 else 0)
    dashboards = query.offset(offset).limit(page_size).all()
    return {
        "dashboards": [dashboard.to_dict() for dashboard in dashboards],
        "pagination": {
            "page": page,
            "pageSize": page_size,
            "totalItems": total_items,
            "totalPages": total_pages,
        },
    }
示例#17
0
def get_project(id_, token_info=None, user=None):
    """Get a single project by ID

    :param id: ID of test project
    :type id: str

    :rtype: Project
    """
    if not is_uuid(id_):
        id_ = convert_objectid_to_uuid(id_)
    project = Project.query.filter(Project.name == id_).first()
    if not project:
        project = Project.query.get(id_)
    if project and not project_has_user(project, user):
        return "Unauthorized", 401
    if not project:
        return "Project not found", 404
    return project.to_dict()
def delete_dashboard(id_, token_info=None, user=None):
    """Deletes a dashboard

    :param id: ID of the dashboard to delete
    :type id: str

    :rtype: tuple
    """
    dashboard = Dashboard.query.get(id_)
    if not dashboard:
        return "Not Found", 404
    if not project_has_user(dashboard.project, user):
        return "Forbidden", 403
    widget_configs = WidgetConfig.query.filter(WidgetConfig.dashboard_id == dashboard.id).all()
    for widget_config in widget_configs:
        session.delete(widget_config)
    session.delete(dashboard)
    session.commit()
    return "OK", 200
示例#19
0
def upload_artifact(body, token_info=None, user=None):
    """Uploads a artifact artifact

    :param result_id: ID of result to attach artifact to
    :type result_id: str
    :param run_id: ID of run to attach artifact to
    :type run_id: str
    :param filename: filename for storage
    :type filename: string
    :param file: file to upload
    :type file: werkzeug.datastructures.FileStorage
    :param additional_metadata: Additional data to pass to server
    :type additional_metadata: object

    :rtype: tuple
    """
    result_id = body.get("result_id") or body.get("resultId")
    run_id = body.get("run_id") or body.get("runId")
    result = Result.query.get(result_id)
    if result and not project_has_user(result.project, user):
        return "Forbidden", 403
    filename = body.get("filename")
    additional_metadata = body.get("additional_metadata", {})
    file_ = connexion.request.files["file"]
    content_type = magic.from_buffer(file_.read())
    data = {
        "contentType": content_type,
        "resultId": result_id,
        "runId": run_id,
        "filename": filename,
    }
    if additional_metadata:
        if isinstance(additional_metadata, str):
            try:
                additional_metadata = json.loads(additional_metadata)
            except (ValueError, TypeError):
                return "Bad request, additionalMetadata is not valid JSON", 400
        if not isinstance(additional_metadata, dict):
            return "Bad request, additionalMetadata is not a JSON object", 400
        data["additionalMetadata"] = additional_metadata
    # Reset the file pointer
    file_.seek(0)
    if data.get("runId"):
        artifact = Artifact(
            filename=filename,
            run_id=data["runId"],
            content=file_.read(),
            upload_date=datetime.utcnow(),
            data=additional_metadata,
        )
    else:
        artifact = Artifact(
            filename=filename,
            result_id=data["resultId"],
            content=file_.read(),
            upload_date=datetime.utcnow(),
            data=additional_metadata,
        )

    session.add(artifact)
    session.commit()
    return artifact.to_dict(), 201