Skip to content

Commit b41a649

Browse files
committed
Merge branch 'next' into nv-6862-js-subscriptions-module
2 parents 5fe18df + 4f4ec3e commit b41a649

File tree

183 files changed

+3379
-987
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

183 files changed

+3379
-987
lines changed

.cspell.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,9 @@
770770
"BLKTM",
771771
"Novuand",
772772
"kobalte",
773-
"jsonrepair"
773+
"jsonrepair",
774+
"xoxb",
775+
"fanout"
774776
],
775777
"flagWords": [],
776778
"patterns": [

.github/workflows/deploy.yml

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ on:
2828
default: staging
2929
options:
3030
- staging
31-
- staging-apse1
31+
- staging-sg
3232
- production-us
3333
- production-eu
34-
- production-apse1
34+
- production-sg
35+
- production-au
36+
- production-uk
37+
- production-jp
38+
- production-kr
3539
- production-us-and-eu
40+
- production-sg-au-uk-jp-kr
3641

3742
deploy_api:
3843
description: "Deploy API"
@@ -89,7 +94,7 @@ jobs:
8994
if [ "${{ github.event.inputs.environment }}" == "staging" ]; then
9095
envs+=("\"staging-eu\"")
9196
fi
92-
if [ "${{ github.event.inputs.environment }}" == "staging-apse1" ]; then
97+
if [ "${{ github.event.inputs.environment }}" == "staging-uk" ]; then
9398
envs+=("\"staging-apse1\"")
9499
fi
95100
if [ "${{ github.event.inputs.environment }}" == "production-us" ]; then
@@ -98,13 +103,32 @@ jobs:
98103
if [ "${{ github.event.inputs.environment }}" == "production-eu" ]; then
99104
envs+=("\"prod-eu\"")
100105
fi
101-
if [ "${{ github.event.inputs.environment }}" == "production-apse1" ]; then
106+
if [ "${{ github.event.inputs.environment }}" == "production-sg" ]; then
102107
envs+=("\"prod-apse1\"")
103108
fi
109+
if [ "${{ github.event.inputs.environment }}" == "production-au" ]; then
110+
envs+=("\"prod-apse2\"")
111+
fi
112+
if [ "${{ github.event.inputs.environment }}" == "production-uk" ]; then
113+
envs+=("\"prod-ew2\"")
114+
fi
115+
if [ "${{ github.event.inputs.environment }}" == "production-jp" ]; then
116+
envs+=("\"prod-apne1\"")
117+
fi
118+
if [ "${{ github.event.inputs.environment }}" == "production-kr" ]; then
119+
envs+=("\"prod-apne2\"")
120+
fi
104121
if [ "${{ github.event.inputs.environment }}" == "production-us-and-eu" ]; then
105122
envs+=("\"prod-us\"")
106123
envs+=("\"prod-eu\"")
107124
fi
125+
if [ "${{ github.event.inputs.environment }}" == "production-sg-au-uk-jp-kr" ]; then
126+
envs+=("\"prod-apse1\"")
127+
envs+=("\"prod-apse2\"")
128+
envs+=("\"prod-ew2\"")
129+
envs+=("\"prod-apne1\"")
130+
envs+=("\"prod-apne2\"")
131+
fi
108132
109133
# Collect selected services
110134
if [ "${{ github.event.inputs.deploy_api }}" == "true" ]; then

.github/workflows/prepare-self-hosted-release.yml

Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,60 @@
11
name: Prepare Self-hosted Release
2+
run-name: >
3+
Building self-hosted${{ github.event.inputs.nightly == 'true' && ' (nightly)' || '' }}:
4+
${{
5+
github.event.inputs.build_api == 'true' && 'api, ' || ''
6+
}}${{
7+
github.event.inputs.build_worker == 'true' && 'worker, ' || ''
8+
}}${{
9+
github.event.inputs.build_dashboard == 'true' && 'dashboard, ' || ''
10+
}}${{
11+
github.event.inputs.build_webhook == 'true' && 'webhook, ' || ''
12+
}}${{
13+
github.event.inputs.build_ws == 'true' && 'ws' || ''
14+
}}
215
316
env:
417
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
518

619
on:
720
push:
821
tags:
9-
- 'v[0-9]+.[0-9]+.[0-9]+'
22+
- "v[0-9]+.[0-9]+.[0-9]+"
1023
workflow_dispatch:
24+
inputs:
25+
nightly:
26+
description: "Build as nightly release"
27+
required: false
28+
default: "false"
29+
type: choice
30+
options:
31+
- "true"
32+
- "false"
33+
build_api:
34+
description: "Build API service"
35+
required: false
36+
type: boolean
37+
default: true
38+
build_worker:
39+
description: "Build Worker service"
40+
required: false
41+
type: boolean
42+
default: true
43+
build_dashboard:
44+
description: "Build Dashboard service"
45+
required: false
46+
type: boolean
47+
default: true
48+
build_webhook:
49+
description: "Build Webhook service"
50+
required: false
51+
type: boolean
52+
default: true
53+
build_ws:
54+
description: "Build WebSocket service"
55+
required: false
56+
type: boolean
57+
default: true
1158

1259
permissions:
1360
contents: write
@@ -16,13 +63,63 @@ permissions:
1663
id-token: write
1764

1865
jobs:
66+
setup_matrix:
67+
runs-on: ubuntu-latest
68+
outputs:
69+
matrix: ${{ steps.set-matrix.outputs.matrix }}
70+
steps:
71+
- name: Validate Selected Services
72+
if: github.event_name == 'workflow_dispatch'
73+
run: |
74+
if [ "${{ github.event.inputs.build_api }}" != "true" ] && \
75+
[ "${{ github.event.inputs.build_worker }}" != "true" ] && \
76+
[ "${{ github.event.inputs.build_dashboard }}" != "true" ] && \
77+
[ "${{ github.event.inputs.build_webhook }}" != "true" ] && \
78+
[ "${{ github.event.inputs.build_ws }}" != "true" ]; then
79+
echo "Error: At least one service must be selected for building."
80+
exit 1
81+
fi
82+
83+
- name: Generate Build Matrix
84+
id: set-matrix
85+
run: |
86+
# If triggered by tag push, build all services
87+
if [ "${{ github.event_name }}" == "push" ]; then
88+
matrix='["novu/api","novu/worker","novu/dashboard","novu/webhook","novu/ws"]'
89+
else
90+
# If triggered by workflow_dispatch, build only selected services
91+
services=()
92+
93+
if [ "${{ github.event.inputs.build_api }}" == "true" ]; then
94+
services+=("\"novu/api\"")
95+
fi
96+
if [ "${{ github.event.inputs.build_worker }}" == "true" ]; then
97+
services+=("\"novu/worker\"")
98+
fi
99+
if [ "${{ github.event.inputs.build_dashboard }}" == "true" ]; then
100+
services+=("\"novu/dashboard\"")
101+
fi
102+
if [ "${{ github.event.inputs.build_webhook }}" == "true" ]; then
103+
services+=("\"novu/webhook\"")
104+
fi
105+
if [ "${{ github.event.inputs.build_ws }}" == "true" ]; then
106+
services+=("\"novu/ws\"")
107+
fi
108+
109+
matrix="[$(IFS=','; echo "${services[*]}")]"
110+
fi
111+
112+
echo "matrix=$matrix" >> $GITHUB_OUTPUT
113+
echo "Building services: $matrix"
114+
19115
build_docker:
116+
needs: setup_matrix
20117
runs-on: ubuntu-latest
21118
timeout-minutes: 90
22119
strategy:
23120
fail-fast: false
24121
matrix:
25-
name: ['novu/api', 'novu/worker', 'novu/dashboard', 'novu/webhook', 'novu/ws']
122+
name: ${{ fromJson(needs.setup_matrix.outputs.matrix) }}
26123
steps:
27124
- name: Git Checkout
28125
uses: actions/checkout@v5
@@ -31,9 +128,21 @@ jobs:
31128
shell: bash
32129
run: |
33130
service=${{ matrix.name }}
34-
LATEST_VERSION=$(jq -r '.version' apps/api/package.json)
35131
SERVICE_NAME=$(basename "${service//-/-}")
36132
SERVICE_COMMON_NAME=$(echo "$SERVICE_NAME" | sed 's/-ee$//')
133+
134+
# Determine version based on nightly flag
135+
IS_NIGHTLY=${{ github.event.inputs.nightly == 'true' }}
136+
if [ "$IS_NIGHTLY" == "true" ]; then
137+
DATE=$(date +'%Y%m%d')
138+
SHORT_SHA=$(git rev-parse --short HEAD)
139+
LATEST_VERSION="nightly-${DATE}-${SHORT_SHA}"
140+
echo "IS_NIGHTLY=true" >> $GITHUB_ENV
141+
else
142+
LATEST_VERSION=$(jq -r '.version' apps/api/package.json)
143+
echo "IS_NIGHTLY=false" >> $GITHUB_ENV
144+
fi
145+
37146
echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_ENV
38147
echo "SERVICE_NAME=$SERVICE_NAME" >> $GITHUB_ENV
39148
echo "SERVICE_COMMON_NAME=$SERVICE_COMMON_NAME" >> $GITHUB_ENV
@@ -47,7 +156,7 @@ jobs:
47156
uses: actions/setup-node@v4
48157
with:
49158
node-version: 20.19.0
50-
cache: 'pnpm'
159+
cache: "pnpm"
51160

52161
- name: Install dependencies
53162
run: pnpm install --frozen-lockfile
@@ -71,7 +180,7 @@ jobs:
71180
- name: Set Up Docker Buildx
72181
uses: docker/setup-buildx-action@v3
73182
with:
74-
driver-opts: 'image=moby/buildkit:v0.13.1'
183+
driver-opts: "image=moby/buildkit:v0.13.1"
75184

76185
- uses: ./.github/actions/free-space
77186
name: Extend space in Action Container
@@ -129,6 +238,12 @@ jobs:
129238
shell: bash
130239
run: |
131240
docker tag novu-$SERVICE_COMMON_NAME ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:${{ env.LATEST_VERSION }}
132-
docker tag novu-$SERVICE_COMMON_NAME ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:latest
133241
docker push ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:${{ env.LATEST_VERSION }}
134-
docker push ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:latest
242+
243+
if [ "${{ env.IS_NIGHTLY }}" == "true" ]; then
244+
docker tag novu-$SERVICE_COMMON_NAME ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:nightly
245+
docker push ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:nightly
246+
else
247+
docker tag novu-$SERVICE_COMMON_NAME ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:latest
248+
docker push ghcr.io/$REGISTRY_OWNER/${{ matrix.name }}:latest
249+
fi

.source

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Add workflow_id column to step_runs table
2+
-- This column stores the workflow template ID for each step execution
3+
ALTER TABLE step_runs
4+
ADD COLUMN IF NOT EXISTS workflow_id String DEFAULT '';
5+
6+
7+
-- Add workflow_id column to traces table
8+
-- This column stores the workflow template ID for each trace
9+
ALTER TABLE traces
10+
ADD COLUMN IF NOT EXISTS workflow_id String DEFAULT '';

apps/api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@novu/api-service",
3-
"version": "3.9.0",
3+
"version": "3.11.0",
44
"description": "description",
55
"author": "",
66
"private": "true",
@@ -32,7 +32,7 @@
3232
"test:e2e:novu-v0": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --timeout 15000 --retries 3 --grep '#novu-v0' --require ts-node/register --exit --file e2e/setup.ts src/**/*.e2e{,-ee}.ts",
3333
"test:e2e:novu-v2": "cross-env NODE_ENV=test CI_EE_TEST=true CLERK_ENABLED=true NODE_OPTIONS=--max_old_space_size=8192 mocha --timeout 30000 --retries 3 --grep '#novu-v2' --require ./swc-register.js --exit --file e2e/setup.ts 'src/**/*.e2e{,-ee}.ts' 'e2e/enterprise/**/*.e2e.ts'",
3434
"migration": "cross-env NODE_ENV=local MIGRATION=true ts-node --transpileOnly",
35-
"clickhouse:migrate": "clickhouse-migrations migrate --host=http://localhost:8123 --user=default --password= --db=novu-local --migrations-home=./migrations/clickhouse-migrations",
35+
"clickhouse:migrate:local": "clickhouse-migrations migrate --host=http://localhost:8123 --user=default --password= --db=novu-local --migrations-home=./migrations/clickhouse-migrations",
3636
"clickhouse:migrate:prod": "clickhouse-migrations migrate --migrations-home=./migrations/clickhouse-migrations",
3737
"link:submodules": "pnpm link ../../enterprise/packages/auth && pnpm link ../../enterprise/packages/translation && pnpm link ../../enterprise/packages/billing",
3838
"admin:remove-user-account": "cross-env NODE_ENV=local MIGRATION=true ts-node --transpileOnly ./admin/remove-user-account.ts",

apps/api/src/app/activity/dtos/shared.dto.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ export class GetWorkflowRunResponseBaseDto {
7676
@IsBoolean()
7777
critical: boolean;
7878

79-
@ApiPropertyOptional({ description: 'Contexts (keys) in which the workflow run was executed', type: [String] })
79+
@ApiPropertyOptional({
80+
description: 'Context (single or multi) in which the workflow run was executed',
81+
type: [String],
82+
})
8083
@IsOptional()
8184
@IsArray()
8285
@IsString({ each: true })

apps/api/src/app/activity/dtos/workflow-runs-request.dto.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,17 @@ export class GetWorkflowRunsRequestDto {
6666
severity?: SeverityLevelEnum[];
6767

6868
@IsOptional()
69-
@Transform(({ value }) => (Array.isArray(value) ? value : [value]))
69+
@Transform(({ value }) => {
70+
// No parameter = no filter
71+
if (value === undefined) return undefined;
72+
73+
// Empty string = filter for records with no (default) context
74+
if (value === '') return [];
75+
76+
// Normalize to array and remove empty strings
77+
const array = Array.isArray(value) ? value : [value];
78+
return array.filter((v) => v !== '');
79+
})
7080
@IsArray()
7181
@IsString({ each: true })
7282
contextKeys?: string[];

apps/api/src/app/activity/usecases/build-active-subscribers-chart/build-active-subscribers-chart.command.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { EnvironmentCommand } from '@novu/application-generic';
2-
import { IsDate, IsDefined } from 'class-validator';
2+
import { IsArray, IsDate, IsDefined, IsOptional, IsString } from 'class-validator';
33

44
export class BuildActiveSubscribersChartCommand extends EnvironmentCommand {
55
@IsDate()
@@ -9,4 +9,9 @@ export class BuildActiveSubscribersChartCommand extends EnvironmentCommand {
99
@IsDate()
1010
@IsDefined()
1111
endDate: Date;
12+
13+
@IsOptional()
14+
@IsArray()
15+
@IsString({ each: true })
16+
workflowIds?: string[];
1217
}

apps/api/src/app/activity/usecases/build-active-subscribers-chart/build-active-subscribers-chart.usecase.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class BuildActiveSubscribersChart {
1414

1515
@InstrumentUsecase()
1616
async execute(command: BuildActiveSubscribersChartCommand): Promise<ActiveSubscribersDataPointDto> {
17-
const { environmentId, organizationId, startDate, endDate } = command;
17+
const { environmentId, organizationId, startDate, endDate, workflowIds } = command;
1818

1919
// Calculate previous period dates
2020
const periodDuration = endDate.getTime() - startDate.getTime();
@@ -27,7 +27,8 @@ export class BuildActiveSubscribersChart {
2727
startDate,
2828
endDate,
2929
previousStartDate,
30-
previousEndDate
30+
previousEndDate,
31+
workflowIds
3132
);
3233

3334
return {

0 commit comments

Comments
 (0)