Skip to content

Commit

Permalink
Merge pull request #720 from weni-ai/feature/internals-update-project
Browse files Browse the repository at this point in the history
Feature/internals update project
  • Loading branch information
AlisoSouza authored Sep 8, 2023
2 parents 233d75b + f6442a0 commit 7a93aff
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 263 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ omit =
./connect/billing/tests.py
./connect/billing/tests_billingv2.py
./connect/billing/migrations/*
./connect/billing/gateway.py
./connect/billing/gateways/*
./connect/template_projects/migrations/*
./connect/template_projects/tests.py
./connect/elastic/tests.py
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# 3.2.0

## *Add*
- Cursor pagination to v2 organizations and v2 projects

# 3.1.0

## *update*
- Update UserIsPaying viewset query params and authorizations

## *Delete*
- Remove unused tasks and models

# 3.0.0

## *Add*
Expand Down
17 changes: 0 additions & 17 deletions connect/api/v1/tests/test_v2_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,11 @@
Invoice
)
from connect.common.mocks import StripeMockGateway
from unittest import skipIf
from unittest.mock import patch
import pendulum
from freezegun import freeze_time
from connect.billing.tasks import end_trial_plan, check_organization_plans, daily_contact_count
from rest_framework import status
import stripe
from django.conf import settings
from connect.api.v1.billing.views import BillingViewSet
from connect.common.tasks import generate_project_invoice

Expand Down Expand Up @@ -418,8 +415,6 @@ def test_upgrade_plan_empty_failure(self):
class BillingViewTestCase(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.stripe = stripe
self.stripe.api_key = settings.BILLING_SETTINGS.get("stripe", {}).get("API_KEY")
self.customer = ""
self.owner, self.owner_token = create_user_and_token("owner")

Expand Down Expand Up @@ -454,18 +449,6 @@ def request_create_org(self, data, token=None):
content_data = json.loads(response.content)
return (response, content_data)

@skipIf("stripe" not in settings.BILLING_SETTINGS, "Stripe not configured")
def test_setup_intent(self):
response, content_data = self.request(path="setup-intent", method="setup_intent")

# setup card
stripe.Customer.create_source(
content_data.get("customer"),
source="tok_visa",
)
self.customer = content_data.get("customer")
self.assertEqual(content_data.get("customer"), "cus_MYOrndkgpPHGK9")

@patch("connect.common.signals.update_user_permission_project")
@patch("connect.billing.get_gateway")
def test_setup_plan(self, mock_get_gateway, mock_permission):
Expand Down
14 changes: 14 additions & 0 deletions connect/api/v2/internals/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,26 @@
RequestPermissionOrganization,
OrganizationLevelRole,
OrganizationRole,
Project,
)

User = get_user_model()
logger = logging.getLogger(__name__)


class InternalProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = [
"uuid",
"name",
"flow_id"
]

uuid = serializers.UUIDField(read_only=True)
name = serializers.CharField(read_only=True)


class CustomParameterSerializer(serializers.Serializer):
project_uuid = serializers.UUIDField(help_text="querystring parameter")

Expand Down
11 changes: 10 additions & 1 deletion connect/api/v2/internals/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@
from rest_framework import views
from rest_framework.response import Response

from connect.common.models import Organization
from connect.common.models import Organization, Project
from connect.api.v2.internals.serializers import (
OrganizationAISerializer,
CustomParameterSerializer,
InternalProjectSerializer,
)
from connect.api.v1.internal.permissions import ModuleHasPermission

from django.shortcuts import get_object_or_404

from drf_yasg2.utils import swagger_auto_schema
from rest_framework.viewsets import ModelViewSet


class InternalProjectViewSet(ModelViewSet):
permission_classes = [ModuleHasPermission]
queryset = Project.objects.all()
lookup_field = "uuid"
serializer_class = InternalProjectSerializer


class AIGetOrganizationView(views.APIView):
Expand Down
1 change: 1 addition & 0 deletions connect/api/v2/routers.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@
"internals/connect/organizations/",
connect_internal_views.AIGetOrganizationView.as_view(),
),
path("internals/connect/projects/<uuid>", connect_internal_views.InternalProjectViewSet.as_view({"patch": "partial_update"}))
]
16 changes: 16 additions & 0 deletions connect/api/v2/user/permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from rest_framework.permissions import BasePermission
from django.conf import settings


class HasValidMarketingPermission(BasePermission):

def has_permission(self, request, view):
token = request.META.get("HTTP_AUTHORIZATION")
if token is None or not token.startswith("Bearer "):
return False

return token == settings.VERIFICATION_MARKETING_TOKEN

def has_object_permission(self, request, view, obj):
print("entro aqui obj permission")
return self.has_permission(request, view)
43 changes: 14 additions & 29 deletions connect/api/v2/user/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from unittest import skipIf
from unittest.mock import Mock, patch
from django.test import TestCase, RequestFactory, override_settings
from rest_framework.test import APIRequestFactory
Expand Down Expand Up @@ -60,13 +59,11 @@ def test_get_user_api_token(self, mock_get_user_api_token):
self.assertEqual(response.status_code, status.HTTP_200_OK)


@skipIf(True, "View need to be refactored")
class UserIsPayingTestCase(TestCase):

@patch("connect.common.signals.update_user_permission_project")
@patch("connect.billing.get_gateway")
def setUp(self, mock_get_gateway, mock_permission):
self.factory = APIRequestFactory()
self.owner, self.owner_token = create_user_and_token("owner")
mock_get_gateway.return_value = StripeMockGateway()
mock_permission.return_value = True
Expand All @@ -86,41 +83,29 @@ def setUp(self, mock_get_gateway, mock_permission):
)
self.view = UserIsPaying.as_view()

@override_settings(VERIFICATION_MARKETING_TOKEN="valid_marketing_token")
def request(self, token, user_email):
self.factory = APIRequestFactory()
url = f"/v2/account/user-is-paying?user_email={user_email}"
request = self.factory.get(url, format="json")
request.META["HTTP_AUTHORIZATION"] = f"Bearer {token}"
return request

@override_settings(VERIFICATION_MARKETING_TOKEN="Bearer valid_marketing_token")
def test_get_endpoint_valid_token_no_auth(self):
url = "/v2/account/user-is-paying"
user_email = self.owner.email
token = "valid_marketing_token"

data = {"user_email": user_email, "token": token}
request = self.factory.get(url, data=data, format="json")

response = self.view(request)
response = self.view(self.request(token, self.owner.email))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_get_endpoint_invalid_token(self):
invalid_token = "invalid_marketing_token"
response = self.view(self.request(invalid_token, self.owner.email))
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

request = self.factory.get(
"/v2/account/user-is-paying",
data={
"user_email": self.owner.email,
"token": "invalid_marketing_token"
}
)
response = self.view(request)

self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

@override_settings(VERIFICATION_MARKETING_TOKEN="valid_marketing_token")
@override_settings(VERIFICATION_MARKETING_TOKEN="Bearer valid_marketing_token")
def test_get_endpoint_valid_token(self):
self.organization.authorizations.create(
user=self.owner, role=OrganizationRole.ADMIN.value
)

url = "/v2/account/user-is-paying"
user_email = self.owner.email
token = "valid_marketing_token"

response = self.client.get(url, {"user_email": user_email, "token": token})

response = self.view(self.request(token, self.owner.email))
self.assertEqual(response.status_code, status.HTTP_200_OK)
27 changes: 11 additions & 16 deletions connect/api/v2/user/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from rest_framework import views, status

from django.conf import settings
from django.http import JsonResponse

from connect.api.v1.internal.flows.flows_rest_client import FlowsRESTClient
from connect.common.models import Project, OrganizationAuthorization, BillingPlan
from .permission import HasValidMarketingPermission
from rest_framework.response import Response


class UserAPIToken(views.APIView): # pragma: no cover
Expand All @@ -20,32 +21,26 @@ def get(self, request, *args, **kwargs):
return JsonResponse(status=response.status_code, data=response.json())


class UserIsPaying(views.APIView): # pragma: no cover
class UserIsPaying(views.APIView):

def get(self, request, *args, **kwargs):
authentication_classes = []
permission_classes = [HasValidMarketingPermission]

user_email = request.data.get("user_email")
token = request.data.get("token")
if token is None:
token = request.query_params.get("token")
def get(self, request, *args, **kwargs):
user_email = request.query_params.get("user_email")
if user_email is None:
user_email = request.query_params.get("user_email")

if token != settings.VERIFICATION_MARKETING_TOKEN:
return JsonResponse(status=status.HTTP_401_UNAUTHORIZED, data={"message": "You don't have permission to do this action"})
return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": "You must provide a user_email"})

org_auth = OrganizationAuthorization.objects.filter(user__email=user_email)
response_data = []
if len(org_auth) == 0:
return JsonResponse(status=status.HTTP_404_NOT_FOUND, data={"message": "This user doesn't have permission on any organization"})
return Response(status=status.HTTP_404_NOT_FOUND, data={"message": "This user doesn't have permission on any organization"})

for auth in org_auth:
current_body = {
"org_uuid": auth.organization.uuid,
"is_paying": auth.organization.organization_billing.plan != BillingPlan.PLAN_TRIAL,
"project": [],
"project": [project.uuid for project in auth.organization.project.all()],
}
for project in auth.organization.project.all():
current_body["project"].append(project.uuid)
response_data.append({auth.organization.name: current_body})
return JsonResponse(status=status.HTTP_200_OK, data=dict(orgs=response_data))
return Response(status=status.HTTP_200_OK, data=dict(orgs=response_data))
24 changes: 24 additions & 0 deletions connect/billing/migrations/0010_auto_20230830_2014.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.20 on 2023-08-30 20:14

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('billing', '0009_contactcount_day'),
]

operations = [
migrations.RemoveField(
model_name='contact',
name='channel',
),
migrations.RemoveField(
model_name='contactcount',
name='channel',
),
migrations.DeleteModel(
name='Channel',
),
]
39 changes: 0 additions & 39 deletions connect/billing/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,6 @@ class SyncManagerTask(models.Model):
after = models.DateTimeField(_("after"))


class Channel(models.Model):
CHANNEL_CHOICES = [
("WA", _("WhatsApp")),
("TG", _("Telegram")),
]
uuid = models.UUIDField(
_("UUID"), primary_key=True, default=uuid4.uuid4, editable=False
)
channel_type = models.CharField(
_("channel_type"), max_length=150, choices=CHANNEL_CHOICES
)
channel_flow_id = models.PositiveIntegerField(_("channel id"), unique=True)
project = models.ForeignKey("common.Project", models.CASCADE, related_name="channel")

@staticmethod
def create(*args, **kwargs):
if not Channel.channel_exists(kwargs["channel_flow_id"]):
channel = Channel.objects.create(
project=kwargs["project"],
channel_flow_id=kwargs["channel_flow_id"],
channel_type=kwargs["channel_type"],
)
else:
channel = Channel.objects.get(channel_flow_id=kwargs["channel_flow_id"])
return channel

@staticmethod
def channel_exists(channel_flow_id):
return Channel.objects.filter(channel_flow_id=channel_flow_id).exists()


class ContactManager(models.Manager):
def create(self, *args, **kwargs):
contact = self.get_contact(kwargs.get("contact_flow_uuid"))
Expand All @@ -85,17 +54,12 @@ class Contact(models.Model):
contact_flow_uuid = models.UUIDField(_("flow identification UUID"))
name = models.CharField(_("contact name"), max_length=150, blank=True, null=True)
last_seen_on = models.DateTimeField(blank=True, null=True)
channel = models.ForeignKey(Channel, models.CASCADE, related_name="channel", null=True)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(blank=True, null=True)
project = models.ForeignKey("common.Project", models.CASCADE, related_name="contacts", null=True)

objects = ContactManager()

def update_channel(self, channel):
self.channel = channel
self.save(update_fields=["channel"])


class Message(models.Model):
uuid = models.UUIDField(
Expand All @@ -109,9 +73,6 @@ class Message(models.Model):


class ContactCount(models.Model):
channel = models.ForeignKey(
Channel, models.CASCADE, related_name="contact_count_channel", null=True
)
count = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
day = models.DateTimeField(null=True)
Expand Down
Loading

0 comments on commit 7a93aff

Please sign in to comment.