Skip to content

Commit

Permalink
Revert "feat(core): Fix populating of node custom api call options (#…
Browse files Browse the repository at this point in the history
…5303)"

This reverts commit e58bc41.
  • Loading branch information
OlegIvaniv committed Feb 2, 2023
1 parent b5154d9 commit ac2c793
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 78 deletions.
12 changes: 0 additions & 12 deletions cypress/e2e/5-ndv.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,4 @@ describe('NDV', () => {
cy.get('[class*=hasIssues]').should('have.length', 1);
});
});

it('should show http node hint if node has custom api option', () => {
workflowPage.actions.addNodeToCanvas('Manual Trigger');
workflowPage.actions.addNodeToCanvas('Slack', true, true);
ndv.getters.httpRequestNotice().should('not.exist');
ndv.getters.parameterInput('operation').click();
ndv.getters.parameterInput('operation').contains('Custom API').click();
ndv.getters.httpRequestNotice().should('be.visible');
ndv.getters.parameterInput('operation').click();
ndv.getters.parameterInput('operation').contains('Post').click();
ndv.getters.httpRequestNotice().should('not.exist');
});
});
1 change: 0 additions & 1 deletion cypress/pages/ndv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export class NDV extends BasePage {
parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`),
nodeNameContainer: () => cy.getByTestId('node-title-container'),
nodeRenameInput: () => cy.getByTestId('node-rename-input'),
httpRequestNotice: () => cy.getByTestId('node-parameters-http-notice'),
};

actions = {
Expand Down
62 changes: 59 additions & 3 deletions packages/cli/src/api/nodeTypes.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,69 @@ import express from 'express';
import { readFile } from 'fs/promises';
import get from 'lodash.get';

import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
import type { ICredentialType, INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';

import { CredentialTypes } from '@/CredentialTypes';
import config from '@/config';
import { NodeTypes } from '@/NodeTypes';
import * as ResponseHelper from '@/ResponseHelper';
import { getNodeTranslationPath } from '@/TranslationHelpers';

function isOAuth(credType: ICredentialType) {
return (
Array.isArray(credType.extends) &&
credType.extends.some((parentType) =>
['oAuth2Api', 'googleOAuth2Api', 'oAuth1Api'].includes(parentType),
)
);
}

/**
* 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;

const credentialTypes = CredentialTypes();

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

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

return isOAuth(credType);
});
}

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;
}

export const nodeTypesController = express.Router();

// Returns node information based on node names and versions
Expand All @@ -22,7 +78,7 @@ nodeTypesController.post(
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 @@ -47,7 +103,7 @@ nodeTypesController.post(
// ignore - no translation exists at path
}

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

const nodeTypes: INodeTypeDescription[] = [];
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export const PLACEHOLDER_EMPTY_EXECUTION_ID = '__UNKNOWN__';
export const PLACEHOLDER_EMPTY_WORKFLOW_ID = '__EMPTY__';
export const TUNNEL_SUBDOMAIN_ENV = 'N8N_TUNNEL_SUBDOMAIN';
export const WAIT_TIME_UNLIMITED = '3000-01-01T00:00:00.000Z';
export const CUSTOM_API_CALL_NAME = 'Custom API Call';
export const CUSTOM_API_CALL_KEY = '__CUSTOM_API_CALL__';

export const RESPONSE_ERROR_MESSAGES = {
NO_ENCRYPTION_KEY: 'Encryption key is missing or was not set',
Expand Down
54 changes: 5 additions & 49 deletions packages/core/src/DirectoryLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import type {
IVersionedNodeType,
KnownNodesAndCredentials,
} from 'n8n-workflow';
import { CUSTOM_NODES_CATEGORY, CUSTOM_API_CALL_KEY, CUSTOM_API_CALL_NAME } from './Constants';
import { CUSTOM_NODES_CATEGORY } from './Constants';
import type { n8n } from './Interfaces';
import { loadClassInIsolation } from './ClassLoader';

Expand Down Expand Up @@ -54,48 +54,6 @@ export abstract class DirectoryLoader {
return path.resolve(this.directory, file);
}

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

return description.credentials.some(({ name }) => {
const credType = this.credentialTypes[name].type;

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

return (
Array.isArray(credType.extends) &&
credType.extends.some((parentType) =>
['oAuth2Api', 'googleOAuth2Api', 'oAuth1Api'].includes(parentType),
)
);
});
}

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

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,
});
}
});
}

protected loadNodeFromFile(packageName: string, nodeName: string, filePath: string) {
let tempNode: INodeType | IVersionedNodeType;
let nodeVersion = 1;
Expand Down Expand Up @@ -131,7 +89,6 @@ export abstract class DirectoryLoader {

const currentVersionNode = tempNode.nodeVersions[tempNode.currentVersion];
this.addCodex({ node: currentVersionNode, filePath, isCustom: packageName === 'CUSTOM' });
this.injectCustomApiCallOption(currentVersionNode.description);
nodeVersion = tempNode.currentVersion;

if (currentVersionNode.hasOwnProperty('executeSingle')) {
Expand All @@ -142,11 +99,10 @@ export abstract class DirectoryLoader {
}
} else {
// Short renaming to avoid type issues

this.injectCustomApiCallOption(tempNode.description);
nodeVersion = Array.isArray(tempNode.description.version)
? tempNode.description.version.slice(-1)[0]
: tempNode.description.version;
const tmpNode = tempNode;
nodeVersion = Array.isArray(tmpNode.description.version)
? tmpNode.description.version.slice(-1)[0]
: tmpNode.description.version;
}

this.known.nodes[fullNodeName] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ const telemetry = instance?.proxy.$telemetry;
const { categorizedItems: allNodes, isTriggerNode } = useNodeTypesStore();
const containsAPIAction = computed(
() =>
activeNodeActions.value?.properties.some((p) =>
state.latestNodeData?.properties.some((p) =>
p.options?.find((o) => o.name === CUSTOM_API_CALL_NAME),
) === true,
);
Expand Down Expand Up @@ -338,10 +338,27 @@ function getCustomAPICallHintLocale(key: string) {
interpolate: { nodeNameTitle },
});
}
// The nodes.json doesn't contain API CALL option so we need to fetch the node detail
// to determine if need to render the API CALL hint
async function fetchNodeDetails() {
if (!state.activeNodeActions) return;
const { getNodesInformation } = useNodeTypesStore();
const { version, name } = state.activeNodeActions;
const payload = {
name,
version: Array.isArray(version) ? version?.slice(-1)[0] : version,
} as INodeTypeNameVersion;
const nodesInfo = await getNodesInformation([payload], false);
state.latestNodeData = nodesInfo[0];
}
function setActiveActionsNodeType(nodeType: INodeTypeDescription | null) {
state.activeNodeActions = nodeType;
setShowTabs(false);
fetchNodeDetails();
if (nodeType) trackActionsView();
}
Expand Down
6 changes: 1 addition & 5 deletions packages/editor-ui/src/components/NodeSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,7 @@
</n8n-text>
</div>

<div
v-if="isCustomApiCallSelected(nodeValues)"
class="parameter-item parameter-notice"
data-test-id="node-parameters-http-notice"
>
<div v-if="isCustomApiCallSelected(nodeValues)" class="parameter-item parameter-notice">
<n8n-notice
:content="
$locale.baseText('nodeSettings.useTheHttpRequestNode', {
Expand Down
7 changes: 2 additions & 5 deletions packages/editor-ui/src/stores/nodeCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,9 @@ const customNodeActionsParsers: {
},
};

function filterActions(actions: INodeActionTypeDescription[]) {
function filterSinglePlaceholderAction(actions: INodeActionTypeDescription[]) {
return actions.filter(
(action: INodeActionTypeDescription, _: number, arr: INodeActionTypeDescription[]) => {
const isApiCall = action.actionKey === CUSTOM_API_CALL_KEY;
if (isApiCall) return false;

const isPlaceholderTriggerAction = action.actionKey === PLACEHOLDER_RECOMMENDED_ACTION_KEY;
return !isPlaceholderTriggerAction || (isPlaceholderTriggerAction && arr.length > 1);
},
Expand Down Expand Up @@ -342,7 +339,7 @@ export const useNodeCreatorStore = defineStore(STORES.NODE_CREATOR, {

const filteredNodes = Object.values(mergedNodes).map((node) => ({
...node,
actions: filterActions(node.actions || []),
actions: filterSinglePlaceholderAction(node.actions || []),
}));

return filteredNodes;
Expand Down

0 comments on commit ac2c793

Please sign in to comment.