Skip to content

Commit

Permalink
chore: stricter types, extra test cases for v2
Browse files Browse the repository at this point in the history
  • Loading branch information
vinayteki95 committed Nov 6, 2024
1 parent 3f8e75d commit 5705f2e
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 129 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,4 @@ dist

# component test report
test_reports/
temp/
temp/
23 changes: 14 additions & 9 deletions src/controllers/util/conversionStrategies/strategyV1ToV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ import { VersionConversionStrategy } from './abstractions';

export class StrategyV1ToV2 extends VersionConversionStrategy<SourceInput, SourceInputV2> {
convert(sourceEvents: SourceInput[]): SourceInputConversionResult<SourceInputV2>[] {
// Currently this is not being used
// Hold off on testing this until atleast one v2 source has been implemented
return sourceEvents.map((sourceEvent) => {
try {
const sourceEventParam = { ...sourceEvent };

let queryParameters: Record<string, unknown> | undefined;
if (sourceEventParam.event && sourceEventParam.event.query_parameters) {
queryParameters = sourceEventParam.event.query_parameters;
delete sourceEventParam.event.query_parameters;
}

const sourceRequest: SourceRequestV2 = {
method: '',
url: '',
proto: '',
headers: {},
query_parameters: {},
body: JSON.stringify(sourceEvent.event),
body: JSON.stringify(sourceEventParam.event),
};
if (queryParameters) {
sourceRequest.query_parameters = queryParameters;
}

const sourceInputV2: SourceInputV2 = {
request: sourceRequest,
source: sourceEvent.source,
source: sourceEventParam.source,
};
return {
output: sourceInputV2,
Expand Down
4 changes: 3 additions & 1 deletion src/controllers/util/conversionStrategies/strategyV2ToV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export class StrategyV2ToV1 extends VersionConversionStrategy<SourceInputV2, Sou
return sourceEvents.map((sourceEvent) => {
try {
const v1Event = { event: JSON.parse(sourceEvent.request.body), source: sourceEvent.source };
v1Event.event.query_parameters = sourceEvent.request.query_parameters;
if (sourceEvent.request) {
v1Event.event.query_parameters = sourceEvent.request.query_parameters;
}
return { output: v1Event };
} catch (err) {
const conversionError =
Expand Down
124 changes: 124 additions & 0 deletions src/controllers/util/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,38 @@ describe('adaptInputToVersion', () => {
expect(result).toEqual(expected);
});

it('should fail trying to convert input from v2 to v0 format when the request version is v2 and the implementation version is v0', () => {
const sourceType = 'pipedream';
const requestVersion = 'v2';

const input = [
{
request: {
method: 'POST',
url: 'http://example.com',
proto: 'HTTP/2',
headers: { headerkey: ['headervalue'] },
body: '{"key": "value',
query_parameters: { paramkey: ['paramvalue'] },
},
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
];
const expected = {
implementationVersion: 'v0',
input: [
{
output: {},
conversionError: new SyntaxError('Unexpected end of JSON input'),
},
],
};

const result = ControllerUtility.adaptInputToVersion(sourceType, requestVersion, input);

expect(result).toEqual(expected);
});

it('should convert input from v2 to v1 format when the request version is v2 and the implementation version is v1', () => {
const sourceType = 'webhook';
const requestVersion = 'v2';
Expand Down Expand Up @@ -269,6 +301,38 @@ describe('adaptInputToVersion', () => {
expect(result).toEqual(expected);
});

it('should fail trying to convert input from v2 to v1 format when the request version is v2 and the implementation version is v1', () => {
const sourceType = 'webhook';
const requestVersion = 'v2';

const input = [
{
request: {
method: 'POST',
url: 'http://example.com',
proto: 'HTTP/2',
headers: { headerkey: ['headervalue'] },
body: '{"key": "value"',
query_parameters: { paramkey: ['paramvalue'] },
},
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
];
const expected = {
implementationVersion: 'v1',
input: [
{
output: {},
conversionError: new SyntaxError('Unexpected end of JSON input'),
},
],
};

const result = ControllerUtility.adaptInputToVersion(sourceType, requestVersion, input);

expect(result).toEqual(expected);
});

// Should return an empty array when the input is an empty array
it('should return an empty array when the input is an empty array', () => {
const sourceType = 'pipedream';
Expand All @@ -280,6 +344,66 @@ describe('adaptInputToVersion', () => {

expect(result).toEqual(expected);
});

it('should convert input from v1 to v2 format when the request version is v1 and the implementation version is v2', () => {
const sourceType = 'someSourceType';
const requestVersion = 'v1';

// Mock return value for getSourceVersionsMap
jest
.spyOn(ControllerUtility as any, 'getSourceVersionsMap')
.mockReturnValue(new Map([['someSourceType', 'v2']]));

const input = [
{
event: { key: 'value', query_parameters: { paramkey: ['paramvalue'] } },
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
{
event: { key: 'value' },
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
{
event: {},
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
];

const expected = {
implementationVersion: 'v2',
input: [
{
output: {
request: {
body: '{"key":"value"}',
query_parameters: { paramkey: ['paramvalue'] },
},
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
},
{
output: {
request: {
body: '{"key":"value"}',
},
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
},
{
output: {
request: {
body: '{}',
},
source: { id: 'source_id', config: { configField1: 'configVal1' } },
},
},
],
};

const result = ControllerUtility.adaptInputToVersion(sourceType, requestVersion, input);

expect(result).toEqual(expected);
});
});

type timestampTestCases = {
Expand Down
97 changes: 0 additions & 97 deletions src/controllers/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import {
ProcessorTransformationRequest,
RouterTransformationRequestData,
RudderMessage,
SourceInput,
SourceInputConversionResult,
SourceInputV2,
SourceRequestV2,
} from '../../types';
import { getValueFromMessage } from '../../v0/util';
import genericFieldMap from '../../v0/util/data/GenericFieldMapping.json';
Expand Down Expand Up @@ -49,84 +46,6 @@ export class ControllerUtility {
return this.sourceVersionMap;
}

private static convertSourceInputv1Tov0(
sourceEvents: SourceInput[],
): SourceInputConversionResult<NonNullable<unknown>>[] {
return sourceEvents.map((sourceEvent) => ({
output: sourceEvent.event as NonNullable<unknown>,
}));
}

private static convertSourceInputv1Tov2(
sourceEvents: SourceInput[],
): SourceInputConversionResult<SourceInputV2>[] {
// Currently this is not being used
// Hold off on testing this until atleast one v2 source has been implemented
return sourceEvents.map((sourceEvent) => {
try {
const sourceRequest: SourceRequestV2 = {
method: '',
url: '',
proto: '',
headers: {},
query_parameters: {},
body: JSON.stringify(sourceEvent.event),
};
const sourceInputV2: SourceInputV2 = {
request: sourceRequest,
source: sourceEvent.source,
};
return {
output: sourceInputV2,
};
} catch (err) {
const conversionError =
err instanceof Error ? err : new Error('error converting v1 to v2 spec');
return { output: {} as SourceInputV2, conversionError };
}
});
}

private static convertSourceInputv0Tov1(
sourceEvents: unknown[],
): SourceInputConversionResult<SourceInput>[] {
return sourceEvents.map((sourceEvent) => ({
output: { event: sourceEvent, source: undefined } as SourceInput,
}));
}

private static convertSourceInputv2Tov0(
sourceEvents: SourceInputV2[],
): SourceInputConversionResult<NonNullable<unknown>>[] {
return sourceEvents.map((sourceEvent) => {
try {
const v0Event = JSON.parse(sourceEvent.request.body);
v0Event.query_parameters = sourceEvent.request.query_parameters;
return { output: v0Event };
} catch (err) {
const conversionError =
err instanceof Error ? err : new Error('error converting v2 to v0 spec');
return { output: {} as NonNullable<unknown>, conversionError };
}
});
}

private static convertSourceInputv2Tov1(
sourceEvents: SourceInputV2[],
): SourceInputConversionResult<SourceInput>[] {
return sourceEvents.map((sourceEvent) => {
try {
const v1Event = { event: JSON.parse(sourceEvent.request.body), source: sourceEvent.source };
v1Event.event.query_parameters = sourceEvent.request.query_parameters;
return { output: v1Event };
} catch (err) {
const conversionError =
err instanceof Error ? err : new Error('error converting v2 to v1 spec');
return { output: {} as SourceInput, conversionError };
}
});
}

public static adaptInputToVersion(
sourceType: string,
requestVersion: string,
Expand All @@ -140,22 +59,6 @@ export class ControllerUtility {
implementationVersion,
);
return { implementationVersion, input: conversionStrategy.convert(input) };

// let updatedInput: SourceInputConversionResult<NonNullable<unknown>>[] = input.map((event) => ({
// output: event,
// }));
// if (requestVersion === 'v0' && implementationVersion === 'v1') {
// updatedInput = this.convertSourceInputv0Tov1(input);
// } else if (requestVersion === 'v1' && implementationVersion === 'v0') {
// updatedInput = this.convertSourceInputv1Tov0(input as SourceInput[]);
// } else if (requestVersion === 'v1' && implementationVersion === 'v2') {
// updatedInput = this.convertSourceInputv1Tov2(input as SourceInput[]);
// } else if (requestVersion === 'v2' && implementationVersion === 'v0') {
// updatedInput = this.convertSourceInputv2Tov0(input as SourceInputV2[]);
// } else if (requestVersion === 'v2' && implementationVersion === 'v1') {
// updatedInput = this.convertSourceInputv2Tov1(input as SourceInputV2[]);
// }
// return { implementationVersion, input: updatedInput };
}

private static getCompatibleStatusCode(status: number): number {
Expand Down
15 changes: 0 additions & 15 deletions src/middlewares/routeActivation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,4 @@ export class RouteActivationMiddleware {
RouteActivationMiddleware.shouldActivateRoute(destination, deliveryFilterList),
);
}

// This middleware will be used by source endpoint when we completely deprecate v0, v1 versions.
public static isVersionAllowed(ctx: Context, next: Next) {
const { version } = ctx.params;
if (version === 'v0' || version === 'v1') {
ctx.status = 500;
ctx.body =
'/v0, /v1 versioned endpoints are deprecated. Use /v2 version endpoint. This is probably caused because of source transformation call from an outdated rudder-server version. Please upgrade rudder-server to a minimum of 1.xx.xx version.';
} else if (version === 'v2') {
next();
} else {
ctx.status = 404;
ctx.body = 'Path not found. Verify the version of your api call.';
}
}
}
15 changes: 9 additions & 6 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,17 +352,20 @@ type Source = {
};

type SourceInput = {
event: NonNullable<unknown>[];
event: {
query_parameters?: any;
[key: string]: any;
};
source?: Source;
};

type SourceRequestV2 = {
method: string;
url: string;
proto: string;
method?: string;
url?: string;
proto?: string;
body: string;
headers: NonNullable<unknown>;
query_parameters: NonNullable<unknown>;
headers?: Record<string, unknown>;
query_parameters?: Record<string, unknown>;
};

type SourceInputV2 = {
Expand Down

0 comments on commit 5705f2e

Please sign in to comment.