Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding zod validations #3066

Merged
merged 5 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/services/destination/postTransformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ export class DestinationPostTransformationService {
): RouterTransformationResponse[] {
const resultantPayloads: RouterTransformationResponse[] = cloneDeep(transformedPayloads);
resultantPayloads.forEach((resultantPayload) => {
if (resultantPayload.batchedRequest && resultantPayload.batchedRequest.userId) {
if (Array.isArray(resultantPayload.batchedRequest)) {
resultantPayload.batchedRequest.forEach((batchedRequest) => {
if (batchedRequest.userId) {
batchedRequest.userId = `${batchedRequest.userId}`;
}
});
} else if (resultantPayload.batchedRequest && resultantPayload.batchedRequest.userId) {
resultantPayload.batchedRequest.userId = `${resultantPayload.batchedRequest.userId}`;
}
});
Expand Down
12 changes: 6 additions & 6 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type ProcessorTransformationOutput = {
type: string;
method: string;
endpoint: string;
userId: string;
userId?: string;
headers?: Record<string, unknown>;
params?: Record<string, unknown>;
body?: {
Expand Down Expand Up @@ -142,7 +142,7 @@ type ProcessorTransformationRequest = {
message: object;
metadata: Metadata;
destination: Destination;
libraries: UserTransformationLibrary[];
libraries?: UserTransformationLibrary[];
};

type RouterTransformationRequestData = {
Expand All @@ -162,17 +162,17 @@ type ProcessorTransformationResponse = {
metadata: Metadata;
statusCode: number;
error?: string;
statTags: object;
statTags?: object;
};

type RouterTransformationResponse = {
batchedRequest?: ProcessorTransformationOutput;
batchedRequest?: ProcessorTransformationOutput | ProcessorTransformationOutput[];
metadata: Metadata[];
destination: Destination;
batched: boolean;
statusCode: number;
error: string;
statTags: object;
error?: string;
statTags?: object;
};

type SourceTransformationOutput = {
Expand Down
105 changes: 104 additions & 1 deletion src/types/zodTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,109 @@
import { isDefinedAndNotNullAndNotEmpty } from '@rudderstack/integrations-lib';
import { isHttpStatusSuccess } from '../v0/util';

const ProcessorTransformationOutputSchema = z.object({
version: z.string(),
type: z.string(),
method: z.string(),
endpoint: z.string(),
userId: z.string().optional(),
headers: z.record(z.unknown()).optional(),
params: z.record(z.unknown()).optional(),
body: z
.object({
JSON: z.record(z.unknown()).optional(),
JSON_ARRAY: z.record(z.unknown()).optional(),
XML: z.record(z.unknown()).optional(),
FORM: z.record(z.unknown()).optional(),
})
.optional(),
files: z.record(z.unknown()).optional(),
});

export const ProcessorTransformationResponseSchema = z
.object({
output: ProcessorTransformationOutputSchema.optional(),
metadata: z.record(z.unknown()),
statusCode: z.number(),
error: z.string().optional(),
statTags: z.record(z.unknown()).optional(),
})
.refine(
(data) => {
if (!isHttpStatusSuccess(data.statusCode)) {
return (
isDefinedAndNotNullAndNotEmpty(data.statTags) ||
isDefinedAndNotNullAndNotEmpty(data.error)

Check warning on line 37 in src/types/zodTypes.ts

View check run for this annotation

Codecov / codecov/patch

src/types/zodTypes.ts#L37

Added line #L37 was not covered by tests
);
}
return true;
},
{
message: "statTags and error can't be empty when status is not a 2XX",
path: ['statTags', 'error'], // Pointing out which field is invalid
},
)
.refine(
(data) => {
if (isHttpStatusSuccess(data.statusCode)) {
return isDefinedAndNotNullAndNotEmpty(data.output);
}
return true;
},
{
message: "output can't be empty when status is 2XX",
path: ['output'], // Pointing out which field is invalid
},
);

export const ProcessorTransformationResponseListSchema = z.array(
ProcessorTransformationResponseSchema,
);

export const RouterTransformationResponseSchema = z
.object({
batchedRequest: z
.array(ProcessorTransformationOutputSchema)
.or(ProcessorTransformationOutputSchema)
.optional(),
metadata: z.array(z.record(z.unknown())), // array of metadata
destination: z.record(z.unknown()),
batched: z.boolean(),
statusCode: z.number(),
error: z.string().optional(),
statTags: z.record(z.unknown()).optional(),
})
.refine(
(data) => {
if (!isHttpStatusSuccess(data.statusCode)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeated

return (
isDefinedAndNotNullAndNotEmpty(data.statTags) ||
isDefinedAndNotNullAndNotEmpty(data.error)

Check warning on line 82 in src/types/zodTypes.ts

View check run for this annotation

Codecov / codecov/patch

src/types/zodTypes.ts#L82

Added line #L82 was not covered by tests
);
}
return true;
},
{
message: "statTags and error can't be empty when status is not a 2XX",
path: ['statTags', 'error'], // Pointing out which field is invalid
},
)
.refine(
(data) => {
if (isHttpStatusSuccess(data.statusCode)) {
return isDefinedAndNotNullAndNotEmpty(data.batchedRequest);
}
return true;
},
{
message: "batchedRequest can't be empty when status is 2XX",
path: ['batchedRequest'], // Pointing out which field is invalid
},
);

export const RouterTransformationResponseListSchema = z.array(RouterTransformationResponseSchema);

// Proxy related schemas
export const ProxyMetadataSchema = z.object({
jobId: z.number(),
attemptNum: z.number(),
Expand All @@ -10,7 +113,7 @@
destinationId: z.string(),
workspaceId: z.string(),
secret: z.record(z.unknown()),
destInfo: z.object({}).optional(),
destInfo: z.record(z.unknown()).optional(),
omitempty: z.record(z.unknown()).optional(),
dontBatch: z.boolean(),
});
Expand Down
5 changes: 3 additions & 2 deletions src/v0/destinations/rakuten/networkHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
return match ? match[1] : null;
};

const responseHandler = (destinationResponse) => {
const responseHandler = (responseParams) => {
const { destinationResponse } = responseParams;
const msg = `[${DESTINATION} Response Handler] - Request Processed Successfully`;
const { response, status } = destinationResponse;
if (status === 400) {
Expand Down Expand Up @@ -71,7 +72,7 @@
neither we have any sample response but just in case if we recoeve non 2xx status
*/
if (status !== 200) {
throw new NetworkError(

Check warning on line 75 in src/v0/destinations/rakuten/networkHandler.js

View check run for this annotation

Codecov / codecov/patch

src/v0/destinations/rakuten/networkHandler.js#L75

Added line #L75 was not covered by tests
`Request failed with status: ${status}`,
status,
{
Expand Down Expand Up @@ -99,5 +100,5 @@

module.exports = {
networkHandler,
responseHandler
responseHandler,
};
11 changes: 5 additions & 6 deletions src/v0/destinations/rakuten/networkHandler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('responseHandler', () => {
status: 200,
};

const result = responseHandler(destinationResponse);
const result = responseHandler({ destinationResponse });

expect(result.status).toBe(200);
expect(result.message).toBe('[RAKUTEN Response Handler] - Request Processed Successfully');
Expand All @@ -21,7 +21,7 @@ describe('responseHandler', () => {
status: 400,
};
expect(() => {
responseHandler(destinationResponse);
responseHandler({ destinationResponse });
}).toThrow('Request failed with status: 400 due to invalid Marketing Id');
});

Expand All @@ -31,7 +31,7 @@ describe('responseHandler', () => {
status: 200,
};
expect(() => {
responseHandler(destinationResponse);
responseHandler({ destinationResponse });
}).toThrow(
'Request failed with status: 200 due to Access denied. Can you try to enable pixel tracking for this mid.',
);
Expand All @@ -43,7 +43,7 @@ describe('responseHandler', () => {
status: 200,
};

const result = responseHandler(destinationResponse);
const result = responseHandler({ destinationResponse });

expect(result.status).toBe(200);
expect(result.message).toBe('[RAKUTEN Response Handler] - Request Processed Successfully');
Expand All @@ -57,8 +57,7 @@ describe('responseHandler', () => {
};

expect(() => {
responseHandler(destinationResponse);
responseHandler({ destinationResponse });
}).toThrow('Request failed with status: 200 with number of bad records 1');

});
});
25 changes: 22 additions & 3 deletions test/integrations/destinations/klaviyo/processor/ecomTestData.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { overrideDestination, transformResultBuilder } from '../../../testUtils';
import { overrideDestination, transformResultBuilder, generateMetadata } from '../../../testUtils';
import { ProcessorTestData } from '../../../testTypes';
import { Destination } from '../../../../../src/types';

const destination = {
const destination: Destination = {
ID: '123',
Name: 'klaviyo',
DestinationDefinition: {
ID: '123',
Name: 'klaviyo',
DisplayName: 'klaviyo',
Config: {},
},
Config: {
publicApiKey: 'dummyPublicApiKey',
privateApiKey: 'dummyPrivateApiKey',
},
Enabled: true,
WorkspaceID: '123',
Transformations: [],
};

const commonTraits = {
Expand All @@ -26,7 +39,7 @@ const commonOutputHeaders = {
revision: '2023-02-22',
};

export const ecomTestData = [
export const ecomTestData: ProcessorTestData[] = [
{
id: 'klaviyo-ecom-test-1',
name: 'klaviyo',
Expand Down Expand Up @@ -64,6 +77,7 @@ export const ecomTestData = [
anonymousId: '9c6bd77ea9da3e68',
originalTimestamp: '2021-01-25T15:32:56.409Z',
},
metadata: generateMetadata(1),
},
],
},
Expand Down Expand Up @@ -108,6 +122,7 @@ export const ecomTestData = [
userId: '',
}),
statusCode: 200,
metadata: generateMetadata(1),
},
],
},
Expand Down Expand Up @@ -170,6 +185,7 @@ export const ecomTestData = [
},
anonymousId: '9c6bd77ea9da3e68',
},
metadata: generateMetadata(2),
},
],
},
Expand Down Expand Up @@ -220,6 +236,7 @@ export const ecomTestData = [
userId: '',
}),
statusCode: 200,
metadata: generateMetadata(2),
},
],
},
Expand Down Expand Up @@ -280,6 +297,7 @@ export const ecomTestData = [
},
originalTimestamp: '2021-01-25T15:32:56.409Z',
},
metadata: generateMetadata(3),
},
],
},
Expand Down Expand Up @@ -336,6 +354,7 @@ export const ecomTestData = [
userId: '',
}),
statusCode: 200,
metadata: generateMetadata(3),
},
],
},
Expand Down
29 changes: 26 additions & 3 deletions test/integrations/destinations/klaviyo/processor/groupTestData.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import { generateSimplifiedGroupPayload, transformResultBuilder } from '../../../testUtils';
import { Destination } from '../../../../../src/types';
import { ProcessorTestData } from '../../../testTypes';
import {
generateMetadata,
generateSimplifiedGroupPayload,
transformResultBuilder,
} from '../../../testUtils';

const destination = {
const destination: Destination = {
ID: '123',
Name: 'klaviyo',
DestinationDefinition: {
ID: '123',
Name: 'klaviyo',
DisplayName: 'klaviyo',
Config: {},
},
Config: {
publicApiKey: 'dummyPublicApiKey',
privateApiKey: 'dummyPrivateApiKey',
},
Enabled: true,
WorkspaceID: '123',
Transformations: [],
};

const headers = {
Expand All @@ -16,7 +33,7 @@ const headers = {

const commonEndpoint = 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs';

export const groupTestData = [
export const groupTestData: ProcessorTestData[] = [
{
id: 'klaviyo-group-test-1',
name: 'klaviyo',
Expand Down Expand Up @@ -47,6 +64,7 @@ export const groupTestData = [
},
timestamp: '2020-01-21T00:21:34.208Z',
}),
metadata: generateMetadata(1),
},
],
},
Expand Down Expand Up @@ -74,6 +92,7 @@ export const groupTestData = [
userId: '',
}),
statusCode: 200,
metadata: generateMetadata(1),
},
],
},
Expand Down Expand Up @@ -109,6 +128,7 @@ export const groupTestData = [
},
timestamp: '2020-01-21T00:21:34.208Z',
}),
metadata: generateMetadata(2),
},
],
},
Expand All @@ -126,8 +146,11 @@ export const groupTestData = [
feature: 'processor',
implementation: 'native',
module: 'destination',
destinationId: 'default-destinationId',
workspaceId: 'default-workspaceId',
},
statusCode: 400,
metadata: generateMetadata(2),
},
],
},
Expand Down
Loading
Loading