Skip to content

Commit

Permalink
⚡ Discoverability flow (#3229)
Browse files Browse the repository at this point in the history
* ✨ Added node credentials type proxy. Changed node credentials input order.

* ⚡ Add computed property from versioning branch

* 🐛 Fix cred ref lost and unsaved

* ⚡ Make options consistent with cred type names

* ⚡ Use prop to set component order

* ⚡ Use constant and version

* ⚡ Fix rendering for generic auth creds

* ⚡ Mark as required on first selection

* ⚡ Implement discoverability flow

* ⚡ Mark as required on subsequent selections

* ⚡ Fix marking as required after cred deletion

* ⚡ Refactor to clean up

* ⚡ Detect position automatically

* ⚡ Add i18n to option label

* ⚡ Hide subtitle for custom action

* ⚡ Detect active credential type

* ⚡ Prop drilling to re-render select

* 🔥 Remove unneeded property

* ✏️ Rename arg

* 🔥 Remove unused import

* 🔥 Remove unneeded getters

* 🔥 Remove unused import

* ⚡ Generalize cred component positioning

* ⚡ Set up request

* 🐛 Fix edge case in endpoint

* ⚡ Display scopes alert box

* ⏪ Revert "Generalize cred comp positioning"

This reverts commit 75eea89.

* ⚡ Consolidate HTTPRN check

* ⚡ Fix hue percentage to degree

* 🔥 Remove unused import

* 🔥 Remove unused import

* 🔥 Remove unused class

* 🔥 Remove unused import

* 📘 Create type for HTTPRN v2 auth params

* ✏️ Rename check

* 🔥 Remove unused import

* ✏️ Add i18n to `reportUnsetCredential()`

* ⚡ Refactor Alex's spacing changes

* ⚡ Post-merge fixes

* ⚡ Add docs link

* 🔥 Exclude Notion OAuth cred

* ✏️ Update copy

* ✏️ Rename param

* 🎨 Reposition notice and simplify styling

* ✏️ Update copy

* ✏️ Update copy

* ⚡ Hide params during custom action

* ⚡ Show notice if any cred type supported

* 🐛 Prevent scopes text overflow

* 🔥 Remove superfluous check

* ✏️ Break up docstring

* 🎨 Tweak notice styling

* ⚡ Reorder cred param in Webhook node

* ✏️ Shorten cred name in scopes notice

* 🧪 Update Notice snapshots

* 🐛 Fix check when `globalRole` is `undefined`

* ⏪ Revert 3f2c4a6

* ⚡ Apply feedback from Product

* 🧪 Update snapshot

* ⚡ Adjust regex expansion pattern for singular

* 🔥 Remove unused import

* 🔥 Remove logging

* ⚡ Make `somethingElse` key more unique

* ⚡ Move something else to constants

* ⚡ Consolidate notice component

* ⚡ Apply latest feedback

* 🧪 Update tests

* 🧪 Update snapshot

* ✏️ Fix singular version

* 🧪 Finalize tests

* ✏️ Rename constant

* 🧪 Expand tests

* 🔥 Remove `truncate` prop

* 🚚 Move scopes fetching to store

* 🚚 Move method to component

* ⚡ Use constant

* ⚡ Refactor `Notice` component

* 🧪 Update tests

* 🔥 Remove unused keys

* ⚡ Inject custom API call option

* 🔥 Remove unused props

* 🎨 Use `compact` prop

* 🧪 Update snapshots

* 🚚 Move scopes to store

* 🚚 Move `nodeCredentialTypes` to parent

* ✏️ Rename cred types per branding

* 🐛 Clear scopes when none

* ⚡ Add default

* 🚚 Move `newHttpRequestNodeCredentialType` to parent

* 🔥 Remove test data

* ⚡ Separate lines for readability

* ⚡ Change reference from node to node name

* ✏️ Rename i18n keys

* ⚡ Refactor OAuth check

* 🔥 Remove unused key

* 🚚 Move `OAuth1/2 API` to i18n

* ⚡ Refactor `skipCheck`

* ⚡ Add `stopPropagation` and `preventDefault`

* 🚚 Move active credential scopes logic to store

* 🎨 Fix spacing for `NodeWebhooks` component

* ⚡ Implement feedback

* ⚡ Update HTTPRN default and issue copy

* Refactor to use `CredentialsSelect` param (#3304)

* ⚡ Refactor into cred type param

* ⚡ Componentize scopes notice

* 🔥 Remove unused data

* 🔥 Remove unused `loadOptions`

* ⚡ Componentize `NodeCredentialType`

* 🐛 Fix param validation

* 🔥 Remove dup methods

* ⚡ Refactor all references to `isHttpRequestNodeV2`

* 🎨 Fix styling

* 🔥 Remove unused import

* 🔥 Remove unused properties

* 🎨 Fix spacing for Pipedrive Trigger node

* 🎨 Undo Webhook node styling change

* 🔥 Remove unused style

* ⚡ Cover `httpHeaderAuth` edge case

* 🐛 Fix `this.node` reference

* 🚚 Rename to `credentialsSelect`

* 🐛 Fix mistaken renaming

* ⚡ Set one attribute per line

* ⚡ Move condition to instantiation site

* 🚚 Rename prop

* ⚡ Refactor away `prepareScopesNotice`

* ✏️ Rename i18n keys

* ✏️ Update i18n calls

* ✏️ Add more i18n keys

* 🔥 Remove unused props

* ✏️ Add explanatory comment

* ⚡ Adjust check in `hasProxyAuth`

* ⚡ Refactor `credentialSelected` from prop to event

* ⚡ Eventify `valueChanged`, `setFocus`, `onBlur`

* ⚡ Eventify `optionSelected`

* ⚡ Add `noDataExpression`

* 🔥 Remove logging

* 🔥 Remove URL from scopes

* ⚡ Disregard expressions for display

* 🎨 Use CSS modules

* 📘 Tigthen interface

* 🐛 Fix generic auth display

* 🐛 Fix generic auth validation

* 📘 Loosen type

* 🚚 Move event params to end

* ⚡ Generalize reference

* ⚡ Refactor generic auth as `credentialsSelect` param

* ⏪ Restore check for `httpHeaderAuth `

* 🚚 Rename `existing` to `predefined`

* Extend metrics for HTTP Request node (#3282)

* ⚡ Extend metrics

* 🧪 Add tests

* ⚡ Update param names

Co-authored-by: Alex Grozav <[email protected]>
  • Loading branch information
ivov and alexgrozav authored May 19, 2022
1 parent 11eedd8 commit d3c884d
Show file tree
Hide file tree
Showing 48 changed files with 1,031 additions and 566 deletions.
27 changes: 0 additions & 27 deletions packages/cli/src/CredentialsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,33 +286,6 @@ export class CredentialsHelper extends ICredentialsHelper {
return combineProperties;
}

/**
* Returns the scope of a credential type
*
* @param {string} type
* @returns {string[]}
* @memberof CredentialsHelper
*/
getScopes(type: string): string[] {
const scopeProperty = this.getCredentialsProperties(type).find(({ name }) => name === 'scope');

if (!scopeProperty?.default || typeof scopeProperty.default !== 'string') {
const errorMessage = `No \`scope\` property found for credential type: ${type}`;

Logger.error(errorMessage);

throw new Error(errorMessage);
}

const { default: scopeDefault } = scopeProperty;

if (/ /.test(scopeDefault)) return scopeDefault.split(' ');

if (/,/.test(scopeDefault)) return scopeDefault.split(',');

return [scopeDefault];
}

/**
* Returns the decrypted credential data with applied overwrites
*
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/LoadNodesAndCredentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class LoadNodesAndCredentialsClass {
// In case "n8n" package is the root and the packages are
// in the "node_modules" folder underneath it.
path.join(__dirname, '..', '..', 'node_modules', 'n8n-workflow'),
path.join(__dirname, '..', 'node_modules', 'n8n-workflow'), // for test run
];
for (const checkPath of checkPaths) {
try {
Expand Down
62 changes: 57 additions & 5 deletions packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ import { ExecutionEntity } from './databases/entities/ExecutionEntity';
import { SharedWorkflow } from './databases/entities/SharedWorkflow';
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from './constants';
import { credentialsController } from './api/credentials.api';
import { oauth2CredentialController } from './api/oauth2Credential.api';
import {
getInstanceBaseUrl,
isEmailSetUp,
Expand Down Expand Up @@ -1457,7 +1456,7 @@ class App {
if (defaultLocale === 'en') {
return nodeInfos.reduce<INodeTypeDescription[]>((acc, { name, version }) => {
const { description } = NodeTypes().getByNameAndVersion(name, version);
acc.push(description);
acc.push(injectCustomApiCallOption(description));
return acc;
}, []);
}
Expand All @@ -1481,7 +1480,7 @@ class App {
// ignore - no translation exists at path
}

nodeTypes.push(description);
nodeTypes.push(injectCustomApiCallOption(description));
}

const nodeTypes: INodeTypeDescription[] = [];
Expand Down Expand Up @@ -1929,8 +1928,6 @@ class App {
// OAuth2-Credential/Auth
// ----------------------------------------

this.app.use(`/${this.restEndpoint}/oauth2-credential`, oauth2CredentialController);

// Authorize OAuth Data
this.app.get(
`/${this.restEndpoint}/oauth2-credential/auth`,
Expand Down Expand Up @@ -3117,3 +3114,58 @@ async function getExecutionsCount(

return { count, estimated: false };
}

const CUSTOM_API_CALL_NAME = 'Custom API Call';
const CUSTOM_API_CALL_KEY = '__CUSTOM_API_CALL__';

/**
* Inject a `Custom API Call` option into `resource` and `operation`
* parameters in a node that supports proxy auth.
*/
function injectCustomApiCallOption(description: INodeTypeDescription) {
if (!supportsProxyAuth(description)) return description;

description.properties.forEach((p) => {
if (
['resource', 'operation'].includes(p.name) &&
Array.isArray(p.options) &&
p.options[p.options.length - 1].name !== CUSTOM_API_CALL_NAME
) {
p.options.push({
name: CUSTOM_API_CALL_NAME,
value: CUSTOM_API_CALL_KEY,
});
}

return p;
});

return description;
}

const credentialTypes = CredentialTypes();

/**
* Whether any of the node's credential types may be used to
* make a request from a node other than itself.
*/
function supportsProxyAuth(description: INodeTypeDescription) {
if (!description.credentials) return false;

return description.credentials.some(({ name }) => {
const credType = credentialTypes.getByName(name);

if (credType.authenticate !== undefined) return true;

return isOAuth(credType);
});
}

function isOAuth(credType: ICredentialType) {
return (
Array.isArray(credType.extends) &&
credType.extends.some((parentType) =>
['oAuth2Api', 'googleOAuth2Api', 'oAuth1Api'].includes(parentType),
)
);
}
61 changes: 0 additions & 61 deletions packages/cli/src/api/oauth2Credential.api.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import { RESPONSE_ERROR_MESSAGES as CORE_RESPONSE_ERROR_MESSAGES } from 'n8n-cor
export const RESPONSE_ERROR_MESSAGES = {
NO_CREDENTIAL: 'Credential not found',
NO_ENCRYPTION_KEY: CORE_RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY,
NO_CREDENTIAL_TYPE: 'Missing query string param: `credentialType`',
CREDENTIAL_TYPE_NOT_OAUTH2: 'Credential type is not OAuth2 - no scopes can be provided',
};

export const AUTH_COOKIE_NAME = 'n8n-auth';
1 change: 0 additions & 1 deletion packages/cli/src/requests.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ export declare namespace OAuthRequest {
namespace OAuth2Credential {
type Auth = OAuth1Credential.Auth;
type Callback = AuthenticatedRequest<{}, {}, {}, { code: string; state: string }>;
type Scopes = AuthenticatedRequest<{}, {}, {}, { credentialType: string }>;
}
}

Expand Down
129 changes: 0 additions & 129 deletions packages/cli/test/integration/oauth2.api.test.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/cli/test/integration/shared/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,3 @@ export const BOOTSTRAP_MYSQL_CONNECTION_NAME: Readonly<string> = 'n8n_bs_mysql';
* Timeout (in milliseconds) to account for fake SMTP service being slow to respond.
*/
export const SMTP_TEST_TIMEOUT = 30_000;

/**
* Timeout (in milliseconds) to account for `LoadNodesAndCredentials()` being slow to run on CI/CD server.
*/
export const LOAD_NODES_AND_CREDS_TEST_TIMEOUT = 30_000;
2 changes: 1 addition & 1 deletion packages/cli/test/integration/shared/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type SmtpTestAccount = {
};
};

type EndpointGroup = 'me' | 'users' | 'auth' | 'owner' | 'passwordReset' | 'credentials' | 'oauth2-credential';
type EndpointGroup = 'me' | 'users' | 'auth' | 'owner' | 'passwordReset' | 'credentials';

export type CredentialPayload = {
name: string;
Expand Down
7 changes: 1 addition & 6 deletions packages/cli/test/integration/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { credentialsController } from '../../../src/api/credentials.api';
import type { User } from '../../../src/databases/entities/User';
import type { EndpointGroup, SmtpTestAccount } from './types';
import type { N8nApp } from '../../../src/UserManagement/Interfaces';
import { oauth2CredentialController } from '../../../src/api/oauth2Credential.api';

/**
* Initialize a test server.
Expand Down Expand Up @@ -64,7 +63,6 @@ export function initTestServer({
if (routerEndpoints.length) {
const map: Record<string, express.Router> = {
credentials: credentialsController,
'oauth2-credential': oauth2CredentialController,
};

for (const group of routerEndpoints) {
Expand Down Expand Up @@ -107,10 +105,7 @@ const classifyEndpointGroups = (endpointGroups: string[]) => {
const functionEndpoints: string[] = [];

endpointGroups.forEach((group) =>
(['credentials', 'oauth2-credential'].includes(group)
? routerEndpoints
: functionEndpoints
).push(group),
(group === 'credentials' ? routerEndpoints : functionEndpoints).push(group),
);

return [routerEndpoints, functionEndpoints];
Expand Down
Loading

0 comments on commit d3c884d

Please sign in to comment.