mirror of
https://github.com/n8n-io/n8n
synced 2026-04-21 15:47:20 +00:00
fix(ClickUp Node): Unclear error message when using OAuth credentials (#28584)
Co-authored-by: Dawid Myslak <dawid.myslak@gmail.com>
This commit is contained in:
parent
7b3696f3f7
commit
19aadf19f7
4 changed files with 52 additions and 2 deletions
|
|
@ -1428,6 +1428,50 @@ describe('Request Helper Functions', () => {
|
|||
expect(result).toEqual({ success: true });
|
||||
expect(mockThis.helpers.httpRequest).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('should NOT retry on token-expired status when oAuth2Options.skipTokenRefresh is true (isN8nRequest path)', async () => {
|
||||
mockThis.getCredentials.mockResolvedValue(makeCredentialData());
|
||||
const error401 = Object.assign(new Error('401'), { response: { status: 401 } });
|
||||
mockThis.helpers.httpRequest.mockRejectedValueOnce(error401);
|
||||
|
||||
await expect(
|
||||
requestOAuth2.call(
|
||||
mockThis,
|
||||
'testOAuth2',
|
||||
{ method: 'GET', url: `${baseUrl}/data` },
|
||||
mockNode,
|
||||
mockAdditionalData,
|
||||
{ skipTokenRefresh: true },
|
||||
true,
|
||||
),
|
||||
).rejects.toThrow('401');
|
||||
expect(mockThis.helpers.httpRequest).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
mockAdditionalData.credentialsHelper.updateCredentialsOauthTokenData,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should NOT retry on token-expired status when oAuth2Options.skipTokenRefresh is true (legacy request path)', async () => {
|
||||
mockThis.getCredentials.mockResolvedValue(makeCredentialData());
|
||||
const error401 = Object.assign(new Error('401'), { statusCode: 401 });
|
||||
mockThis.helpers.request.mockRejectedValueOnce(error401);
|
||||
|
||||
await expect(
|
||||
requestOAuth2.call(
|
||||
mockThis,
|
||||
'testOAuth2',
|
||||
{ method: 'GET', url: `${baseUrl}/data` },
|
||||
mockNode,
|
||||
mockAdditionalData,
|
||||
{ skipTokenRefresh: true },
|
||||
false,
|
||||
),
|
||||
).rejects.toThrow('401');
|
||||
expect(mockThis.helpers.request).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
mockAdditionalData.credentialsHelper.updateCredentialsOauthTokenData,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestOAuth2 - client credentials initial token fetch', () => {
|
||||
|
|
|
|||
|
|
@ -889,6 +889,7 @@ export async function requestOAuth2(
|
|||
});
|
||||
}
|
||||
const tokenExpiredStatusCode = resolveTokenExpiredStatusCode(oAuth2Options, credentials);
|
||||
const shouldSkipTokenRefresh = oAuth2Options?.skipTokenRefresh === true;
|
||||
|
||||
const refreshCtx: RefreshOAuth2TokenContext = {
|
||||
credentials,
|
||||
|
|
@ -916,7 +917,7 @@ export async function requestOAuth2(
|
|||
|
||||
if (isN8nRequest) {
|
||||
return await this.helpers.httpRequest(newRequestOptions).catch(async (error: AxiosError) => {
|
||||
if (error.response?.status === tokenExpiredStatusCode) {
|
||||
if (!shouldSkipTokenRefresh && error.response?.status === tokenExpiredStatusCode) {
|
||||
return await retryWithNewToken(async (opts) => await this.helpers.httpRequest(opts));
|
||||
}
|
||||
throw error;
|
||||
|
|
@ -928,6 +929,7 @@ export async function requestOAuth2(
|
|||
.then((response) => {
|
||||
const requestOptions = newRequestOptions as any;
|
||||
if (
|
||||
!shouldSkipTokenRefresh &&
|
||||
requestOptions.resolveWithFullResponse === true &&
|
||||
requestOptions.simple === false &&
|
||||
response.statusCode === tokenExpiredStatusCode
|
||||
|
|
@ -937,7 +939,7 @@ export async function requestOAuth2(
|
|||
return response;
|
||||
})
|
||||
.catch(async (error: IResponseError) => {
|
||||
if (error.statusCode === tokenExpiredStatusCode) {
|
||||
if (!shouldSkipTokenRefresh && error.statusCode === tokenExpiredStatusCode) {
|
||||
return await retryWithNewToken(
|
||||
async (opts) => await this.helpers.request(opts as IRequestOptions),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ export async function clickupApiRequest(
|
|||
const oAuth2Options: IOAuth2Options = {
|
||||
keepBearer: false,
|
||||
tokenType: 'Bearer',
|
||||
// ClickUp's access token doesn't expire and
|
||||
// ClickUp does not return refresh tokens
|
||||
skipTokenRefresh: true,
|
||||
};
|
||||
return await this.helpers.requestOAuth2.call(
|
||||
this,
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ export interface IBinaryData {
|
|||
// credentials file.
|
||||
export interface IOAuth2Options {
|
||||
includeCredentialsOnRefreshOnBody?: boolean;
|
||||
skipTokenRefresh?: boolean;
|
||||
property?: string;
|
||||
tokenType?: string;
|
||||
keepBearer?: boolean;
|
||||
|
|
|
|||
Loading…
Reference in a new issue