Skip to content

Commit

Permalink
[FLOWS-212] - Create catalog linked to the Vtex App (#376)
Browse files Browse the repository at this point in the history
* Create catalog linked to the Vtex App, add services to manipulate Vtex data, adjust the Facebook client
  • Loading branch information
elitonzky authored Dec 21, 2023
1 parent f38bc51 commit 64ce29e
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 40 deletions.
4 changes: 2 additions & 2 deletions marketplace/clients/facebook/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def get_url(self):

class FacebookClient(FacebookAuthorization, RequestClient):
# Product Catalog
def create_catalog(self, business_id, name, category=None):
def create_catalog(self, business_id, name, category="commerce"):
url = self.get_url + f"{business_id}/owned_product_catalogs"
data = {"name": name}
if category:
Expand Down Expand Up @@ -71,7 +71,7 @@ def upload_product_feed(self, feed_id, file, update_only=False):
)
return response.json()

def create_product_feed_via_url(
def create_product_feed_by_url(
self, product_catalog_id, name, feed_url, file_type, interval, hour
): # TODO: adjust this method
url = self.get_url + f"{product_catalog_id}/product_feeds"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ class MockFacebookService:
def __init__(self, *args, **kwargs):
pass

def create_vtex_catalog(self, validated_data, app, vtex_app, user):
if validated_data["name"] == "valid_catalog":
return (Catalog(app=app, facebook_catalog_id="123456789"), "123456789")
else:
return (None, None)

def catalog_deletion(self, catalog):
if catalog.facebook_catalog_id == "123456789":
return True
else:
return False

def enable_catalog(self, catalog):
return {"success": "True"}

Expand Down Expand Up @@ -47,6 +59,18 @@ def setUp(self):
name="catalog test",
category="commerce",
)
self.catalog_success = Catalog.objects.create(
app=self.app,
facebook_catalog_id="123456789",
name="valid_catalog",
category="commerce",
)
self.catalog_failure = Catalog.objects.create(
app=self.app,
facebook_catalog_id="987654321",
name="invlid_catalog",
category="commerce",
)
self.user_authorization = self.user.authorizations.create(
project_uuid=self.app.project_uuid
)
Expand Down Expand Up @@ -74,13 +98,13 @@ class CatalogListTestCase(MockServiceTestCase):
current_view_mapping = {"get": "list"}

def test_list_catalogs(self):
url = reverse("catalog-list", kwargs={"app_uuid": self.app.uuid})
url = reverse("catalog-list-create", kwargs={"app_uuid": self.app.uuid})
response = self.request.get(url, app_uuid=self.app.uuid)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.json["results"]), 1)
self.assertEqual(len(response.json["results"]), 3)

def test_filter_by_name(self):
url = reverse("catalog-list", kwargs={"app_uuid": self.app.uuid})
url = reverse("catalog-list-create", kwargs={"app_uuid": self.app.uuid})

response = self.client.get(url, {"name": "catalog test"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
Expand All @@ -96,7 +120,7 @@ class CatalogRetrieveTestCase(MockServiceTestCase):

def test_retreive_catalog(self):
url = reverse(
"catalog-detail",
"catalog-detail-delete",
kwargs={"app_uuid": self.app.uuid, "catalog_uuid": self.catalog.uuid},
)
response = self.request.get(
Expand Down Expand Up @@ -146,9 +170,115 @@ def test_list_catalog_with_connected_catalog(self):
name="another catalog test",
category="commerce",
)
url = reverse("catalog-list", kwargs={"app_uuid": self.app.uuid})
url = reverse("catalog-list-create", kwargs={"app_uuid": self.app.uuid})
response = self.request.get(url, app_uuid=self.app.uuid)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.json["results"]), 2)
self.assertEqual(len(response.json["results"]), 4)
self.assertTrue(response.json["results"][0]["is_connected"])


class CatalogDestroyTestCase(MockServiceTestCase):
current_view_mapping = {"delete": "destroy"}

def test_delete_catalog_success(self):
url = reverse(
"catalog-detail-delete",
kwargs={"app_uuid": self.app.uuid, "catalog_uuid": self.catalog.uuid},
)

response = self.request.delete(
url, app_uuid=self.app.uuid, catalog_uuid=self.catalog_success.uuid
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

def test_delete_catalog_failure(self):
url = reverse(
"catalog-detail-delete",
kwargs={"app_uuid": self.app.uuid, "catalog_uuid": self.catalog.uuid},
)
response = self.request.delete(
url, app_uuid=self.app.uuid, catalog_uuid=self.catalog_failure.uuid
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data["detail"], "Failed to delete catalog on Facebook."
)


class CatalogCreateTestCase(MockServiceTestCase):
current_view_mapping = {"post": "create"}

def setUp(self):
super().setUp()
# Configures a vtex App for an already created wpp-cloud App
self.vtex_app_configured = App.objects.create(
code="vtex",
created_by=self.user,
config={"domain": "valid_domain"},
configured=True,
project_uuid=self.app.project_uuid,
platform=App.PLATFORM_WENI_FLOWS,
)
# Creation of a wpp-cloud App to simulate a link with two Vtex Apps
self.app_double_vtex = App.objects.create(
code="wpp-cloud",
created_by=self.user,
project_uuid=str(uuid.uuid4()),
platform=App.PLATFORM_WENI_FLOWS,
)
# Create a Vtex App 01 and 02 with repeating the project_uuid to simulate duplicity of integration
self.vtex_app_01 = App.objects.create(
code="vtex",
created_by=self.user,
config={"domain": "double_domain"},
configured=True,
project_uuid=self.app_double_vtex.project_uuid,
platform=App.PLATFORM_WENI_FLOWS,
)
self.vtex_app_02 = App.objects.create(
code="vtex",
created_by=self.user,
config={"domain": "double_domain"},
configured=True,
project_uuid=self.app_double_vtex.project_uuid,
platform=App.PLATFORM_WENI_FLOWS,
)
# Create a wpp-cloud without App-vtex linked to the project
self.app_without_vtex = App.objects.create(
code="wpp-cloud",
created_by=self.user,
project_uuid=str(uuid.uuid4()),
platform=App.PLATFORM_WENI_FLOWS,
)

def test_create_catalog_with_vtex_app(self):
data = {"name": "valid_catalog"}
url = reverse("catalog-list-create", kwargs={"app_uuid": self.app.uuid})

response = self.request.post(url, data, app_uuid=self.app.uuid)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response.data["facebook_catalog_id"], "123456789")

def test_create_catalog_with_unconfigured_app(self):
data = {"name": "valid_catalog"}
url = reverse(
"catalog-list-create", kwargs={"app_uuid": self.app_without_vtex.uuid}
)

response = self.request.post(url, data, app_uuid=self.app_without_vtex.uuid)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(response.data["detail"], "There is no VTEX App configured.")

def test_create_catalog_with_multiple_configured_apps(self):
data = {"name": "valid_catalog"}
url = reverse(
"catalog-list-create", kwargs={"app_uuid": self.app_double_vtex.uuid}
)

response = self.request.post(url, data, app_uuid=self.app_double_vtex.uuid)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data["detail"],
"Multiple VTEX Apps are configured, which is not expected.",
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
catalog_patterns = [
path(
"<uuid:app_uuid>/catalogs/",
CatalogViewSet.as_view({"get": "list"}),
name="catalog-list",
CatalogViewSet.as_view({"get": "list", "post": "create"}),
name="catalog-list-create",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/",
CatalogViewSet.as_view({"get": "retrieve"}),
name="catalog-detail",
CatalogViewSet.as_view({"get": "retrieve", "delete": "destroy"}),
name="catalog-detail-delete",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/enable/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
TresholdSerializer,
CatalogListSerializer,
)
from marketplace.services.vtex.generic_service import VtexService


class BaseViewSet(viewsets.ModelViewSet):
Expand All @@ -49,6 +50,17 @@ class Pagination(PageNumberPagination):
class CatalogViewSet(BaseViewSet):
serializer_class = CatalogSerializer
pagination_class = Pagination
vtex_generic_service_class = VtexService

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._vtex_app_service = None

@property
def vtex_service(self): # pragma: no cover
if not self._vtex_app_service:
self._vtex_app_service = self.vtex_generic_service_class()
return self._vtex_app_service

def filter_queryset(self, queryset):
params = self.request.query_params
Expand All @@ -68,6 +80,24 @@ def get_object(self):
catalog_uuid = self.kwargs.get("catalog_uuid")
return get_object_or_404(queryset, uuid=catalog_uuid)

def create(self, request, app_uuid, *args, **kwargs):
app = get_object_or_404(App, uuid=app_uuid, code="wpp-cloud")
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)

vtex_app = self.vtex_service.get_vtex_app_or_error(app.project_uuid)

catalog, _fba_catalog_id = self.fb_service.create_vtex_catalog(
serializer.validated_data, app, vtex_app, self.request.user
)
if not catalog:
return Response(

Check warning on line 94 in marketplace/core/types/channels/whatsapp_cloud/catalogs/views/views.py

View check run for this annotation

Codecov / codecov/patch

marketplace/core/types/channels/whatsapp_cloud/catalogs/views/views.py#L94

Added line #L94 was not covered by tests
{"detail": "Failed to create catalog on Facebook."},
status=status.HTTP_400_BAD_REQUEST,
)

return Response(CatalogSerializer(catalog).data, status=status.HTTP_201_CREATED)

def retrieve(self, request, *args, **kwargs):
catalog = self.get_object()
connected_catalog_id = self.fb_service.get_connected_catalog(catalog.app)
Expand All @@ -92,6 +122,16 @@ def list(self, request, *args, **kwargs):

return self.get_paginated_response(serialized_data)

def destroy(self, request, *args, **kwargs):
success = self.fb_service.catalog_deletion(self.get_object())
if not success:
return Response(
{"detail": "Failed to delete catalog on Facebook."},
status=status.HTTP_400_BAD_REQUEST,
)

return Response(status=status.HTTP_204_NO_CONTENT)

@action(detail=True, methods=["POST"])
def enable_catalog(self, request, *args, **kwargs):
response = self.fb_service.enable_catalog(self.get_object())
Expand Down
Loading

0 comments on commit 64ce29e

Please sign in to comment.