diff --git a/.github/workflows/build-mailroom-push-tag-shared.yaml b/.github/workflows/build-mailroom-push-tag-shared.yaml index 84abb5464..26b87447d 100644 --- a/.github/workflows/build-mailroom-push-tag-shared.yaml +++ b/.github/workflows/build-mailroom-push-tag-shared.yaml @@ -10,7 +10,20 @@ on: jobs: docker: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + platform: + - linux/amd64 + - linux/arm64 steps: + - name: Check out the repo + uses: actions/checkout@v4 + if: github.event_name != 'pull_request' + with: + ref: "${{env.GITHUB_SHA}}" + token: ${{ secrets.DEVOPS_GITHUB_PERMANENT_TOKEN }} + - name: Set variables run: | TAG="$( echo "${GITHUB_REF}" | cut -d'/' -f3 )" @@ -36,6 +49,8 @@ jobs: echo "MANIFESTS_REPOSITORY=weni-ai/kubernetes-manifests-platform" | tee -a "${GITHUB_ENV}" echo "MANIFESTS_APPLICATION=weni-flows/mailroom" | tee -a "${GITHUB_ENV}" echo "MANIFESTS_PATCH_TARGET=deployment.json" | tee -a "${GITHUB_ENV}" + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" | tee -a "${GITHUB_ENV}" - name: Check out the repo uses: actions/checkout@v3 @@ -43,20 +58,21 @@ jobs: ref: "${{env.GITHUB_SHA}}" - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ secrets.ECR_SHARED }} username: ${{ secrets.AWS_ACCESS_KEY_ID_SHARED }} password: ${{ secrets.AWS_SECRET_ACCESS_KEY_SHARED }} - name: Build and push - Mailroom Image - uses: docker/build-push-action@v3 + id: build + uses: docker/build-push-action@v6 with: context: . labels: | @@ -64,10 +80,78 @@ jobs: commit=${{env.COMMIT_SHA}} repository=${{env.IMAGE_SOURCE_URL}} file: docker/Dockerfile - platforms: linux/amd64,linux/arm64 - push: true - tags: "${{env.IMAGE_TAG}}" - no-cache: true + platforms: ${{ matrix.platform }} + # push: true + # tags: "${{env.IMAGE_TAG}}" + # no-cache: true + outputs: type=image,name=${{ secrets.ECR_SHARED }}/mailroom,push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: + - docker + 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 }}/mailroom:$TAG" | tee -a "${GITHUB_ENV}" + echo "MANIFESTS_REPOSITORY=weni-ai/kubernetes-manifests-platform" | tee -a "${GITHUB_ENV}" + echo "MANIFESTS_APPLICATION=weni-flows/mailroom" | tee -a "${GITHUB_ENV}" + echo "MANIFESTS_PATCH_TARGET=deployment.json" | tee -a "${GITHUB_ENV}" + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to ECR + uses: docker/login-action@v3 + if: github.event_name != 'pull_request' + with: + registry: ${{ secrets.ECR_SHARED }} + username: ${{ secrets.AWS_ACCESS_KEY_ID_SHARED }} + password: ${{ secrets.AWS_SECRET_ACCESS_KEY_SHARED }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create -t "${{ env.IMAGE_TAG }}" \ + $(printf '${{ secrets.ECR_SHARED }}/mailroom@sha256:%s ' *) - name: Check out Kubernetes Manifests uses: actions/checkout@master diff --git a/WENI-CHANGELOG.md b/WENI-CHANGELOG.md index 7c4eb6e34..676fdc466 100644 --- a/WENI-CHANGELOG.md +++ b/WENI-CHANGELOG.md @@ -1,3 +1,7 @@ +1.39.1 +---------- + * Update goflow to v1.4.1 + 1.39.0 ---------- * Feat: WhatsApp Order Details diff --git a/core/goflow/engine.go b/core/goflow/engine.go index 0d3c3ea87..df5bf0481 100644 --- a/core/goflow/engine.go +++ b/core/goflow/engine.go @@ -7,6 +7,7 @@ import ( "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/engine" "github.com/nyaruka/goflow/services/brain" + "github.com/nyaruka/goflow/services/meta" "github.com/nyaruka/goflow/services/webhooks" "github.com/nyaruka/goflow/services/wenigpt" "github.com/nyaruka/mailroom/runtime" @@ -75,6 +76,7 @@ func Engine(c *runtime.Config) flows.Engine { WithWebhookServiceFactory(webhooks.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes)). WithWeniGPTServiceFactory(wenigpt.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes, c.WenigptAuthToken, c.WenigptBaseURL)). WithBrainServiceFactory(brain.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes, c.RouterAuthToken, c.RouterBaseURL)). + WithMetaServiceFactory(meta.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes, c.WhatsappSystemUserToken, c.MetaWebhookURL)). WithClassificationServiceFactory(classificationFactory(c)). WithEmailServiceFactory(emailFactory(c)). WithTicketServiceFactory(ticketFactory(c)). @@ -104,6 +106,7 @@ func Simulator(c *runtime.Config) flows.Engine { WithWebhookServiceFactory(webhooks.NewServiceFactory(httpClient, nil, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes)). WithWeniGPTServiceFactory(wenigpt.NewServiceFactory(httpClient, nil, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes, c.WenigptAuthToken, c.WenigptBaseURL)). WithBrainServiceFactory(brain.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes, c.RouterAuthToken, c.RouterBaseURL)). + WithMetaServiceFactory(meta.NewServiceFactory(httpClient, httpRetries, httpAccess, webhookHeaders, c.WebhooksMaxBodyBytes, c.WhatsappSystemUserToken, c.MetaWebhookURL)). WithClassificationServiceFactory(classificationFactory(c)). // simulated sessions do real classification WithExternalServiceServiceFactory(externalServiceFactory((c))). // and real external services WithMsgCatalogServiceFactory(msgCatalogFactory((c))). // msg catalog diff --git a/core/handlers/meta_called.go b/core/handlers/meta_called.go new file mode 100644 index 000000000..acb3bfb86 --- /dev/null +++ b/core/handlers/meta_called.go @@ -0,0 +1,57 @@ +package handlers + +import ( + "context" + "time" + + "github.com/nyaruka/goflow/flows" + "github.com/nyaruka/goflow/flows/events" + "github.com/nyaruka/mailroom/core/hooks" + "github.com/nyaruka/mailroom/core/models" + "github.com/nyaruka/mailroom/runtime" + + "github.com/jmoiron/sqlx" + "github.com/sirupsen/logrus" +) + +func init() { + models.RegisterEventHandler(events.TypeMetaCalled, handleMetaCalled) +} + +// handleServiceCalled is called for each service called event +func handleMetaCalled(ctx context.Context, rt *runtime.Runtime, tx *sqlx.Tx, oa *models.OrgAssets, scene *models.Scene, e flows.Event) error { + event := e.(*events.MetaCalledEvent) + + run, _ := scene.Session().FindStep(e.StepUUID()) + flow, _ := oa.Flow(run.FlowReference().UUID) + + if flow != nil { + // create a log for each HTTP call + for _, httpLog := range event.HTTPLogs { + logrus.WithFields(logrus.Fields{ + "contact_uuid": scene.ContactUUID(), + "session_id": scene.SessionID(), + "url": httpLog.URL, + "status": httpLog.Status, + "elapsed_ms": httpLog.ElapsedMS, + }).Debug("meta called") + + log := models.NewWebhookCalledLog( + oa.OrgID(), + flow.(*models.Flow).ID(), + httpLog.URL, + httpLog.StatusCode, + httpLog.Request, + httpLog.Response, + httpLog.Status != flows.CallStatusSuccess, + time.Millisecond*time.Duration(httpLog.ElapsedMS), + httpLog.Retries, + event.CreatedOn(), + scene.ContactID(), + ) + scene.AppendToEventPreCommitHook(hooks.InsertHTTPLogsHook, log) + } + } + + return nil +} diff --git a/mailroom.go b/mailroom.go index 40938c3c1..2f4d0e779 100644 --- a/mailroom.go +++ b/mailroom.go @@ -8,8 +8,8 @@ import ( "time" "github.com/nyaruka/gocommon/storage" + "github.com/nyaruka/goflow/flows" "github.com/nyaruka/goflow/flows/routers" - "github.com/nyaruka/goflow/services/webhooks" "github.com/nyaruka/mailroom/core/queue" "github.com/nyaruka/mailroom/runtime" "github.com/nyaruka/mailroom/web" @@ -77,7 +77,7 @@ func NewMailroom(config *runtime.Config) *Mailroom { routers.SetZeroshotAPIURL(mr.rt.Config.ZeroshotAPIUrl) // set whatsapp system user token to be used in goflow - webhooks.SetWhatsAppSystemUserToken(mr.rt.Config.WhatsappSystemUserToken) + flows.SetWhatsAppSystemUserToken(mr.rt.Config.WhatsappSystemUserToken) return mr } diff --git a/runtime/config.go b/runtime/config.go index 1b83dde81..67c65c430 100644 --- a/runtime/config.go +++ b/runtime/config.go @@ -95,6 +95,7 @@ type Config struct { RouterAuthToken string `help:"router authorization token"` WhatsappSystemUserToken string `help:"WhatsApp system user token"` + MetaWebhookURL string `help:"Meta webhook URL"` } // NewDefaultConfig returns a new default configuration object @@ -161,6 +162,7 @@ func NewDefaultConfig() *Config { RouterAuthToken: "", WhatsappSystemUserToken: "", + MetaWebhookURL: "https://graph.facebook.com/v21.0", } }