diff --git a/.github/workflows/build-integrations-engine-push-tag-shared.yaml b/.github/workflows/build-integrations-engine-push-tag-shared.yaml new file mode 100644 index 00000000..47832ead --- /dev/null +++ b/.github/workflows/build-integrations-engine-push-tag-shared.yaml @@ -0,0 +1,133 @@ +name: Build Weni Integrations Engine in Shared (Push Tag) + +on: + push: + tags: + - '*.*.*-develop' + - '*.*.*-staging' + - '*.*.*' + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Set variables + run: | + TAG="$( echo "${GITHUB_REF}" | cut -d'/' -f3 )" + if grep -qs -e '^.*.*-develop' <<< "${TAG}" ; then + echo "Found environment: DEVELOP - ${TAG}" + echo "MANIFESTS_ENVIRONMENT=develop" | tee -a "${GITHUB_ENV}" + elif grep -qs -e '^.*.*-staging' <<< "${TAG}" ; then + echo "Found environment: STAGING - ${TAG}" + echo "MANIFESTS_ENVIRONMENT=staging" | tee -a "${GITHUB_ENV}" + elif grep -qs -e '^.*.*' <<< "${TAG}" ; then + echo "No environment found, assuming: PRODUCTION - ${TAG}" + echo "MANIFESTS_ENVIRONMENT=production" | tee -a "${GITHUB_ENV}" + else + echo 'Not a valid tag. Skipping...' + exit 1 + fi + echo "TAG=$TAG" | tee -a "${GITHUB_ENV}" + VERSION="${TAG}" + echo "VERSION=${VERSION}" | tee -a "${GITHUB_ENV}" + echo "COMMIT_SHA=$GITHUB_SHA" | tee -a "${GITHUB_ENV}" + echo "IMAGE_TAG=${{ secrets.ECR_SHARED }}/integrations:engine-$TAG" | tee -a "${GITHUB_ENV}" + echo "IMAGE_SOURCE_URL=https://github.com/weni-ai/weni-integrations-engine" | tee -a "${GITHUB_ENV}" + echo "MANIFESTS_REPOSITORY=Ilhasoft/kubernetes-manifests-platform" | tee -a "${GITHUB_ENV}" + echo "MANIFESTS_APPLICATION=integrations-engine" | tee -a "${GITHUB_ENV}" + echo "MANIFESTS_PATCH_TARGET=deployment-engine.json" | tee -a "${GITHUB_ENV}" + + - name: Check out the repo + uses: actions/checkout@v3 + with: + ref: "${{env.GITHUB_SHA}}" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to ECR + uses: docker/login-action@v2 + with: + registry: ${{ secrets.ECR_SHARED }} + username: ${{ secrets.AWS_ACCESS_KEY_ID_SHARED }} + password: ${{ secrets.AWS_SECRET_ACCESS_KEY_SHARED }} + + - name: Build and push - Weni Integrations Engine Image + uses: docker/build-push-action@v3 + with: + context: . + labels: | + tag=${{env.TAG}} + commit=${{env.COMMIT_SHA}} + repository=${{env.IMAGE_SOURCE_URL}} + file: docker/Dockerfile + push: true + tags: "${{env.IMAGE_TAG}}" + no-cache: true + + - name: Check out Kubernetes Manifests + uses: actions/checkout@master + with: + ref: main + repository: "${{ env.MANIFESTS_REPOSITORY }}" + token: "${{ secrets.DEVOPS_GITHUB_PERMANENT_TOKEN }}" + path: ./kubernetes-manifests/ + + - name: Update image on deployment + run: | + which jq > /dev/null 2>&1 || ( sudo apt update ; sudo apt install -y jq ) + # Dep: coreutils + verlte() { + [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ] + } + verlt(){ + [ "$1" = "$2" ] && return 1 || verlte $1 $2 + } + export PROJECT_DIR="${{ env.MANIFESTS_APPLICATION }}" + ENV_DIR="kubernetes-manifests/${{ env.MANIFESTS_APPLICATION }}/${MANIFESTS_ENVIRONMENT}" + for e in ${ENV_DIR}; do + echo "Update ${e}:" + if [ ! -d "${e}" ] ; then + echo "${e}: Does not exist, skipping" + elif [ ! -r "${e}/kustomization.yaml" ] ; then + echo "${e}/kustomization.yaml: Does not readable, skipping" + elif [ ! -r "${e}/${{ env.MANIFESTS_PATCH_TARGET }}" ] ; then + echo "${e}/${{ env.MANIFESTS_PATCH_TARGET }}: Does not readable, skipping" + else + OLD_IMAGE=$( + cat "${e}/${{ env.MANIFESTS_PATCH_TARGET }}" \ + | jq '.[] | select(.path == "/spec/template/spec/containers/0/image") | .value' + ) + echo "Old image to replace: ${OLD_IMAGE}" + OLD_VERSION=$( + echo "${OLD_IMAGE}" \ + | sed s'/^.*[v:-]\([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/'g \ + | head -n1 + ) + echo "Old image version to compare: ${OLD_VERSION}<=${{env.VERSION}}" + if verlte "${OLD_VERSION}" "${VERSION}" || [[ ! "${OLD_VERSION}" =~ [0-9]+\.[0-9]+\.[0-9]+ ]] ; then + echo 'New configurations:' + new_configuration=$( + cat "${e}/${{ env.MANIFESTS_PATCH_TARGET }}" \ + | jq '(..|select(.path == "/spec/template/spec/containers/0/image")?) += {value: "'"${{env.IMAGE_TAG}}"'"}' + ) + echo "${new_configuration}" + echo "${new_configuration}" > "${e}/${{ env.MANIFESTS_PATCH_TARGET }}" + else + echo "Version in file is greater than build, skipping update yaml" + fi + fi + done + + - name: Commit & Push changes + uses: actions-js/push@master + with: + github_token: "${{ secrets.DEVOPS_GITHUB_PERMANENT_TOKEN }}" + repository: "${{ env.MANIFESTS_REPOSITORY }}" + directory: ./kubernetes-manifests/ + branch: main + message: "From Weni Integrations Engine Build (Push Tag ${{ env.MANIFESTS_ENVIRONMENT }})" + diff --git a/marketplace/core/types/channels/whatsapp_cloud/requests.py b/marketplace/core/types/channels/whatsapp_cloud/requests.py index c900139a..6470dca6 100644 --- a/marketplace/core/types/channels/whatsapp_cloud/requests.py +++ b/marketplace/core/types/channels/whatsapp_cloud/requests.py @@ -79,8 +79,8 @@ def get_phone_number(self, phone_number_id: str): return response.json() -class PhotoAPIRequest(object): +class PhotoAPIRequest(object): def __init__(self, access_token: str, phone_number_id: str) -> None: self._access_token = access_token self._phone_number_id = phone_number_id @@ -88,26 +88,25 @@ def __init__(self, access_token: str, phone_number_id: str) -> None: @property def _headers(self) -> dict: return {"Authorization": f"Bearer {self._access_token}"} - + def _get_url(self, endpoint: str) -> str: return f"{settings.WHATSAPP_API_URL}/{endpoint}" - def create_upload_session(self, access_token : str, file_length : int, file_type : str) -> str: - url = self._get_url(f"/app/uploads?access_token={self._access_token}&file_length={file_length}&file_type={file_type}") + def create_upload_session(self, access_token: str, file_length: int, file_type: str) -> str: + url = self._get_url( + f"/app/uploads?access_token={self._access_token}&file_length={file_length}&file_type={file_type}" + ) response = requests.post(url, headers=self._headers) if response.status_code != status.HTTP_200_OK: raise FacebookApiException(response.json()) return response.json().get("id", "") - - def upload_photo(self, upload_session_id: str, photo : str, is_uploading : bool = False) -> str: + + def upload_photo(self, upload_session_id: str, photo: str, is_uploading: bool = False) -> str: url = self._get_url(f"/{upload_session_id}") - headers = { - "Content-Type": photo.content_type, - "Authorization": f"OAuth {self._access_token}" - } + headers = {"Content-Type": photo.content_type, "Authorization": f"OAuth {self._access_token}"} if not is_uploading: headers["file_offset"] = 0 @@ -119,18 +118,16 @@ def upload_photo(self, upload_session_id: str, photo : str, is_uploading : bool return response.json().get("h", "") - def set_photo(self, photo): url = self._get_url(f"/{self._phone_number_id}/whatsapp_business_profile") - - upload_session_id = self.create_upload_session(self._access_token, len(photo.file.getvalue()), file_type=photo.content_type) + + upload_session_id = self.create_upload_session( + self._access_token, len(photo.file.getvalue()), file_type=photo.content_type + ) upload_handle = upload_photo(upload_session_id, photo) - payload = { - "messaging_product": "whatsapp", - "profile_picture_handle": upload_handle - } + payload = {"messaging_product": "whatsapp", "profile_picture_handle": upload_handle} response = requests.post(url, headers=self._headers, json=payload) diff --git a/marketplace/core/types/channels/whatsapp_cloud/views.py b/marketplace/core/types/channels/whatsapp_cloud/views.py index 9d16d942..35ba4ef2 100644 --- a/marketplace/core/types/channels/whatsapp_cloud/views.py +++ b/marketplace/core/types/channels/whatsapp_cloud/views.py @@ -1,6 +1,8 @@ import string from typing import TYPE_CHECKING +from django.http import JsonResponse +from billiard.pool import MaybeEncodingError from rest_framework.response import Response from rest_framework.exceptions import ValidationError from rest_framework import status @@ -118,7 +120,16 @@ def create(self, request, *args, **kwargs): task = celery_app.send_task( name="create_wac_channel", args=[request.user.email, project_uuid, phone_number_id, config] ) - task.wait() + try: + task.wait() + except MaybeEncodingError: + return JsonResponse( + { + "error": "Maybe a Channel with that 'phone_number_id' alredy exists", + "error_type": "WhatsApp.config.error.channel_already_exists", + }, + status=status.HTTP_409_CONFLICT, + ) config["title"] = config.get("wa_number") config["wa_allocation_config_id"] = allocation_config_id