From a7c2360590af65aab5a0e4167870dbd3bc35fa5c Mon Sep 17 00:00:00 2001 From: GalT <39020298+tatarco@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:45:20 +0100 Subject: [PATCH] feat(api): all passing but 1 --- .../hydrate-email-schema.usecase.ts | 18 ++- .../app/workflows-v2/generate-preview.e2e.ts | 25 ++-- .../src/app/workflows-v2/maily-test-data.ts | 138 +++++++++++------- .../generate-preview.usecase.ts | 29 ++-- 4 files changed, 134 insertions(+), 76 deletions(-) diff --git a/apps/api/src/app/environments-v1/usecases/output-renderers/hydrate-email-schema.usecase.ts b/apps/api/src/app/environments-v1/usecases/output-renderers/hydrate-email-schema.usecase.ts index c768bf0d4c9..493e0509f67 100644 --- a/apps/api/src/app/environments-v1/usecases/output-renderers/hydrate-email-schema.usecase.ts +++ b/apps/api/src/app/environments-v1/usecases/output-renderers/hydrate-email-schema.usecase.ts @@ -137,7 +137,11 @@ export class HydrateEmailSchemaUseCase { node: TipTapNode & { attrs: { each: string } }, itemPointerToDefaultRecord: Record ) { + console.log('masterPayload', JSON.stringify(masterPayload)); + console.log('node.attrs.each:', node.attrs.each); const resolvedValue = this.getValueByPath(masterPayload, node.attrs.each); + console.log('resolvedValue', resolvedValue); + if (!resolvedValue) { return [this.buildElement(itemPointerToDefaultRecord, '1'), this.buildElement(itemPointerToDefaultRecord, '2')]; } @@ -165,13 +169,25 @@ export class HydrateEmailSchemaUseCase { } private getValueByPath(obj: Record, path: string): any { + console.log('Initial Object:', obj); + console.log('Path to Access:', path); + const keys = path.split('.'); + console.log('Keys to Traverse:', keys); return keys.reduce((currentObj, key) => { + console.log('Current Object:', currentObj); + console.log('Current Key:', key); + if (currentObj && typeof currentObj === 'object' && key in currentObj) { - return currentObj[key]; + const nextObj = currentObj[key]; + console.log(`Found key '${key}':`, nextObj); + + return nextObj; } + console.warn(`Key '${key}' not found in`, currentObj); + return undefined; }, obj); } diff --git a/apps/api/src/app/workflows-v2/generate-preview.e2e.ts b/apps/api/src/app/workflows-v2/generate-preview.e2e.ts index 8b18832b864..d17a10803fb 100644 --- a/apps/api/src/app/workflows-v2/generate-preview.e2e.ts +++ b/apps/api/src/app/workflows-v2/generate-preview.e2e.ts @@ -15,7 +15,7 @@ import { import { InAppOutput } from '@novu/framework/internal'; import { createWorkflowClient, HttpError, NovuRestResult } from './clients'; import { buildCreateWorkflowDto } from './workflow.controller.e2e'; -import { fullCodeSnippet } from './maily-test-data'; +import { forSnippet, fullCodeSnippet } from './maily-test-data'; const SUBJECT_TEST_PAYLOAD = '{{payload.subject.test.payload}}'; const PLACEHOLDER_SUBJECT_INAPP = '{{payload.subject}}'; @@ -152,24 +152,24 @@ describe('Generate Preview', () => { describe('for', () => { it('should populate for if payload exist with actual values', async () => { const { stepUuid, workflowId } = await createWorkflowAndReturnId(StepTypeEnum.EMAIL); - const body1 = 'ball is round'; - const body2 = 'square is square'; + const name1 = 'ball is round'; + const name2 = 'square is square'; const previewResponseDto = await generatePreview( workflowId, stepUuid, { validationStrategies: [], - controlValues: stepTypeTo[StepTypeEnum.EMAIL], - payloadValues: { items: [{ body: body1 }, { body: body2 }] }, + controlValues: buildSimpleForEmail() as unknown as Record, + payloadValues: { food: { items: [{ name: name1 }, { name: name2 }] } }, }, 'email' ); expect(previewResponseDto.result!.preview).to.exist; const preview = previewResponseDto.result!.preview.body; - expect(preview).not.to.contain('{{item.body}}1'); - expect(preview).not.to.contain('{{item.body}}1'); - expect(preview).to.contain(body1); - expect(preview).to.contain(body2); + expect(preview).not.to.contain('{{item.name}}1'); + expect(preview).not.to.contain('{{item.name}}2'); + expect(preview).to.contain(name1); + expect(preview).to.contain(name2); }); }); }); @@ -262,7 +262,12 @@ function buildEmailControlValuesPayload(): EmailStepControlSchemaDto { emailEditor: JSON.stringify(fullCodeSnippet), }; } - +function buildSimpleForEmail(): EmailStepControlSchemaDto { + return { + subject: `Hello, World! ${SUBJECT_TEST_PAYLOAD}`, + emailEditor: JSON.stringify(forSnippet), + }; +} function buildInAppControlValues(): InAppOutput { return { subject: `Hello, World! ${PLACEHOLDER_SUBJECT_INAPP}`, diff --git a/apps/api/src/app/workflows-v2/maily-test-data.ts b/apps/api/src/app/workflows-v2/maily-test-data.ts index ab4fd391234..6c337d3cf40 100644 --- a/apps/api/src/app/workflows-v2/maily-test-data.ts +++ b/apps/api/src/app/workflows-v2/maily-test-data.ts @@ -1,3 +1,82 @@ +export const forSnippet = { + type: 'doc', + content: [ + { + type: 'for', + attrs: { + each: 'payload.food.items', + isUpdatingKey: false, + }, + content: [ + { + type: 'paragraph', + attrs: { + textAlign: 'left', + }, + content: [ + { + type: 'text', + text: 'this is a food item with name ', + }, + { + type: 'payloadValue', + attrs: { + id: 'name', + label: null, + }, + }, + { + type: 'text', + text: ' ', + }, + ], + }, + { + type: 'for', + attrs: { + each: 'payload.food.warnings', + isUpdatingKey: false, + }, + content: [ + { + type: 'bulletList', + content: [ + { + type: 'listItem', + attrs: { + color: null, + }, + content: [ + { + type: 'paragraph', + attrs: { + textAlign: 'left', + }, + content: [ + { + type: 'payloadValue', + attrs: { + id: 'header', + label: null, + }, + }, + { + type: 'text', + text: ' ', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], +}; + export const fullCodeSnippet = { type: 'doc', content: [ @@ -44,7 +123,7 @@ export const fullCodeSnippet = { content: [ { type: 'text', - text: 'Are you ready to transform your email communication? Introducing Maily, the powerful email editor that enables you to craft captivating emails effortlessly.', + text: 'Are you ready to transform your email communication? Introducing Maily, the powerful emaly.', }, ], }, @@ -183,55 +262,6 @@ export const fullCodeSnippet = { }, ], }, - { - type: 'for', - attrs: { - each: 'comments', - isUpdatingKey: false, - }, - content: [ - { - type: 'paragraph', - attrs: { - textAlign: 'left', - }, - content: [ - { - type: 'text', - text: 'Comment subject: ', - }, - { - type: 'payloadValue', - attrs: { - id: 'subject', - label: null, - }, - }, - { - type: 'text', - text: ' and the body is: ', - }, - { - type: 'payloadValue', - attrs: { - id: 'body', - label: null, - }, - }, - { - type: 'text', - text: ' ', - }, - ], - }, - ], - }, - { - type: 'paragraph', - attrs: { - textAlign: 'left', - }, - }, { type: 'paragraph', attrs: { @@ -269,7 +299,7 @@ export const fullCodeSnippet = { { type: 'for', attrs: { - each: 'origins', + each: 'payload.origins', isUpdatingKey: false, }, content: [ @@ -335,7 +365,7 @@ export const fullCodeSnippet = { { type: 'for', attrs: { - each: 'students', + each: 'payload.students', isUpdatingKey: false, }, content: [ @@ -433,7 +463,7 @@ export const fullCodeSnippet = { { type: 'for', attrs: { - each: 'food.items', + each: 'payload.food.items', isUpdatingKey: false, }, content: [ @@ -463,7 +493,7 @@ export const fullCodeSnippet = { { type: 'for', attrs: { - each: 'food.warnings', + each: 'payload.food.warnings', isUpdatingKey: false, }, content: [ diff --git a/apps/api/src/app/workflows-v2/usecases/generate-preview/generate-preview.usecase.ts b/apps/api/src/app/workflows-v2/usecases/generate-preview/generate-preview.usecase.ts index cd4b6ea1eb2..0e0edffc222 100644 --- a/apps/api/src/app/workflows-v2/usecases/generate-preview/generate-preview.usecase.ts +++ b/apps/api/src/app/workflows-v2/usecases/generate-preview/generate-preview.usecase.ts @@ -168,21 +168,28 @@ function buildResponse( }; } -function findMissingKeys(requiredRecord: Record, actualRecord: Record) { +function findMissingKeys(requiredRecord: Record, actualRecord: Record): string[] { const requiredKeys = collectKeys(requiredRecord); const actualKeys = collectKeys(actualRecord); return _.difference(requiredKeys, actualKeys); } -function collectKeys(obj, prefix = '') { - return _.reduce(obj, (result, value, key) => { - const newKey = prefix ? `${prefix}.${key}` : key; - if (_.isObject(value) && !_.isArray(value)) { - result.push(...this.collectKeys(value, newKey)); - } else { - result.push(newKey); - } - return result; - }); +function collectKeys(obj: Record, prefix = ''): string[] { + // Initialize result as an empty array of strings + return _.reduce( + obj, + (result: string[], value, key) => { + const newKey = prefix ? `${prefix}.${key}` : key; + if (_.isObject(value) && !_.isArray(value)) { + // Call collectKeys recursively and concatenate the results + result.push(...collectKeys(value, newKey)); + } else { + result.push(newKey); + } + + return result; + }, + [] // Pass an empty array as the initial value + ); }