diff --git a/.github/workflows/maced-contract-canary.yml b/.github/workflows/maced-contract-canary.yml new file mode 100644 index 0000000000..ab1d9675bc --- /dev/null +++ b/.github/workflows/maced-contract-canary.yml @@ -0,0 +1,35 @@ +name: Maced contract canary + +on: + pull_request: + paths: + - 'apps/api/src/security-penetration-tests/**' + - 'apps/api/test/maced-contract.e2e-spec.ts' + - 'apps/api/package.json' + - '.github/workflows/maced-contract-canary.yml' + schedule: + - cron: '0 * * * *' + workflow_dispatch: + +permissions: + contents: read + +jobs: + maced-contract-canary: + runs-on: warp-ubuntu-latest-arm64-4x + timeout-minutes: 15 + env: + MACED_API_KEY: ${{ secrets.MACED_API_KEY }} + MACED_CONTRACT_E2E_RUN_ID: ${{ secrets.MACED_CONTRACT_E2E_RUN_ID }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/dangerous-git-checkout + - name: Install Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install dependencies + run: bun install --frozen-lockfile + - name: Run Maced provider contract canary + working-directory: ./apps/api + run: bun run test:e2e:maced diff --git a/apps/api/package.json b/apps/api/package.json index 11bedd1330..7202037f9c 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -126,6 +126,7 @@ "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", + "test:e2e:maced": "MACED_CONTRACT_E2E=1 jest --config ./test/jest-e2e.json --runInBand ./maced-contract.e2e-spec.ts", "test:watch": "jest --watch", "typecheck": "tsc --noEmit" } diff --git a/apps/api/src/email/templates/automation-bulk-failures.tsx b/apps/api/src/email/templates/automation-bulk-failures.tsx new file mode 100644 index 0000000000..a9f77ea9a2 --- /dev/null +++ b/apps/api/src/email/templates/automation-bulk-failures.tsx @@ -0,0 +1,143 @@ +import * as React from 'react'; +import { + Body, + Button, + Container, + Font, + Heading, + Html, + Link, + Preview, + Section, + Tailwind, + Text, +} from '@react-email/components'; +import { Footer } from '../components/footer'; +import { Logo } from '../components/logo'; +import { getUnsubscribeUrl } from '@trycompai/email'; + +interface FailedTaskItem { + title: string; + url: string; + failedCount: number; + totalCount: number; +} + +interface Props { + toName: string; + toEmail: string; + organizationName: string; + tasksUrl: string; + tasks: FailedTaskItem[]; +} + +const MAX_DISPLAYED_TASKS = 15; + +export const AutomationBulkFailuresEmail = ({ + toName, + toEmail, + organizationName, + tasksUrl, + tasks, +}: Props) => { + const unsubscribeUrl = getUnsubscribeUrl(toEmail); + const taskCount = tasks.length; + const taskText = taskCount === 1 ? 'task' : 'tasks'; + const displayedTasks = tasks.slice(0, MAX_DISPLAYED_TASKS); + const remainingCount = taskCount - displayedTasks.length; + + return ( + + + + + + + + {`${taskCount} ${taskText} with automation failures`} + + + + + + + Automation Failures Summary + + + + Hello {toName}, + + + + Today's scheduled automations found failures in{' '} + {taskCount} {taskText} in{' '} + {organizationName}. + + +
+ {displayedTasks.map((task, index) => ( + + {'• '} + + {task.title} + + {' '}({task.failedCount}/{task.totalCount} failed) + + ))} + {remainingCount > 0 && ( + + and {remainingCount} more... + + )} +
+ +
+ +
+ + + or copy and paste this URL into your browser:{' '} + + {tasksUrl} + + + +
+ + Don't want to receive task assignment notifications?{' '} + + Manage your email preferences + + . + +
+ +
+ +