Skip to content
Open
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
12 changes: 11 additions & 1 deletion __mocks__/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,17 @@ paths:
responses:
"200":
$ref: "#/responses/NotFound"

/test-redirect-response-header:
post:
operationId: "testRedirectResponseHeader"
responses:
"302":
description: "redirect to location"
headers:
Location:
type: string
"500":
description: "Fatal error"
definitions:
Person:
$ref: "definitions.yaml#/Person"
Expand Down
12 changes: 11 additions & 1 deletion __mocks__/openapi_v3/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,17 @@ paths:
responses:
"200":
$ref: "#/components/responses/NotFound"

/test-redirect-response-header:
post:
operationId: "testRedirectResponseHeader"
responses:
"302":
description: "redirect to location"
headers:
Location:
type: string
"500":
description: "Fatal error"
# -------------
# Components
# -------------
Expand Down
13 changes: 13 additions & 0 deletions e2e/src/__tests__/test-api/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,17 @@ describeSuite("Http client generated from Test API spec", () => {
// @ts-expect-error
client.putTestParameterWithBodyReference({ body: "" });
});

it("should make post with response 302 and location header", async () => {
const client = createClient({
baseUrl: `http://localhost:${mockPort}`,
fetchApi: (nodeFetch as any) as typeof fetch,
basePath: ""
});

expect(client.testRedirectResponseHeader).toEqual(expect.any(Function));

const result = await client.testRedirectResponseHeader({});
expect(isRight(result)).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,9 @@ import {
TestCustomTokenHeaderT,
testCustomTokenHeaderDefaultDecoder,
TestWithEmptyResponseT,
testWithEmptyResponseDefaultDecoder
testWithEmptyResponseDefaultDecoder,
TestRedirectResponseHeaderT,
testRedirectResponseHeaderDefaultDecoder
} from \\"./requestTypes\\";

// This is a placeholder for undefined when dealing with object keys
Expand All @@ -957,7 +959,8 @@ export type ApiOperation = TypeofApiCall<TestAuthBearerT> &
TypeofApiCall<TestParametersAtPathLevelT> &
TypeofApiCall<TestSimplePatchT> &
TypeofApiCall<TestCustomTokenHeaderT> &
TypeofApiCall<TestWithEmptyResponseT>;
TypeofApiCall<TestWithEmptyResponseT> &
TypeofApiCall<TestRedirectResponseHeaderT>;

export type ParamKeys = keyof (TypeofApiParams<TestAuthBearerT> &
TypeofApiParams<TestSimpleTokenT> &
Expand All @@ -975,7 +978,8 @@ export type ParamKeys = keyof (TypeofApiParams<TestAuthBearerT> &
TypeofApiParams<TestParametersAtPathLevelT> &
TypeofApiParams<TestSimplePatchT> &
TypeofApiParams<TestCustomTokenHeaderT> &
TypeofApiParams<TestWithEmptyResponseT>);
TypeofApiParams<TestWithEmptyResponseT> &
TypeofApiParams<TestRedirectResponseHeaderT>);

/**
* Defines an adapter for TypeofApiCall which omit one or more parameters in the signature
Expand Down Expand Up @@ -1015,7 +1019,8 @@ export type WithDefaultsT<
| TestParametersAtPathLevelT
| TestSimplePatchT
| TestCustomTokenHeaderT
| TestWithEmptyResponseT,
| TestWithEmptyResponseT
| TestRedirectResponseHeaderT,
K
>;

Expand Down Expand Up @@ -1070,6 +1075,10 @@ export type Client<
readonly testCustomTokenHeader: TypeofApiCall<TestCustomTokenHeaderT>;

readonly testWithEmptyResponse: TypeofApiCall<TestWithEmptyResponseT>;

readonly testRedirectResponseHeader: TypeofApiCall<
TestRedirectResponseHeaderT
>;
}
: {
readonly testAuthBearer: TypeofApiCall<
Expand Down Expand Up @@ -1190,6 +1199,13 @@ export type Client<
Omit<RequestParams<TestWithEmptyResponseT>, K>
>
>;

readonly testRedirectResponseHeader: TypeofApiCall<
ReplaceRequestParams<
TestRedirectResponseHeaderT,
Omit<RequestParams<TestRedirectResponseHeaderT>, K>
>
>;
};

/**
Expand Down Expand Up @@ -1597,6 +1613,27 @@ export function createClient<K extends ParamKeys>({
options
);

const testRedirectResponseHeaderT: ReplaceRequestParams<
TestRedirectResponseHeaderT,
RequestParams<TestRedirectResponseHeaderT>
> = {
method: \\"post\\",

headers: () => ({
\\"Content-Type\\": \\"application/json\\"
}),
response_decoder: testRedirectResponseHeaderDefaultDecoder(),
url: ({}) => \`\${basePath}/test-redirect-response-header\`,

body: () => \\"{}\\",

query: () => withoutUndefinedValues({})
};
const testRedirectResponseHeader: TypeofApiCall<TestRedirectResponseHeaderT> = createFetchRequestForApi(
testRedirectResponseHeaderT,
options
);

return {
testAuthBearer: (withDefaults || identity)(testAuthBearer),
testSimpleToken: (withDefaults || identity)(testSimpleToken),
Expand Down Expand Up @@ -1624,7 +1661,10 @@ export function createClient<K extends ParamKeys>({
),
testSimplePatch: (withDefaults || identity)(testSimplePatch),
testCustomTokenHeader: (withDefaults || identity)(testCustomTokenHeader),
testWithEmptyResponse: (withDefaults || identity)(testWithEmptyResponse)
testWithEmptyResponse: (withDefaults || identity)(testWithEmptyResponse),
testRedirectResponseHeader: (withDefaults || identity)(
testRedirectResponseHeader
)
};
}
"
Expand Down Expand Up @@ -2696,7 +2736,9 @@ import {
TestCustomTokenHeaderT,
testCustomTokenHeaderDefaultDecoder,
TestWithEmptyResponseT,
testWithEmptyResponseDefaultDecoder
testWithEmptyResponseDefaultDecoder,
TestRedirectResponseHeaderT,
testRedirectResponseHeaderDefaultDecoder
} from \\"./requestTypes\\";

// This is a placeholder for undefined when dealing with object keys
Expand All @@ -2722,7 +2764,8 @@ export type ApiOperation = TypeofApiCall<TestAuthBearerT> &
TypeofApiCall<TestParametersAtPathLevelT> &
TypeofApiCall<TestSimplePatchT> &
TypeofApiCall<TestCustomTokenHeaderT> &
TypeofApiCall<TestWithEmptyResponseT>;
TypeofApiCall<TestWithEmptyResponseT> &
TypeofApiCall<TestRedirectResponseHeaderT>;

export type ParamKeys = keyof (TypeofApiParams<TestAuthBearerT> &
TypeofApiParams<TestAuthBearerHttpT> &
Expand All @@ -2741,7 +2784,8 @@ export type ParamKeys = keyof (TypeofApiParams<TestAuthBearerT> &
TypeofApiParams<TestParametersAtPathLevelT> &
TypeofApiParams<TestSimplePatchT> &
TypeofApiParams<TestCustomTokenHeaderT> &
TypeofApiParams<TestWithEmptyResponseT>);
TypeofApiParams<TestWithEmptyResponseT> &
TypeofApiParams<TestRedirectResponseHeaderT>);

/**
* Defines an adapter for TypeofApiCall which omit one or more parameters in the signature
Expand Down Expand Up @@ -2782,7 +2826,8 @@ export type WithDefaultsT<
| TestParametersAtPathLevelT
| TestSimplePatchT
| TestCustomTokenHeaderT
| TestWithEmptyResponseT,
| TestWithEmptyResponseT
| TestRedirectResponseHeaderT,
K
>;

Expand Down Expand Up @@ -2839,6 +2884,10 @@ export type Client<
readonly testCustomTokenHeader: TypeofApiCall<TestCustomTokenHeaderT>;

readonly testWithEmptyResponse: TypeofApiCall<TestWithEmptyResponseT>;

readonly testRedirectResponseHeader: TypeofApiCall<
TestRedirectResponseHeaderT
>;
}
: {
readonly testAuthBearer: TypeofApiCall<
Expand Down Expand Up @@ -2966,6 +3015,13 @@ export type Client<
Omit<RequestParams<TestWithEmptyResponseT>, K>
>
>;

readonly testRedirectResponseHeader: TypeofApiCall<
ReplaceRequestParams<
TestRedirectResponseHeaderT,
Omit<RequestParams<TestRedirectResponseHeaderT>, K>
>
>;
};

/**
Expand Down Expand Up @@ -3407,6 +3463,27 @@ export function createClient<K extends ParamKeys>({
options
);

const testRedirectResponseHeaderT: ReplaceRequestParams<
TestRedirectResponseHeaderT,
RequestParams<TestRedirectResponseHeaderT>
> = {
method: \\"post\\",

headers: () => ({
\\"Content-Type\\": \\"application/json\\"
}),
response_decoder: testRedirectResponseHeaderDefaultDecoder(),
url: ({}) => \`\${basePath}/test-redirect-response-header\`,

body: () => \\"{}\\",

query: () => withoutUndefinedValues({})
};
const testRedirectResponseHeader: TypeofApiCall<TestRedirectResponseHeaderT> = createFetchRequestForApi(
testRedirectResponseHeaderT,
options
);

return {
testAuthBearer: (withDefaults || identity)(testAuthBearer),
testAuthBearerHttp: (withDefaults || identity)(testAuthBearerHttp),
Expand Down Expand Up @@ -3435,7 +3512,10 @@ export function createClient<K extends ParamKeys>({
),
testSimplePatch: (withDefaults || identity)(testSimplePatch),
testCustomTokenHeader: (withDefaults || identity)(testCustomTokenHeader),
testWithEmptyResponse: (withDefaults || identity)(testWithEmptyResponse)
testWithEmptyResponse: (withDefaults || identity)(testWithEmptyResponse),
testRedirectResponseHeader: (withDefaults || identity)(
testRedirectResponseHeader
)
};
}
"
Expand Down
22 changes: 22 additions & 0 deletions src/commands/gen-api-models/__tests__/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,28 @@ describe.each`
})
);
});

it("should parse an operation with response with 302 and header location", () => {
const parsed = getParser(spec).parseOperation(
//@ts-ignore
spec,
"/test-redirect-response-header",
[],
"undefined",
"undefined"
)("post");

expect(parsed).toEqual(
expect.objectContaining({
method: "post",
path: "/test-redirect-response-header",
parameters: [],
responses: expect.arrayContaining([
{ e1: "302", e2: "undefined", e3: ["Location"] }
])
})
);
});
});

describe.each`
Expand Down
4 changes: 2 additions & 2 deletions src/commands/gen-api-models/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ export const renderOperation = (
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, prefer-arrow/prefer-arrow-functions
export function renderDecoderCode({ responses, operationId }: IOperationInfo) {
// use the first 2xx type as "success type" that we allow to be overridden
// use the first 2xx type and 3xx as "success type" that we allow to be overridden
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it ok to handle 3xx code as "succesful"?
We'll have a right path without a proper decoded object inside the response.

const firstSuccessType = responses.find(
({ e1 }) => e1.length === 3 && e1[0] === "2"
({ e1 }) => e1.length === 3 && (e1[0] === "2" || e1[0] === "3")
);
if (!firstSuccessType) {
return "";
Expand Down