Skip to content

Commit fc2e5c2

Browse files
chore(root): Release 2025-02-06 11:01 (#7673)
2 parents b3d78d5 + 79739cb commit fc2e5c2

File tree

75 files changed

+1380
-421
lines changed

Some content is hidden

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

75 files changed

+1380
-421
lines changed

apps/api/migrations/expire-at/expire-at-delay.migration.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ describe('Create expireAt - TTL support - with pending jobs', function () {
7474
});
7575

7676
it('should add expireAt to pending events that were digested', async function () {
77-
await session.awaitRunningJobs(digestTemplate?._id, false, 5);
77+
await session.waitForJobCompletion(digestTemplate?._id, false, 5);
7878

7979
await notificationExpireAt(query);
8080

apps/api/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
"pretest": "pnpm build:metadata",
2626
"generate:swagger": "cross-env NODE_ENV=test PORT=1336 ts-node exportOpenAPIJSON.ts",
2727
"generate:sdk": " (cd ../../libs/internal-sdk && speakeasy run --skip-compile --minimal --skip-versioning) && (cd ../../libs/internal-sdk && pnpm build) ",
28-
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --require ts-node/register --exit 'src/**/*.spec.ts'",
29-
"test:e2e:novu-v1": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --grep '#novu-v1' --require ts-node/register --exit --file e2e/setup.ts src/**/*.e2e{,-ee}.ts",
30-
"test:e2e:novu-v2": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test CI_EE_TEST=true CLERK_ENABLED=true NODE_OPTIONS=--max_old_space_size=8192 mocha --grep '#novu-v2' --require ts-node/register --exit --file e2e/setup.ts 'src/**/*.e2e{,-ee}.ts'",
28+
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --timeout 5000 --require ts-node/register --exit 'src/**/*.spec.ts'",
29+
"test:e2e:novu-v1": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test mocha --timeout 5000 --grep '#novu-v1' --require ts-node/register --exit --file e2e/setup.ts src/**/*.e2e{,-ee}.ts ",
30+
"test:e2e:novu-v2": "cross-env TS_NODE_COMPILER_OPTIONS='{\"strictNullChecks\": false}' NODE_ENV=test CI_EE_TEST=true CLERK_ENABLED=true NODE_OPTIONS=--max_old_space_size=8192 mocha --timeout 5000 --grep '#novu-v2' --require ts-node/register --exit --file e2e/setup.ts 'src/**/*.e2e{,-ee}.ts'",
3131
"migration": "cross-env NODE_ENV=local MIGRATION=true ts-node --transpileOnly",
3232
"link:submodules": "pnpm link ../../enterprise/packages/auth && pnpm link ../../enterprise/packages/translation && pnpm link ../../enterprise/packages/billing",
3333
"admin:remove-user-account": "cross-env NODE_ENV=local MIGRATION=true ts-node --transpileOnly ./admin/remove-user-account.ts",
@@ -37,7 +37,7 @@
3737
"@aws-sdk/client-secrets-manager": "^3.716.0",
3838
"@godaddy/terminus": "^4.12.1",
3939
"@google-cloud/storage": "^6.2.3",
40-
"@maily-to/render": "^0.0.17",
40+
"@maily-to/render": "^0.0.19",
4141
"@nestjs/axios": "3.0.3",
4242
"@nestjs/common": "10.4.1",
4343
"@nestjs/core": "10.4.1",

apps/api/src/app/environments-v1/usecases/output-renderers/email-output-renderer.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -559,13 +559,13 @@ describe('EmailOutputRendererUsecase', () => {
559559
});
560560
});
561561

562-
describe('for block transformation and expansion', () => {
563-
it('should handle for loop block transformation with array of objects', async () => {
562+
describe('repeat block transformation and expansion', () => {
563+
it('should handle repeat loop block transformation with array of objects', async () => {
564564
const mockTipTapNode: MailyJSONContent = {
565565
type: 'doc',
566566
content: [
567567
{
568-
type: 'for',
568+
type: 'repeat',
569569
attrs: {
570570
each: 'payload.comments',
571571
isUpdatingKey: false,
@@ -610,7 +610,7 @@ describe('EmailOutputRendererUsecase', () => {
610610

611611
const renderCommand = {
612612
controlValues: {
613-
subject: 'For Loop Test',
613+
subject: 'Repeat Loop Test',
614614
body: JSON.stringify(mockTipTapNode),
615615
},
616616
fullPayloadForRender: {
@@ -626,12 +626,12 @@ describe('EmailOutputRendererUsecase', () => {
626626
expect(result.body).to.include('This is an author: <!-- -->Jane<!-- -->Post Title');
627627
});
628628

629-
it('should handle for loop block transformation with array of primitives', async () => {
629+
it('should handle repeat loop block transformation with array of primitives', async () => {
630630
const mockTipTapNode: MailyJSONContent = {
631631
type: 'doc',
632632
content: [
633633
{
634-
type: 'for',
634+
type: 'repeat',
635635
attrs: {
636636
each: 'payload.names',
637637
isUpdatingKey: false,
@@ -662,7 +662,7 @@ describe('EmailOutputRendererUsecase', () => {
662662

663663
const renderCommand = {
664664
controlValues: {
665-
subject: 'For Loop Test',
665+
subject: 'Repeat Loop Test',
666666
body: JSON.stringify(mockTipTapNode),
667667
},
668668
fullPayloadForRender: {

apps/api/src/app/environments-v1/usecases/output-renderers/email-output-renderer.usecase.ts

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
/* eslint-disable no-param-reassign */
22
import { render as mailyRender, JSONContent as MailyJSONContent } from '@maily-to/render';
33
import { Injectable } from '@nestjs/common';
4-
import { Liquid } from 'liquidjs';
54
import { EmailRenderOutput } from '@novu/shared';
65
import { InstrumentUsecase } from '@novu/application-generic';
6+
77
import { FullPayloadForRender, RenderCommand } from './render-command';
88
import { WrapMailyInLiquidUseCase } from './maily-to-liquid/wrap-maily-in-liquid.usecase';
9-
import { MAILY_ITERABLE_MARK, MailyAttrsEnum, MailyContentTypeEnum } from './maily-to-liquid/maily.types';
9+
import { MAILY_ITERABLE_MARK, MailyAttrsEnum } from './maily-to-liquid/maily.types';
1010
import { parseLiquid } from '../../../shared/helpers/liquid';
11+
import { hasShow, isRepeatNode, isVariableNode } from './maily-to-liquid/maily-utils';
1112

1213
export class EmailOutputRendererCommand extends RenderCommand {}
1314

@@ -88,15 +89,15 @@ export class EmailOutputRendererUsecase {
8889
while (queue.length > 0) {
8990
const current = queue.shift()!;
9091

91-
if (this.hasShow(current.node)) {
92+
if (hasShow(current.node)) {
9293
await this.handleShowNode(current.node, variables, current.parent);
9394
}
9495

95-
if (this.isForNode(current.node)) {
96+
if (isRepeatNode(current.node)) {
9697
await this.handleEachNode(current.node, variables, current.parent);
9798
}
9899

99-
if (this.isVariableNode(current.node)) {
100+
if (isVariableNode(current.node)) {
100101
this.processVariableNodeTypes(current.node);
101102
}
102103

@@ -156,23 +157,6 @@ export class EmailOutputRendererUsecase {
156157
node.text = node.attrs?.id || '';
157158
}
158159

159-
private isForNode(
160-
node: MailyJSONContent
161-
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.EACH_KEY]: string } } {
162-
return !!(
163-
node.type === MailyContentTypeEnum.FOR &&
164-
node.attrs &&
165-
node.attrs[MailyAttrsEnum.EACH_KEY] !== undefined &&
166-
typeof node.attrs[MailyAttrsEnum.EACH_KEY] === 'string'
167-
);
168-
}
169-
170-
private hasShow(
171-
node: MailyJSONContent
172-
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.SHOW_IF_KEY]: string } } {
173-
return node.attrs?.[MailyAttrsEnum.SHOW_IF_KEY] !== undefined && node.attrs?.[MailyAttrsEnum.SHOW_IF_KEY] !== null;
174-
}
175-
176160
/**
177161
* For 'each' node, multiply the content by the number of items in the iterable array
178162
* and add indexes to the placeholders.
@@ -229,7 +213,7 @@ export class EmailOutputRendererUsecase {
229213
return nodes.map((node) => {
230214
const processedNode = { ...node };
231215

232-
if (this.isVariableNode(processedNode)) {
216+
if (isVariableNode(processedNode)) {
233217
this.processVariableNodeTypes(processedNode);
234218

235219
if (processedNode.text) {
@@ -257,15 +241,4 @@ export class EmailOutputRendererUsecase {
257241
return Boolean(normalized);
258242
}
259243
}
260-
261-
private isVariableNode(
262-
node: MailyJSONContent
263-
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.ID]: string } } {
264-
return !!(
265-
node.type === MailyContentTypeEnum.VARIABLE &&
266-
node.attrs &&
267-
node.attrs[MailyAttrsEnum.ID] !== undefined &&
268-
typeof node.attrs[MailyAttrsEnum.ID] === 'string'
269-
);
270-
}
271244
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { JSONContent as MailyJSONContent } from '@maily-to/render';
2+
3+
import { MailyAttrsEnum, MailyContentTypeEnum } from './maily.types';
4+
5+
export const isRepeatNode = (
6+
node: MailyJSONContent
7+
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.EACH_KEY]: string } } => {
8+
return !!(
9+
(node.type === MailyContentTypeEnum.REPEAT || node.type === MailyContentTypeEnum.FOR) &&
10+
node.attrs &&
11+
node.attrs[MailyAttrsEnum.EACH_KEY] !== undefined &&
12+
typeof node.attrs[MailyAttrsEnum.EACH_KEY] === 'string'
13+
);
14+
};
15+
16+
export const isVariableNode = (
17+
node: MailyJSONContent
18+
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.ID]: string } } => {
19+
return !!(
20+
node.type === MailyContentTypeEnum.VARIABLE &&
21+
node.attrs &&
22+
node.attrs[MailyAttrsEnum.ID] !== undefined &&
23+
typeof node.attrs[MailyAttrsEnum.ID] === 'string'
24+
);
25+
};
26+
27+
export const hasShow = (
28+
node: MailyJSONContent
29+
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.SHOW_IF_KEY]: string } } => {
30+
return node.attrs?.[MailyAttrsEnum.SHOW_IF_KEY] !== undefined && node.attrs?.[MailyAttrsEnum.SHOW_IF_KEY] !== null;
31+
};
32+
33+
export const hasAttrs = (node: MailyJSONContent): node is MailyJSONContent & { attrs: Record<string, any> } => {
34+
return !!node.attrs;
35+
};
36+
37+
export const hasMarks = (node: MailyJSONContent): node is MailyJSONContent & { marks: Record<string, any>[] } => {
38+
return !!node.marks;
39+
};

apps/api/src/app/environments-v1/usecases/output-renderers/maily-to-liquid/maily.types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
export enum MailyContentTypeEnum {
22
VARIABLE = 'variable',
3+
REPEAT = 'repeat',
4+
/**
5+
* Legacy enum value maintained for backwards compatibility
6+
* @deprecated
7+
*/
38
FOR = 'for',
49
BUTTON = 'button',
510
IMAGE = 'image',

apps/api/src/app/environments-v1/usecases/output-renderers/maily-to-liquid/wrap-maily-in-liquid.usecase.ts

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
/* eslint-disable no-param-reassign */
22
import { Injectable } from '@nestjs/common';
33
import { JSONContent as MailyJSONContent } from '@maily-to/render';
4+
45
import { WrapMailyInLiquidCommand } from './wrap-maily-in-liquid.command';
56
import {
67
MailyContentTypeEnum,
78
MailyAttrsEnum,
89
MAILY_ITERABLE_MARK,
910
MAILY_FIRST_CITIZEN_VARIABLE_KEY,
1011
} from './maily.types';
12+
import { hasAttrs, hasMarks, isRepeatNode } from './maily-utils';
1113

1214
/**
13-
* Enriches Maily JSON content with Liquid syntax for variables.
15+
* Enriches Maily JSON content with Liquid syntax repeat variables.
1416
*
1517
* @example
1618
* Input:
1719
* {
18-
* type: "for",
20+
* type: "repeat",
1921
* attrs: { each: "payload.comments" },
2022
* content: [{
2123
* type: "variable",
@@ -53,19 +55,19 @@ export class WrapMailyInLiquidUseCase {
5355
const newNode = { ...node } as MailyJSONContent & { attrs: Record<string, any> };
5456

5557
// if this is a for loop node, track its variable
56-
if (this.isForNode(node)) {
58+
if (isRepeatNode(node)) {
5759
parentForLoopKey = node.attrs[MailyAttrsEnum.EACH_KEY];
5860
}
5961

6062
if (node.content) {
6163
newNode.content = node.content.map((child) => this.wrapVariablesInLiquid(child, parentForLoopKey));
6264
}
6365

64-
if (this.hasAttrs(node)) {
66+
if (hasAttrs(node)) {
6567
newNode.attrs = this.processVariableNodeAttributes(node, parentForLoopKey);
6668
}
6769

68-
if (this.hasMarks(node)) {
70+
if (hasMarks(node)) {
6971
newNode.marks = this.processNodeMarks(node);
7072
}
7173

@@ -144,25 +146,6 @@ export class WrapMailyInLiquidUseCase {
144146

145147
return `{{ ${variableName}${fallbackSuffix} }}`;
146148
}
147-
148-
private hasAttrs(node: MailyJSONContent): node is MailyJSONContent & { attrs: Record<string, any> } {
149-
return !!node.attrs;
150-
}
151-
152-
private hasMarks(node: MailyJSONContent): node is MailyJSONContent & { marks: Record<string, any>[] } {
153-
return !!node.marks;
154-
}
155-
156-
private isForNode(
157-
node: MailyJSONContent
158-
): node is MailyJSONContent & { attrs: { [MailyAttrsEnum.EACH_KEY]: string } } {
159-
return !!(
160-
node.type === MailyContentTypeEnum.FOR &&
161-
node.attrs &&
162-
node.attrs[MailyAttrsEnum.EACH_KEY] !== undefined &&
163-
typeof node.attrs[MailyAttrsEnum.EACH_KEY] === 'string'
164-
);
165-
}
166149
}
167150

168151
const variableAttributeConfig = (type: MailyContentTypeEnum) => {

0 commit comments

Comments
 (0)