Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow creating integration configurations where deleted versions exist #2531

Merged
merged 2 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions api/integrations/amplitude/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from rest_framework import serializers

from integrations.amplitude.models import AmplitudeConfiguration
from integrations.common.serializers import (
BaseEnvironmentIntegrationModelSerializer,
)


class AmplitudeConfigurationSerializer(serializers.ModelSerializer):
class AmplitudeConfigurationSerializer(BaseEnvironmentIntegrationModelSerializer):
class Meta:
model = AmplitudeConfiguration
fields = ("id", "api_key")
4 changes: 2 additions & 2 deletions api/integrations/amplitude/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from integrations.amplitude.models import AmplitudeConfiguration
from integrations.amplitude.serializers import AmplitudeConfigurationSerializer
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet


class AmplitudeConfigurationViewSet(IntegrationCommonViewSet):
class AmplitudeConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = AmplitudeConfigurationSerializer
pagination_class = None # set here to ensure documentation is correct
model_class = AmplitudeConfiguration
2 changes: 1 addition & 1 deletion api/integrations/common/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ class BaseEnvironmentIntegrationModelSerializer(_BaseIntegrationModelSerializer)


class BaseProjectIntegrationModelSerializer(_BaseIntegrationModelSerializer):
one_to_one_field_name = "project"
one_to_one_field_name = "project_id"
32 changes: 31 additions & 1 deletion api/integrations/common/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework.exceptions import (
NotFound,
Expand All @@ -7,9 +8,10 @@

from environments.models import Environment
from environments.permissions.constants import VIEW_ENVIRONMENT
from projects.permissions import VIEW_PROJECT


class IntegrationCommonViewSet(viewsets.ModelViewSet):
class EnvironmentIntegrationCommonViewSet(viewsets.ModelViewSet):
serializer_class = None
pagination_class = None # set here to ensure documentation is correct
model_class = None
Expand Down Expand Up @@ -52,3 +54,31 @@ def get_environment_from_request(self):
Get environment object from URL parameters in request.
"""
return Environment.objects.get(api_key=self.kwargs["environment_api_key"])


class ProjectIntegrationBaseViewSet(viewsets.ModelViewSet):
serializer_class = None
pagination_class = None
model_class = None

def get_queryset(self):
if getattr(self, "swagger_fake_view", False):
return self.model_class.objects.none()

project = get_object_or_404(
self.request.user.get_permitted_projects(VIEW_PROJECT),
pk=self.kwargs["project_pk"],
)
return self.model_class.objects.filter(project=project)

def perform_create(self, serializer):
project_id = self.kwargs["project_pk"]
if self.model_class.objects.filter(project_id=project_id).exists():
raise ValidationError(
f"{self.model_class.__name__} for this project already exists."
)
serializer.save(project_id=project_id)

def perform_update(self, serializer):
project_id = self.kwargs["project_pk"]
serializer.save(project_id=project_id)
33 changes: 3 additions & 30 deletions api/integrations/datadog/views.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,8 @@
from rest_framework import viewsets
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404

from integrations.common.views import ProjectIntegrationBaseViewSet
from integrations.datadog.models import DataDogConfiguration
from integrations.datadog.serializers import DataDogConfigurationSerializer
from projects.permissions import VIEW_PROJECT


class DataDogConfigurationViewSet(viewsets.ModelViewSet):
class DataDogConfigurationViewSet(ProjectIntegrationBaseViewSet):
serializer_class = DataDogConfigurationSerializer
pagination_class = None # set here to ensure documentation is correct

def get_queryset(self):
if getattr(self, "swagger_fake_view", False):
return DataDogConfiguration.objects.none()

project = get_object_or_404(
self.request.user.get_permitted_projects(VIEW_PROJECT),
pk=self.kwargs["project_pk"],
)
return DataDogConfiguration.objects.filter(project=project)

def perform_create(self, serializer):
project_id = self.kwargs["project_pk"]
if DataDogConfiguration.objects.filter(project_id=project_id).exists():
raise ValidationError(
"DataDogConfiguration for this project already exist."
)

serializer.save(project_id=project_id)

def perform_update(self, serializer):
project_id = self.kwargs["project_pk"]
serializer.save(project_id=project_id)
model_class = DataDogConfiguration
4 changes: 2 additions & 2 deletions api/integrations/dynatrace/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.dynatrace.models import DynatraceConfiguration
from integrations.dynatrace.serializers import DynatraceConfigurationSerializer


class DynatraceConfigurationViewSet(IntegrationCommonViewSet):
class DynatraceConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = DynatraceConfigurationSerializer
pagination_class = None # set here to ensure documentation is correct
model_class = DynatraceConfiguration
4 changes: 2 additions & 2 deletions api/integrations/heap/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.heap.models import HeapConfiguration
from integrations.heap.serializers import HeapConfigurationSerializer


class HeapConfigurationViewSet(IntegrationCommonViewSet):
class HeapConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = HeapConfigurationSerializer
model_class = HeapConfiguration
4 changes: 2 additions & 2 deletions api/integrations/mixpanel/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.mixpanel.models import MixpanelConfiguration
from integrations.mixpanel.serializers import MixpanelConfigurationSerializer


class MixpanelConfigurationViewSet(IntegrationCommonViewSet):
class MixpanelConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = MixpanelConfigurationSerializer
model_class = MixpanelConfiguration
33 changes: 3 additions & 30 deletions api/integrations/new_relic/views.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,8 @@
from rest_framework import viewsets
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404

from integrations.common.views import ProjectIntegrationBaseViewSet
from integrations.new_relic.models import NewRelicConfiguration
from integrations.new_relic.serializers import NewRelicConfigurationSerializer
from projects.permissions import VIEW_PROJECT


class NewRelicConfigurationViewSet(viewsets.ModelViewSet):
class NewRelicConfigurationViewSet(ProjectIntegrationBaseViewSet):
serializer_class = NewRelicConfigurationSerializer
pagination_class = None # set here to ensure documentation is correct

def get_queryset(self):
if getattr(self, "swagger_fake_view", False):
return NewRelicConfiguration.objects.none()

project = get_object_or_404(
self.request.user.get_permitted_projects(VIEW_PROJECT),
pk=self.kwargs["project_pk"],
)
return NewRelicConfiguration.objects.filter(project=project)

def perform_create(self, serializer):
project_id = self.kwargs["project_pk"]
if NewRelicConfiguration.objects.filter(project_id=project_id).exists():
raise ValidationError(
"NewRelicConfiguration for this project already exist."
)

serializer.save(project_id=project_id)

def perform_update(self, serializer):
project_id = self.kwargs["project_pk"]
serializer.save(project_id=project_id)
model_class = NewRelicConfiguration
4 changes: 2 additions & 2 deletions api/integrations/rudderstack/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.rudderstack.models import RudderstackConfiguration
from integrations.rudderstack.serializers import (
RudderstackConfigurationSerializer,
)


class RudderstackConfigurationViewSet(IntegrationCommonViewSet):
class RudderstackConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = RudderstackConfigurationSerializer
pagination_class = None # set here to ensure documentation is correct
model_class = RudderstackConfiguration
4 changes: 2 additions & 2 deletions api/integrations/segment/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.segment.models import SegmentConfiguration
from integrations.segment.serializers import SegmentConfigurationSerializer


class SegmentConfigurationViewSet(IntegrationCommonViewSet):
class SegmentConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = SegmentConfigurationSerializer
pagination_class = None # set here to ensure documentation is correct
model_class = SegmentConfiguration
4 changes: 2 additions & 2 deletions api/integrations/slack/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from slack_sdk.oauth import AuthorizeUrlGenerator

from environments.models import Environment
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.slack.models import SlackConfiguration, SlackEnvironment
from integrations.slack.serializers import (
SlackChannelListQueryParamSerializer,
Expand Down Expand Up @@ -58,7 +58,7 @@ def list(self, request, *args, **kwargs):
return Response(serializer.data)


class SlackEnvironmentViewSet(IntegrationCommonViewSet):
class SlackEnvironmentViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = SlackEnvironmentSerializer
pagination_class = None # set here to ensure documentation is correct
model_class = SlackEnvironment
Expand Down
4 changes: 2 additions & 2 deletions api/integrations/webhook/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from integrations.common.views import IntegrationCommonViewSet
from integrations.common.views import EnvironmentIntegrationCommonViewSet
from integrations.webhook.models import WebhookConfiguration
from integrations.webhook.serializers import WebhookConfigurationSerializer


class WebhookConfigurationViewSet(IntegrationCommonViewSet):
class WebhookConfigurationViewSet(EnvironmentIntegrationCommonViewSet):
serializer_class = WebhookConfigurationSerializer
model_class = WebhookConfiguration
12 changes: 12 additions & 0 deletions api/tests/unit/integrations/amplitude/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pytest

from integrations.amplitude.models import AmplitudeConfiguration


@pytest.fixture()
def deleted_amplitude_integration(environment):
amplitude_configuration = AmplitudeConfiguration.objects.create(
environment=environment, api_key="some-key"
)
amplitude_configuration.delete()
return amplitude_configuration
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import json

from django.urls import reverse
from rest_framework import status


def test_create_amplitude_integration(environment, admin_client):
# Given
url = reverse(
"api-v1:environments:integrations-amplitude-list", args=[environment.api_key]
)

# When
response = admin_client.post(
path=url,
data=json.dumps({"api_key": "some-key"}),
content_type="application/json",
)

# Then
assert response.status_code == status.HTTP_201_CREATED


def test_create_amplitude_integration_in_environment_with_deleted_integration(
environment, admin_client, deleted_amplitude_integration
):
# Given
url = reverse(
"api-v1:environments:integrations-amplitude-list", args=[environment.api_key]
)

# When
response = admin_client.post(
path=url,
data=json.dumps({"api_key": "some-key"}),
content_type="application/json",
)

# Then
assert response.status_code == status.HTTP_201_CREATED
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_base_project_integration_model_serializer_save_updates_existing_if_soft
serializer.is_valid(raise_exception=True)

# When
serializer.save(project=project)
serializer.save(project_id=project.id)

# Then
updated_webhook_config = DataDogConfiguration.objects.filter(
Expand Down
Empty file.
12 changes: 12 additions & 0 deletions api/tests/unit/integrations/datadog/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pytest

from integrations.datadog.models import DataDogConfiguration


@pytest.fixture()
def deleted_datadog_configuration(project):
configuration = DataDogConfiguration.objects.create(
project=project, api_key="some-key"
)
configuration.delete()
return configuration
27 changes: 27 additions & 0 deletions api/tests/unit/integrations/datadog/test_unit_datadog_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import json

from django.urls import reverse
from rest_framework import status


def test_create_datadog_configuration_in_project_with_deleted_configuration(
admin_client, project, deleted_datadog_configuration
):
# Given
url = reverse("api-v1:projects:integrations-datadog-list", args=[project.id])

api_key, base_url = "some-key", "https://api.newrelic.com/"

# When
response = admin_client.post(
path=url,
data=json.dumps({"api_key": api_key, "base_url": base_url}),
content_type="application/json",
)

# Then
assert response.status_code == status.HTTP_201_CREATED

response_json = response.json()
assert response_json["api_key"] == api_key
assert response_json["base_url"] == base_url
Empty file.
12 changes: 12 additions & 0 deletions api/tests/unit/integrations/newrelic/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pytest

from integrations.new_relic.models import NewRelicConfiguration


@pytest.fixture()
def deleted_newrelic_configuration(project):
configuration = NewRelicConfiguration.objects.create(
project=project, api_key="some-key"
)
configuration.delete()
return configuration
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json

from django.urls import reverse
from rest_framework import status


def test_create_newrelic_configuration_in_project_with_deleted_configuration(
admin_client, project, deleted_newrelic_configuration
):
# Given
url = reverse("api-v1:projects:integrations-new-relic-list", args=[project.id])

api_key, base_url, app_id = "some-key", "https://api.newrelic.com/", "1"

# When
response = admin_client.post(
path=url,
data=json.dumps({"api_key": api_key, "base_url": base_url, "app_id": app_id}),
content_type="application/json",
)

# Then
assert response.status_code == status.HTTP_201_CREATED

response_json = response.json()
assert response_json["api_key"] == api_key
assert response_json["base_url"] == base_url
assert response_json["app_id"] == app_id