def test_update_version_zero_empty_string(mocker):
    """
    Test empty strings are not added to the actions
    """

    metadata = {
        "general": {
            "id": "test_plugin",
            "name": "test",
            "qgisMinimumVersion": "0.0.0",
            "qgisMaximumVersion": "0.0.1",
            "description": "this is a test",
            "about": "testing",
            "version": "1.0.0",
            "author": "me",
            "email": "*****@*****.**",
            "tags": "",
        }
    }

    mocker.patch("pynamodb.models.Model.update")
    version_zero = mocker.Mock()
    version_zero.revisions = 0
    version_zero.ended_at = None
    version_zero.attribute_values = {}
    version_zero.updated_at = None
    version_zero.file_name = None

    MetadataModel.update_version_zero(metadata, version_zero, "c611a73c-12a0-4414-9ab5-ed1889122073")
    # check tags (value = empty string) did not make it in the actions
    assert "tags" not in [str(i.values[0]) for i in version_zero.mock_calls[0][2]["actions"] if i.format_string != "{0}"]
def test_validate_token(mocker):
    """
    Test successful matching of secret
    """

    token = "12345"
    plugin_stage = "dev"
    mocker.patch("src.plugin.metadata_model.MetadataModel.query", return_value=query_iter_obj(mocker, hash_token(token)))
    plugin_id = "test_plugin"
    MetadataModel.validate_token(token, plugin_id, plugin_stage)
Пример #3
0
def upload(plugin_id):
    """
    End point for processing QGIS plugin data POSTed by the user
    If the query param "?stage" is not supplied, plugins POSTed are
    considered production. if `?stage=dev` is used the plugin
    is stored as a dev version
    :param data: plugin
    :type data: binary data
    :returns: tuple (http response, http code)
    :rtype: tuple (flask.wrappers.Response, int)
    """

    plugin_stage = request.args.get("stage", DEFUALT_STAGE)
    validate_stage(plugin_stage)

    post_data = request.get_data()
    if not post_data:
        get_log().error("NoDataSupplied")
        raise DataError(400, "No plugin file supplied")

    # Get users access token from header
    token = get_access_token(request.headers)
    MetadataModel.validate_token(token, plugin_id, plugin_stage)

    # Test the file is a zipfile
    if not zipfile.is_zipfile(BytesIO(post_data)):
        get_log().error("NotZipfile")
        raise DataError(400, "Plugin file supplied not a Zipfile")

    # Extract plugin metadata
    plugin_zipfile = zipfile.ZipFile(BytesIO(post_data), "r", zipfile.ZIP_DEFLATED, False)
    metadata_path = plugin_parser.metadata_path(plugin_zipfile)
    metadata = plugin_parser.metadata_contents(plugin_zipfile, metadata_path)

    # Get the plugins root dir. This is what QGIS references when handling plugins
    g.plugin_id = plugin_parser.zipfile_root_dir(plugin_zipfile)
    if g.plugin_id != plugin_id:
        raise DataError(400, "Invalid plugin name %s" % g.plugin_id)

    # Allocate a filename
    filename = str(uuid.uuid4())
    get_log().info("FileName", filename=filename)

    # Upload the plugin to s3
    aws.s3_put(post_data, repo_bucket_name, filename, g.plugin_id)
    get_log().info("UploadedTos3", filename=filename, bucketName=repo_bucket_name)

    # Update metadata database
    try:
        plugin_metadata = MetadataModel.new_plugin_version(metadata, g.plugin_id, filename, plugin_stage)
    except ValueError as error:
        raise DataError(400, str(error))
    return format_response(plugin_metadata, 201)
def test_validate_token_incorrect_secret(mocker):
    """
    Fail if token does not match database secret
    """

    mocker.patch("src.plugin.metadata_model.MetadataModel.query", return_value=query_iter_obj(mocker, "54321"))
    mocker.patch("werkzeug.local.LocalProxy.__getattr__", return_value={"authorization": "basic 12345"})
    mocker.patch("src.plugin.log.g", return_value={"requestId": "1234567", "plugin_id": "test_plugin"})

    token = "12345"
    plugin_id = "test_plugin"
    plugin_stage = "dev"
    with pytest.raises(DataError) as error:
        MetadataModel.validate_token(token, plugin_id, plugin_stage)
    assert "Invalid token" in str(error.value)
def test_metadata_model_missing_required(mocker):
    """
    pynamdodb model throws exception on save if
    required fields are null
    """

    mocker.patch("pynamodb.connection.base.get_session")
    mocker.patch("pynamodb.connection.table.Connection")

    metadata = {
        "id": "c611a73c-12a0-4414-9ab5-ed1889122073",
        "name": "test",
        "qgisMinimumVersion": "0.0.0",
        "qgisMaximumVersion": "0.0.1",
        "description": "this is a test",
        "about": "testing",
        "version": "1.0.0",
        "author": "me",
        "email": "*****@*****.**",
    }

    result = MetadataModel(
        id=metadata.get("id"),
        updated_at=metadata.get("updated_at"),
        name=metadata.get("name"),
        qgis_minimum_version=metadata.get("qgisMinimumVersion"),
        qgis_maximum_version=metadata.get("qgisMaximumVersion"),
        description=metadata.get("description"),
        about=metadata.get("about"),
        version=metadata.get("version"),
        author_name=metadata.get("author"),
        email=metadata.get("email"),
        changelog=metadata.get("changelog"),
        experimental=metadata.get("experimental"),
        deprecated=metadata.get("deprecated"),
        tags=metadata.get("tags"),
        homepage=metadata.get("homepage"),
        repository=metadata.get("repository"),
        tracker=metadata.get("tracker"),
        icon=metadata.get("icon"),
        category=metadata.get("category"),
    )

    with pytest.raises(ValueError) as excinfo:
        result.save()
        assert "ValueError" in str(excinfo.value)
Пример #6
0
def health():
    """
    Ping to confirm the service is up
    """

    checks = {}

    # check database connection
    MetadataModel.all_version_zeros(DEFUALT_STAGE)
    checks["db"] = {"status": "ok"}

    # check s3 connection
    aws.s3_head_bucket(repo_bucket_name)
    checks["s3"] = {"status": "ok"}

    # Anything not a 200 has been caught as an
    # error and the health-checks have failed
    get_log().info({"status": "ok", "details": checks})
    return format_response({"status": "ok"}, 200)
Пример #7
0
def get_all_plugins():
    """
    List all plugin's metadata
    :returns: tuple (http response, http code)
    :rtype: tuple (flask.wrappers.Response, int)
    """

    plugin_stage = request.args.get("stage", DEFUALT_STAGE)
    response = list(MetadataModel.all_version_zeros(plugin_stage))
    return format_response(response, 200)
Пример #8
0
def archive(plugin_id):
    """
    Takes a plugin_id as input and adds an end-date to the
    metadata record associated to the Id
    :param plugin_id: plugin_id
    :type data: string
    :returns: tuple (http response, http code)
    :rtype: tuple (flask.wrappers.Response, int)
    """

    plugin_stage = request.args.get("stage", DEFUALT_STAGE)
    g.plugin_id = plugin_id

    # Get users access token from header
    token = get_access_token(request.headers)
    # validate access token
    MetadataModel.validate_token(token, g.plugin_id, plugin_stage)
    # Archive plugins
    response = MetadataModel.archive_plugin(plugin_id, plugin_stage)
    return format_response(response, 200)
Пример #9
0
def get_all_revisions(plugin_id):
    """
    Takes a plugin_id and returns all associated plugin revisions
    :param plugin_id: plugin_id
    :type data: string
    :returns: tuple (http response, http code)
    :rtype: tuple (flask.wrappers.Response, int)
    """

    plugin_stage = request.args.get("stage", DEFUALT_STAGE)
    g.plugin_id = plugin_id
    return format_response(MetadataModel.plugin_all_versions(plugin_id, plugin_stage), 200)
Пример #10
0
def get_plugin(plugin_id):
    """
    Takes a plugin_id as input and returns the metadata of the
    one associated plugin to this Id
    :param plugin_id: plugin_id
    :type data: string
    :returns: tuple (http response, http code)
    :rtype: tuple (flask.wrappers.Response, int)
    """

    plugin_stage = request.args.get("stage", DEFUALT_STAGE)
    g.plugin_id = plugin_id
    return format_response(MetadataModel.plugin_version_zero(plugin_id, plugin_stage), 200)
Пример #11
0
def generate_xml_body(repo_bucket_name, aws_region, qgis_version,
                      plugin_stage):
    """
    Generate XML describing plugin store
    from dynamodb plugin metadata db
    :param repo_bucket_name: s3 bucket name
    :type repo_bucket_name: string
    :param aws_region:  aws_region
    :type aws_region: string
    :returns: string representation of plugin xml
    :rtype: string
    """

    current_plugins = filter(
        lambda item: compatible_with_qgis_version(item, qgis_version),
        MetadataModel.all_version_zeros(plugin_stage))
    root = ET.Element("plugins")
    for plugin in current_plugins:
        if not plugin["revisions"]:
            continue
        current_group = ET.SubElement(root, "pyqgis_plugin", {
            "name": plugin["name"],
            "version": plugin["version"]
        })
        for key, value in plugin.items():
            if key not in ("file_name", "name", "id", "category", "email",
                           "item_version", "stage"):
                new_element = new_xml_element(key, value)
                current_group.append(new_element)
        new_element = new_xml_element(
            "file_name", f"{plugin['id']}.{plugin['version']}.zip")
        current_group.append(new_element)
        download_url = generate_download_url(repo_bucket_name, aws_region,
                                             plugin["file_name"])
        new_element = new_xml_element("download_url", download_url)
        current_group.append(new_element)
    return ET.tostring(root)
def test_metadata_model(mocker):
    """
    model parameters are set and save is executed
    without error
    """

    mocker.patch("pynamodb.connection.base.get_session")
    mocker.patch("pynamodb.connection.table.Connection")
    file_name = str(uuid.uuid4())

    metadata = {
        "id": "test_plugin",
        "item_version": "000000",
        "revisions": "0",
        "name": "test",
        "qgis_minimum_version": "0.0.0",
        "qgis_maximum_version": "0.0.1",
        "description": "this is a test",
        "about": "testing",
        "version": "1.0.0",
        "author_name": "me",
        "email": "*****@*****.**",
        "changelog": "done stuff",
        "experimental": "True",
        "deprecated": "False",
        "tags": "test",
        "homepage": "plugin.com",
        "repository": "github/plugin",
        "tracker": "github/pluigin/issues",
        "icon": "icon.png",
        "category": "test",
        "file_name": file_name,
    }

    result = MetadataModel(
        id=metadata.get("id"),
        item_version=metadata.get("item_version"),
        revisions=metadata.get("revisions"),
        name=metadata.get("name"),
        qgis_minimum_version=metadata.get("qgis_minimum_version"),
        qgis_maximum_version=metadata.get("qgis_maximum_version"),
        description=metadata.get("description"),
        about=metadata.get("about"),
        version=metadata.get("version"),
        author_name=metadata.get("author_name"),
        email=metadata.get("email"),
        changelog=metadata.get("changelog"),
        experimental=metadata.get("experimental"),
        deprecated=metadata.get("deprecated"),
        tags=metadata.get("tags"),
        homepage=metadata.get("homepage"),
        repository=metadata.get("repository"),
        tracker=metadata.get("tracker"),
        icon=metadata.get("icon"),
        category=metadata.get("category"),
        file_name=metadata.get("file_name"),
    )

    result.save()

    for key in metadata:
        assert result.attribute_values[key] == metadata[key]