diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index 4b85896525ecf..303b7a0fcb632 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -31,6 +31,7 @@ import type { IHttpRequestHelper, INodeTypeData, INodeTypes, + ICredentialTestFunctions, } from 'n8n-workflow'; import { ICredentialsHelper, @@ -53,6 +54,9 @@ import { CredentialsOverwrites } from '@/CredentialsOverwrites'; import { whereClause } from './UserManagement/UserManagementHelper'; import { RESPONSE_ERROR_MESSAGES } from './constants'; import { Container } from 'typedi'; +import { isObjectLiteral } from './utils'; + +const { OAUTH2_CREDENTIAL_TEST_SUCCEEDED, OAUTH2_CREDENTIAL_TEST_FAILED } = RESPONSE_ERROR_MESSAGES; const mockNode = { name: '', @@ -466,6 +470,14 @@ export class CredentialsHelper extends ICredentialsHelper { await Db.collections.Credentials.update(findQuery, newCredentialsData); } + private static hasAccessToken(credentialsDecrypted: ICredentialsDecrypted) { + const oauthTokenData = credentialsDecrypted?.data?.oauthTokenData; + + if (!isObjectLiteral(oauthTokenData)) return false; + + return 'access_token' in oauthTokenData; + } + private getCredentialTestFunction( credentialType: string, ): ICredentialTestFunction | ICredentialTestRequestData | undefined { @@ -496,6 +508,26 @@ export class CredentialsHelper extends ICredentialsHelper { for (const nodeType of allNodeTypes) { // Check each of teh credentials for (const { name, testedBy } of nodeType.description.credentials ?? []) { + if ( + name === credentialType && + this.credentialTypes.getParentTypes(name).includes('oAuth2Api') + ) { + return async function oauth2CredTest( + this: ICredentialTestFunctions, + cred: ICredentialsDecrypted, + ): Promise { + return CredentialsHelper.hasAccessToken(cred) + ? { + status: 'OK', + message: OAUTH2_CREDENTIAL_TEST_SUCCEEDED, + } + : { + status: 'Error', + message: OAUTH2_CREDENTIAL_TEST_FAILED, + }; + }; + } + if (name === credentialType && !!testedBy) { if (typeof testedBy === 'string') { if (node instanceof VersionedNodeType) { diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts index 12f08845575dd..caef03bcb2324 100644 --- a/packages/cli/src/constants.ts +++ b/packages/cli/src/constants.ts @@ -45,6 +45,8 @@ export const RESPONSE_ERROR_MESSAGES = { PACKAGE_LOADING_FAILED: 'The specified package could not be loaded', DISK_IS_FULL: 'There appears to be insufficient disk space', USERS_QUOTA_REACHED: 'Maximum number of users reached', + OAUTH2_CREDENTIAL_TEST_SUCCEEDED: 'Connection Successful!', + OAUTH2_CREDENTIAL_TEST_FAILED: 'This OAuth2 credential was not connected to an account.', }; export const AUTH_COOKIE_NAME = 'n8n-auth'; diff --git a/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts b/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts index fe6696386878e..bb21e758fa938 100644 --- a/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts +++ b/packages/cli/src/databases/migrations/common/1659888469333-AddJsonKeyPinData.ts @@ -1,3 +1,4 @@ +import { isObjectLiteral } from '@/utils'; import type { IDataObject, INodeExecutionData } from 'n8n-workflow'; import type { MigrationContext, IrreversibleMigration } from '@db/types'; @@ -5,10 +6,6 @@ type OldPinnedData = { [nodeName: string]: IDataObject[] }; type NewPinnedData = { [nodeName: string]: INodeExecutionData[] }; type Workflow = { id: number; pinData: string | OldPinnedData }; -function isObjectLiteral(item: unknown): item is { [key: string]: string } { - return typeof item === 'object' && item !== null && !Array.isArray(item); -} - function isJsonKeyObject(item: unknown): item is { json: unknown; [keys: string]: unknown; diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 0912b02582a05..5e4f38d01158a 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -97,3 +97,7 @@ export function isStringArray(value: unknown): value is string[] { } export const isIntegerString = (value: string) => /^\d+$/.test(value); + +export function isObjectLiteral(item: unknown): item is { [key: string]: string } { + return typeof item === 'object' && item !== null && !Array.isArray(item); +}