Skip to content

Commit

Permalink
feat: viewsets and models for wpp-cloud catalog and product
Browse files Browse the repository at this point in the history
  • Loading branch information
elitonzky committed Sep 1, 2023
1 parent 9d5cf80 commit f128ec8
Show file tree
Hide file tree
Showing 16 changed files with 937 additions and 5 deletions.
27 changes: 27 additions & 0 deletions marketplace/clients/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import requests

from marketplace.clients.exceptions import CustomAPIException


class RequestClient:
def make_request(
self, url: str, method: str, headers=None, data=None, params=None, files=None
):
response = requests.request(

Check warning on line 10 in marketplace/clients/base.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/base.py#L10

Added line #L10 was not covered by tests
method=method,
url=url,
headers=headers,
json=data,
timeout=60,
params=params,
files=files,
)
if response.status_code >= 500:
raise CustomAPIException(status_code=response.status_code)
elif response.status_code >= 400:
raise CustomAPIException(

Check warning on line 22 in marketplace/clients/base.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/base.py#L19-L22

Added lines #L19 - L22 were not covered by tests
detail=response.json() if response.text else response.text,
status_code=response.status_code,
)

return response

Check warning on line 27 in marketplace/clients/base.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/base.py#L27

Added line #L27 was not covered by tests
7 changes: 7 additions & 0 deletions marketplace/clients/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from rest_framework.exceptions import APIException


class CustomAPIException(APIException):
def __init__(self, detail=None, code=None, status_code=None):
super().__init__(detail, code)
self.status_code = status_code or self.status_code

Check warning on line 7 in marketplace/clients/exceptions.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/exceptions.py#L6-L7

Added lines #L6 - L7 were not covered by tests
127 changes: 127 additions & 0 deletions marketplace/clients/facebook/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import time

from django.conf import settings

from marketplace.clients.base import RequestClient

WHATSAPP_VERSION = settings.WHATSAPP_VERSION
ACCESS_TOKEN = settings.WHATSAPP_SYSTEM_USER_ACCESS_TOKEN


class FacebookAuthorization:
BASE_URL = f"https://graph.facebook.com/{WHATSAPP_VERSION}/"

def __init__(self):
self.access_token = ACCESS_TOKEN

Check warning on line 15 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L15

Added line #L15 was not covered by tests

def _get_headers(self):
headers = {"Authorization": f"Bearer {self.access_token}"}
return headers

Check warning on line 19 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L18-L19

Added lines #L18 - L19 were not covered by tests

@property
def get_url(self):
return self.BASE_URL

Check warning on line 23 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L23

Added line #L23 was not covered by tests


class FacebookClient(FacebookAuthorization, RequestClient):
def create_catalog(self, business_id, name, category=None):
url = self.get_url + f"{business_id}/owned_product_catalogs"
data = {"name": name}
if category:
data["vertical"] = category

Check warning on line 31 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L28-L31

Added lines #L28 - L31 were not covered by tests

headers = self._get_headers()
response = self.make_request(url, method="POST", headers=headers, data=data)

Check warning on line 34 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L33-L34

Added lines #L33 - L34 were not covered by tests

return response.json()

Check warning on line 36 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L36

Added line #L36 was not covered by tests

def destroy_catalog(self, catalog_id):
url = self.get_url + f"{catalog_id}"

Check warning on line 39 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L39

Added line #L39 was not covered by tests

headers = self._get_headers()
response = self.make_request(url, method="DELETE", headers=headers)

Check warning on line 42 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L41-L42

Added lines #L41 - L42 were not covered by tests

return response.json().get("success")

Check warning on line 44 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L44

Added line #L44 was not covered by tests

def create_product_feed(self, product_catalog_id, name):
url = self.get_url + f"{product_catalog_id}/product_feeds"

Check warning on line 47 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L47

Added line #L47 was not covered by tests

data = {"name": name}
headers = self._get_headers()
response = self.make_request(url, method="POST", headers=headers, data=data)

Check warning on line 51 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L49-L51

Added lines #L49 - L51 were not covered by tests

return response.json()

Check warning on line 53 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L53

Added line #L53 was not covered by tests

def upload_product_feed(self, feed_id, file):
url = self.get_url + f"{feed_id}/uploads"

Check warning on line 56 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L56

Added line #L56 was not covered by tests

headers = self._get_headers()
files = {

Check warning on line 59 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L58-L59

Added lines #L58 - L59 were not covered by tests
"file": (
file.name,
file,
file.content_type,
)
}
response = self.make_request(url, method="POST", headers=headers, files=files)
return response.json()

Check warning on line 67 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L66-L67

Added lines #L66 - L67 were not covered by tests

def get_upload_status(self, feed_id, max_attempts=10, wait_time=30):
"""
Checks the upload status using long polling.
Args:
upload_id (str): The ID of the upload.
max_attempts (int): Maximum number of polling attempts. Default is 10.
wait_time (int): Wait time in seconds between polling attempts. Default is 30 seconds.
Returns:
bool or str: True if 'end_time' is found, otherwise a formatted error message.
"""
url = self.get_url + f"{feed_id}/uploads"
headers = self._get_headers()

Check warning on line 82 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L81-L82

Added lines #L81 - L82 were not covered by tests

attempts = 0
while attempts < max_attempts:
response = self.make_request(url, method="GET", headers=headers)
data = response.json()

Check warning on line 87 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L84-L87

Added lines #L84 - L87 were not covered by tests

if data.get("data") and data["data"][0].get("end_time"):
return True

Check warning on line 90 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L89-L90

Added lines #L89 - L90 were not covered by tests

time.sleep(wait_time)
attempts += 1

Check warning on line 93 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L92-L93

Added lines #L92 - L93 were not covered by tests

total_wait_time = wait_time * max_attempts
return (

Check warning on line 96 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L95-L96

Added lines #L95 - L96 were not covered by tests
f"Unable to retrieve the upload completion status for feed {feed_id}. "
f"Waited for a total of {total_wait_time} seconds."
)

def list_products_by_feed(self, feed_id):
url = self.get_url + f"{feed_id}/products"

Check warning on line 102 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L102

Added line #L102 was not covered by tests

headers = self._get_headers()
response = self.make_request(url, method="GET", headers=headers)

Check warning on line 105 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L104-L105

Added lines #L104 - L105 were not covered by tests

return response.json()

Check warning on line 107 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L107

Added line #L107 was not covered by tests

def list_all_products_by_feed(self, feed_id):
url = self.get_url + f"{feed_id}/products"
headers = self._get_headers()
all_products = []

Check warning on line 112 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L110-L112

Added lines #L110 - L112 were not covered by tests

while url:
response = self.make_request(url, method="GET", headers=headers).json()
all_products.extend(response.get("data", []))
url = response.get("paging", {}).get("next")

Check warning on line 117 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L114-L117

Added lines #L114 - L117 were not covered by tests

return all_products

Check warning on line 119 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L119

Added line #L119 was not covered by tests

def destroy_feed(self, feed_id):
url = self.get_url + f"{feed_id}"

Check warning on line 122 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L122

Added line #L122 was not covered by tests

headers = self._get_headers()
response = self.make_request(url, method="DELETE", headers=headers)

Check warning on line 125 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L124-L125

Added lines #L124 - L125 were not covered by tests

return response.json().get("success")

Check warning on line 127 in marketplace/clients/facebook/client.py

View check run for this annotation

Codecov / codecov/patch

marketplace/clients/facebook/client.py#L127

Added line #L127 was not covered by tests
37 changes: 37 additions & 0 deletions marketplace/core/types/channels/whatsapp_cloud/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django.urls import path

from .views import CatalogViewSet, ProductFeedViewSet


urlpatterns = [
path(
"<uuid:app_uuid>/catalogs/",
CatalogViewSet.as_view({"post": "create", "get": "list"}),
name="catalog-list-create",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/",
CatalogViewSet.as_view({"get": "retrieve", "delete": "destroy"}),
name="catalog-detail-destroy",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/products/",
CatalogViewSet.as_view({"get": "list_products"}),
name="catalog-products-list",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/product_feeds/",
ProductFeedViewSet.as_view({"post": "create", "get": "list"}),
name="product-feed-list-create",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/product_feeds/<uuid:feed_uuid>/",
ProductFeedViewSet.as_view({"get": "retrieve", "delete": "destroy"}),
name="product-feed-detail-destroy",
),
path(
"<uuid:app_uuid>/catalogs/<uuid:catalog_uuid>/product_feeds/<uuid:feed_uuid>/products/",
ProductFeedViewSet.as_view({"get": "list_products"}),
name="product-feed-products-list",
),
]
Loading

0 comments on commit f128ec8

Please sign in to comment.