# What this PR does Remove [`apps.get_model`](https://docs.djangoproject.com/en/3.2/ref/applications/#django.apps.apps.get_model) invocations and use inline `import` statements in places where models are imported within functions/methods to avoid circular imports. I believe `import` statements are more appropriate for most use cases as they allow for better static code analysis & formatting, and solve the issue of circular imports without being unnecessarily dynamic as `apps.get_model`. With `import` statements, it's possible to: - Jump to model definitions in most IDEs - Automatically sort inline imports with `isort` - Find import errors faster/easier (most IDEs highlight broken imports) - Have more consistency across regular & inline imports when importing models This PR also adds a flake8 rule to ban imports of `django.apps.apps`, so it's harder to use `apps.get_model` by mistake (it's possible to ignore this rule by using `# noqa: I251`). The rule is not enforced on directories with migration files, because `apps.get_model` is often used to get a historical state of a model, which is useful when writing migrations ([see this SO answer for more details](https://stackoverflow.com/a/37769213)). So `apps.get_model` is considered OK in migrations (even necessary in some cases). ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required)
172 lines
7.1 KiB
Python
172 lines
7.1 KiB
Python
import json
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from django.conf import settings
|
|
from django.test import override_settings
|
|
from django.urls import reverse
|
|
from rest_framework import status
|
|
from rest_framework.test import APIClient
|
|
|
|
GRAFANA_TOKEN = "TEST_TOKEN"
|
|
STACK_ID = 1
|
|
ORG_ID = 5
|
|
GRAFANA_API_URL = "hello.com"
|
|
LICENSE = "OpenSource"
|
|
STACK_SLUG = "asdfasdf"
|
|
ORG_SLUG = "hellooo"
|
|
ORG_TITLE = "nmvcnmvnmvc"
|
|
REGION_SLUG = "nmcvnmcvnmcvnmcv"
|
|
CLUSTER_SLUG = "nmcvnmcvnmcvnmcvnmcv"
|
|
SELF_HOSTED_SETTINGS = {
|
|
"GRAFANA_API_URL": GRAFANA_API_URL,
|
|
"STACK_ID": STACK_ID,
|
|
"ORG_ID": ORG_ID,
|
|
"LICENSE": LICENSE,
|
|
"STACK_SLUG": STACK_SLUG,
|
|
"ORG_SLUG": ORG_SLUG,
|
|
"ORG_TITLE": ORG_TITLE,
|
|
"REGION_SLUG": REGION_SLUG,
|
|
"CLUSTER_SLUG": CLUSTER_SLUG,
|
|
}
|
|
|
|
UNABLE_TO_FIND_GRAFANA_ERROR_MSG = f"Unable to connect to the specified Grafana API - {GRAFANA_API_URL}"
|
|
UNAUTHED_GRAFANA_API_ERROR_MSG = (
|
|
f"You are not authorized to communicate with the specified Grafana API - {GRAFANA_API_URL}"
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def make_self_hosted_install_header():
|
|
def _make_instance_context_header(token):
|
|
return {
|
|
"HTTP_X-Instance-Context": json.dumps({"grafana_token": token}),
|
|
}
|
|
|
|
return _make_instance_context_header
|
|
|
|
|
|
@override_settings(LICENSE=settings.CLOUD_LICENSE_NAME)
|
|
def test_a_cloud_license_gets_an_unauthorized_error(make_self_hosted_install_header):
|
|
client = APIClient()
|
|
url = reverse("grafana-plugin:self-hosted-install")
|
|
response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))
|
|
|
|
assert response.status_code == status.HTTP_403_FORBIDDEN
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"grafana_api_status_code,expected_error_msg",
|
|
[
|
|
(status.HTTP_404_NOT_FOUND, UNABLE_TO_FIND_GRAFANA_ERROR_MSG),
|
|
(status.HTTP_401_UNAUTHORIZED, UNAUTHED_GRAFANA_API_ERROR_MSG),
|
|
(status.HTTP_401_UNAUTHORIZED, UNAUTHED_GRAFANA_API_ERROR_MSG),
|
|
],
|
|
)
|
|
@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient")
|
|
def test_it_properly_handles_errors_from_the_grafana_api(
|
|
mocked_grafana_api_client, make_self_hosted_install_header, grafana_api_status_code, expected_error_msg
|
|
):
|
|
mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": grafana_api_status_code})
|
|
|
|
client = APIClient()
|
|
url = reverse("grafana-plugin:self-hosted-install")
|
|
response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))
|
|
|
|
assert mocked_grafana_api_client.called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
|
|
assert mocked_grafana_api_client.return_value.check_token.called_once_with()
|
|
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
assert response.data["error"] == expected_error_msg
|
|
|
|
|
|
@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
|
|
@pytest.mark.django_db
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient")
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.sync_organization")
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.provision_plugin")
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.revoke_plugin")
|
|
def test_if_organization_exists_it_is_updated(
|
|
mocked_revoke_plugin,
|
|
mocked_provision_plugin,
|
|
mocked_sync_organization,
|
|
mocked_grafana_api_client,
|
|
make_self_hosted_install_header,
|
|
make_organization,
|
|
):
|
|
organization = make_organization(stack_id=STACK_ID, org_id=ORG_ID)
|
|
provision_plugin_response = {"stackId": STACK_ID, "orgId": ORG_ID, "onCallToken": "HELLOOO", "license": LICENSE}
|
|
|
|
mocked_provision_plugin.return_value = provision_plugin_response
|
|
mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": status.HTTP_200_OK})
|
|
mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.return_value = True
|
|
|
|
client = APIClient()
|
|
url = reverse("grafana-plugin:self-hosted-install")
|
|
response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))
|
|
|
|
assert mocked_grafana_api_client.called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
|
|
assert mocked_grafana_api_client.return_value.check_token.called_once_with()
|
|
assert mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.called_once_with()
|
|
|
|
assert mocked_sync_organization.called_once_with(organization)
|
|
assert mocked_provision_plugin.called_once_with()
|
|
assert mocked_revoke_plugin.called_once_with()
|
|
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
assert response.data == {"error": None, **provision_plugin_response}
|
|
|
|
organization.refresh_from_db()
|
|
|
|
assert organization.grafana_url == GRAFANA_API_URL
|
|
assert organization.api_token == GRAFANA_TOKEN
|
|
assert organization.is_rbac_permissions_enabled is True
|
|
|
|
|
|
@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
|
|
@pytest.mark.django_db
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient")
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.sync_organization")
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.provision_plugin")
|
|
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.revoke_plugin")
|
|
def test_if_organization_does_not_exist_it_is_created(
|
|
mocked_revoke_plugin,
|
|
mocked_provision_plugin,
|
|
mocked_sync_organization,
|
|
mocked_grafana_api_client,
|
|
make_self_hosted_install_header,
|
|
):
|
|
provision_plugin_response = {"stackId": STACK_ID, "orgId": ORG_ID, "onCallToken": "HELLOOO", "license": LICENSE}
|
|
|
|
mocked_provision_plugin.return_value = provision_plugin_response
|
|
mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": status.HTTP_200_OK})
|
|
mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.return_value = True
|
|
|
|
client = APIClient()
|
|
url = reverse("grafana-plugin:self-hosted-install")
|
|
response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))
|
|
|
|
from apps.user_management.models import Organization
|
|
|
|
organization = Organization.objects.filter(stack_id=STACK_ID, org_id=ORG_ID).first()
|
|
|
|
assert mocked_grafana_api_client.called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
|
|
assert mocked_grafana_api_client.return_value.check_token.called_once_with()
|
|
assert mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.called_once_with()
|
|
|
|
assert mocked_sync_organization.called_once_with(organization)
|
|
assert mocked_provision_plugin.called_once_with()
|
|
assert not mocked_revoke_plugin.called
|
|
|
|
assert response.status_code == status.HTTP_201_CREATED
|
|
assert response.data == {"error": None, **provision_plugin_response}
|
|
|
|
assert organization.stack_id == STACK_ID
|
|
assert organization.stack_slug == STACK_SLUG
|
|
assert organization.org_slug == ORG_SLUG
|
|
assert organization.org_title == ORG_TITLE
|
|
assert organization.region_slug == REGION_SLUG
|
|
assert organization.grafana_url == GRAFANA_API_URL
|
|
assert organization.api_token == GRAFANA_TOKEN
|
|
assert organization.is_rbac_permissions_enabled is True
|