Change getCredentials type inference

This commit is contained in:
Elias Meire 2024-09-25 12:01:38 +02:00
parent 2d8b62f316
commit 14752bbf1c
No known key found for this signature in database
14 changed files with 263 additions and 319 deletions

View file

@ -0,0 +1,54 @@
// vite.config.mts
import { defineConfig } from "file:///Users/elias/projects/github/n8n-io/n8n/node_modules/.pnpm/vite@5.4.6_@types+node@18.16.16_sass@1.64.1_terser@5.16.1/node_modules/vite/dist/node/index.js";
import { resolve } from "path";
import vue from "file:///Users/elias/projects/github/n8n-io/n8n/node_modules/.pnpm/@vitejs+plugin-vue@5.1.4_vite@5.4.6_@types+node@18.16.16_sass@1.64.1_terser@5.16.1__vue@3.4.21_typescript@5.6.2_/node_modules/@vitejs/plugin-vue/dist/index.mjs";
import icons from "file:///Users/elias/projects/github/n8n-io/n8n/node_modules/.pnpm/unplugin-icons@0.19.0_@vue+compiler-sfc@3.4.21_vue-template-compiler@2.7.14/node_modules/unplugin-icons/dist/vite.js";
import dts from "file:///Users/elias/projects/github/n8n-io/n8n/node_modules/.pnpm/vite-plugin-dts@3.9.1_@types+node@18.16.16_rollup@4.22.2_typescript@5.6.2_vite@5.4.6_@types+n_rcaikbew5ptk64olpe4bf2iruu/node_modules/vite-plugin-dts/dist/index.mjs";
var __vite_injected_original_dirname = "/Users/elias/projects/github/n8n-io/n8n/packages/@n8n/chat";
var includeVue = process.env.INCLUDE_VUE === "true";
var srcPath = resolve(__vite_injected_original_dirname, "src");
var vite_config_default = defineConfig({
plugins: [
vue(),
icons({
compiler: "vue3",
autoInstall: true
}),
dts()
],
resolve: {
alias: {
"@": srcPath,
"@n8n/chat": srcPath,
lodash: "lodash-es"
}
},
define: {
"process.env.NODE_ENV": process.env.NODE_ENV ? `"${process.env.NODE_ENV}"` : '"development"'
},
build: {
emptyOutDir: !includeVue,
lib: {
entry: resolve(__vite_injected_original_dirname, "src", "index.ts"),
name: "N8nChat",
fileName: (format) => includeVue ? `chat.bundle.${format}.js` : `chat.${format}.js`
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: includeVue ? [] : ["vue"],
output: {
exports: "named",
// Provide global variables to use in the UMD build
// for externalized deps
globals: includeVue ? {} : {
vue: "Vue"
}
}
}
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcubXRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiL1VzZXJzL2VsaWFzL3Byb2plY3RzL2dpdGh1Yi9uOG4taW8vbjhuL3BhY2thZ2VzL0BuOG4vY2hhdFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL1VzZXJzL2VsaWFzL3Byb2plY3RzL2dpdGh1Yi9uOG4taW8vbjhuL3BhY2thZ2VzL0BuOG4vY2hhdC92aXRlLmNvbmZpZy5tdHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL1VzZXJzL2VsaWFzL3Byb2plY3RzL2dpdGh1Yi9uOG4taW8vbjhuL3BhY2thZ2VzL0BuOG4vY2hhdC92aXRlLmNvbmZpZy5tdHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tICdwYXRoJztcbmltcG9ydCB2dWUgZnJvbSAnQHZpdGVqcy9wbHVnaW4tdnVlJztcbmltcG9ydCBpY29ucyBmcm9tICd1bnBsdWdpbi1pY29ucy92aXRlJztcbmltcG9ydCBkdHMgZnJvbSAndml0ZS1wbHVnaW4tZHRzJztcblxuY29uc3QgaW5jbHVkZVZ1ZSA9IHByb2Nlc3MuZW52LklOQ0xVREVfVlVFID09PSAndHJ1ZSc7XG5jb25zdCBzcmNQYXRoID0gcmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMnKTtcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG5cdHBsdWdpbnM6IFtcblx0XHR2dWUoKSxcblx0XHRpY29ucyh7XG5cdFx0XHRjb21waWxlcjogJ3Z1ZTMnLFxuXHRcdFx0YXV0b0luc3RhbGw6IHRydWUsXG5cdFx0fSksXG5cdFx0ZHRzKCksXG5cdF0sXG5cdHJlc29sdmU6IHtcblx0XHRhbGlhczoge1xuXHRcdFx0J0AnOiBzcmNQYXRoLFxuXHRcdFx0J0BuOG4vY2hhdCc6IHNyY1BhdGgsXG5cdFx0XHRsb2Rhc2g6ICdsb2Rhc2gtZXMnLFxuXHRcdH0sXG5cdH0sXG5cdGRlZmluZToge1xuXHRcdCdwcm9jZXNzLmVudi5OT0RFX0VOVic6IHByb2Nlc3MuZW52Lk5PREVfRU5WID8gYFwiJHtwcm9jZXNzLmVudi5OT0RFX0VOVn1cImAgOiAnXCJkZXZlbG9wbWVudFwiJyxcblx0fSxcblx0YnVpbGQ6IHtcblx0XHRlbXB0eU91dERpcjogIWluY2x1ZGVWdWUsXG5cdFx0bGliOiB7XG5cdFx0XHRlbnRyeTogcmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMnLCAnaW5kZXgudHMnKSxcblx0XHRcdG5hbWU6ICdOOG5DaGF0Jyxcblx0XHRcdGZpbGVOYW1lOiAoZm9ybWF0KSA9PiAoaW5jbHVkZVZ1ZSA/IGBjaGF0LmJ1bmRsZS4ke2Zvcm1hdH0uanNgIDogYGNoYXQuJHtmb3JtYXR9LmpzYCksXG5cdFx0fSxcblx0XHRyb2xsdXBPcHRpb25zOiB7XG5cdFx0XHQvLyBtYWtlIHN1cmUgdG8gZXh0ZXJuYWxpemUgZGVwcyB0aGF0IHNob3VsZG4ndCBiZSBidW5kbGVkXG5cdFx0XHQvLyBpbnRvIHlvdXIgbGlicmFyeVxuXHRcdFx0ZXh0ZXJuYWw6IGluY2x1ZGVWdWUgPyBbXSA6IFsndnVlJ10sXG5cdFx0XHRvdXRwdXQ6IHtcblx0XHRcdFx0ZXhwb3J0czogJ25hbWVkJyxcblx0XHRcdFx0Ly8gUHJvdmlkZSBnbG9iYWwgdmFyaWFibGVzIHRvIHVzZSBpbiB0aGUgVU1EIGJ1aWxkXG5cdFx0XHRcdC8vIGZvciBleHRlcm5hbGl6ZWQgZGVwc1xuXHRcdFx0XHRnbG9iYWxzOiBpbmNsdWRlVnVlXG5cdFx0XHRcdFx0PyB7fVxuXHRcdFx0XHRcdDoge1xuXHRcdFx0XHRcdFx0XHR2dWU6ICdWdWUnLFxuXHRcdFx0XHRcdFx0fSxcblx0XHRcdH0sXG5cdFx0fSxcblx0fSxcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUFrVyxTQUFTLG9CQUFvQjtBQUMvWCxTQUFTLGVBQWU7QUFDeEIsT0FBTyxTQUFTO0FBQ2hCLE9BQU8sV0FBVztBQUNsQixPQUFPLFNBQVM7QUFKaEIsSUFBTSxtQ0FBbUM7QUFNekMsSUFBTSxhQUFhLFFBQVEsSUFBSSxnQkFBZ0I7QUFDL0MsSUFBTSxVQUFVLFFBQVEsa0NBQVcsS0FBSztBQUd4QyxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMzQixTQUFTO0FBQUEsSUFDUixJQUFJO0FBQUEsSUFDSixNQUFNO0FBQUEsTUFDTCxVQUFVO0FBQUEsTUFDVixhQUFhO0FBQUEsSUFDZCxDQUFDO0FBQUEsSUFDRCxJQUFJO0FBQUEsRUFDTDtBQUFBLEVBQ0EsU0FBUztBQUFBLElBQ1IsT0FBTztBQUFBLE1BQ04sS0FBSztBQUFBLE1BQ0wsYUFBYTtBQUFBLE1BQ2IsUUFBUTtBQUFBLElBQ1Q7QUFBQSxFQUNEO0FBQUEsRUFDQSxRQUFRO0FBQUEsSUFDUCx3QkFBd0IsUUFBUSxJQUFJLFdBQVcsSUFBSSxRQUFRLElBQUksUUFBUSxNQUFNO0FBQUEsRUFDOUU7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNOLGFBQWEsQ0FBQztBQUFBLElBQ2QsS0FBSztBQUFBLE1BQ0osT0FBTyxRQUFRLGtDQUFXLE9BQU8sVUFBVTtBQUFBLE1BQzNDLE1BQU07QUFBQSxNQUNOLFVBQVUsQ0FBQyxXQUFZLGFBQWEsZUFBZSxNQUFNLFFBQVEsUUFBUSxNQUFNO0FBQUEsSUFDaEY7QUFBQSxJQUNBLGVBQWU7QUFBQTtBQUFBO0FBQUEsTUFHZCxVQUFVLGFBQWEsQ0FBQyxJQUFJLENBQUMsS0FBSztBQUFBLE1BQ2xDLFFBQVE7QUFBQSxRQUNQLFNBQVM7QUFBQTtBQUFBO0FBQUEsUUFHVCxTQUFTLGFBQ04sQ0FBQyxJQUNEO0FBQUEsVUFDQSxLQUFLO0FBQUEsUUFDTjtBQUFBLE1BQ0g7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUNELENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==

View file

@ -102,6 +102,7 @@ import type {
SSHTunnelFunctions,
SchedulingFunctions,
AiEvent,
ICredentialType,
} from 'n8n-workflow';
import {
NodeConnectionType,
@ -1995,7 +1996,7 @@ export function getAdditionalKeys(
export async function getCredentials<T extends object = ICredentialDataDecryptedObject>(
workflow: Workflow,
node: INode,
type: string,
credType: string | (new () => ICredentialType),
additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode,
executeData?: IExecuteData,
@ -2004,6 +2005,7 @@ export async function getCredentials<T extends object = ICredentialDataDecrypted
connectionInputData?: INodeExecutionData[],
itemIndex?: number,
): Promise<T> {
const type = typeof credType === 'string' ? credType : credType.name;
// Get the NodeType as it has the information if the credentials are required
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion);
if (nodeType === undefined) {
@ -3496,8 +3498,10 @@ export function getExecutePollFunctions(
},
getMode: () => mode,
getActivationMode: () => activation,
getCredentials: async (type) =>
await getCredentials(workflow, node, type, additionalData, mode),
getCredentials: async (type: string | (new () => ICredentialType)) => {
const typeString = typeof type === 'string' ? type : type.name;
return await getCredentials(workflow, node, typeString, additionalData, mode);
},
getNodeParameter: (
parameterName: string,
fallbackValue?: any,
@ -3560,7 +3564,7 @@ export function getExecuteTriggerFunctions(
},
getMode: () => mode,
getActivationMode: () => activation,
getCredentials: async (type) =>
getCredentials: async (type: string | (new () => ICredentialType)) =>
await getCredentials(workflow, node, type, additionalData, mode),
getNodeParameter: (
parameterName: string,
@ -3620,7 +3624,7 @@ export function getExecuteFunctions(
...getCommonWorkflowFunctions(workflow, node, additionalData),
...executionCancellationFunctions(abortSignal),
getMode: () => mode,
getCredentials: async (type, itemIndex) =>
getCredentials: async (type: string | (new () => ICredentialType), itemIndex: number) =>
await getCredentials(
workflow,
node,
@ -3948,7 +3952,7 @@ export function getExecuteSingleFunctions(
getContext(type: ContextType): IContextObject {
return NodeHelpers.getContext(runExecutionData, type, node);
},
getCredentials: async (type) =>
getCredentials: async (type: string | (new () => ICredentialType)) =>
await getCredentials(
workflow,
node,
@ -4088,7 +4092,7 @@ export function getLoadOptionsFunctions(
return ((workflow: Workflow, node: INode, path: string) => {
return {
...getCommonWorkflowFunctions(workflow, node, additionalData),
getCredentials: async (type) =>
getCredentials: async (type: string | (new () => ICredentialType)) =>
await getCredentials(workflow, node, type, additionalData, 'internal'),
getCurrentNodeParameter: (
parameterPath: string,
@ -4169,7 +4173,7 @@ export function getExecuteHookFunctions(
return ((workflow: Workflow, node: INode) => {
return {
...getCommonWorkflowFunctions(workflow, node, additionalData),
getCredentials: async (type) =>
getCredentials: async (type: string | (new () => ICredentialType)) =>
await getCredentials(workflow, node, type, additionalData, mode),
getMode: () => mode,
getActivationMode: () => activation,
@ -4243,7 +4247,7 @@ export function getExecuteWebhookFunctions(
}
return additionalData.httpRequest.body;
},
getCredentials: async (type) =>
getCredentials: async (type: string | (new () => ICredentialType)) =>
await getCredentials(workflow, node, type, additionalData, mode),
getHeaderData(): IncomingHttpHeaders {
if (additionalData.httpRequest === undefined) {

View file

@ -1,14 +1,4 @@
import type { ICredentialType } from 'n8n-workflow';
import { CredentialSchema, type InferCredentialSchema } from '../utils/CredentialSchema';
const ftpCredentialSchema = CredentialSchema.create({
host: CredentialSchema.string({ label: 'Host', placeholder: 'localhost' }),
port: CredentialSchema.number({ label: 'Port', default: 21 }),
username: CredentialSchema.string({ label: 'Username', optional: true }),
password: CredentialSchema.password({ optional: true }),
});
export type FtpCredentialSchema = InferCredentialSchema<typeof ftpCredentialSchema>;
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class Ftp implements ICredentialType {
name = 'ftp';
@ -17,5 +7,36 @@ export class Ftp implements ICredentialType {
documentationUrl = 'ftp';
properties = ftpCredentialSchema.toNodeProperties();
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
required: true,
type: 'string',
default: '',
placeholder: 'localhost',
},
{
displayName: 'Port',
name: 'port',
required: true,
type: 'number',
default: 21,
},
{
displayName: 'Username',
name: 'username',
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
];
}

View file

@ -1,23 +1,4 @@
import type { ICredentialType } from 'n8n-workflow';
import { CredentialSchema, type InferCredentialSchema } from '../utils/CredentialSchema';
const sftpCredentialSchema = CredentialSchema.create({
host: CredentialSchema.string({ label: 'Host', placeholder: 'localhost' }),
port: CredentialSchema.number({ label: 'Port', default: 22 }),
username: CredentialSchema.string({ label: 'Username' }),
password: CredentialSchema.password(),
privateKey: CredentialSchema.password({
label: 'Private Key',
description:
'String that contains a private key for either key-based or hostbased user authentication (OpenSSH format)',
}),
passphrase: CredentialSchema.password({
label: 'Passphrase',
description: 'For an encrypted private key, this is the passphrase used to decrypt it',
}),
});
export type SftpCredentialSchema = InferCredentialSchema<typeof sftpCredentialSchema>;
import type { ICredentialType, INodeProperties } from 'n8n-workflow';
export class Sftp implements ICredentialType {
name = 'sftp';
@ -26,5 +7,55 @@ export class Sftp implements ICredentialType {
documentationUrl = 'ftp';
properties = sftpCredentialSchema.toNodeProperties();
properties: INodeProperties[] = [
{
displayName: 'Host',
name: 'host',
required: true,
type: 'string',
default: '',
},
{
displayName: 'Port',
name: 'port',
required: true,
type: 'number',
default: 22,
},
{
displayName: 'Username',
name: 'username',
required: true,
type: 'string',
default: '',
},
{
displayName: 'Password',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
},
{
displayName: 'Private Key',
name: 'privateKey',
type: 'string',
typeOptions: { password: true },
default: '',
description:
'String that contains a private key for either key-based or hostbased user authentication (OpenSSH format)',
},
{
displayName: 'Passphrase',
name: 'passphrase',
typeOptions: {
password: true,
},
type: 'string',
default: '',
description: 'For an encrypted private key, this is the passphrase used to decrypt it',
},
];
}

View file

@ -1,47 +1,39 @@
import type { ICredentialType } from "n8n-workflow";
import {
CredentialSchema,
type InferCredentialSchema,
} from "../utils/CredentialSchema";
import { type ICredentialType, CredentialSchema, type InferCredentialSchema } from 'n8n-workflow';
export const strapiApiCredentialSchema = CredentialSchema.create({
notice: CredentialSchema.notice(
"Make sure you are using a user account not an admin account",
),
email: CredentialSchema.email({ placeholder: "name@email.com" }),
notice: CredentialSchema.notice('Make sure you are using a user account not an admin account'),
email: CredentialSchema.email({ placeholder: 'name@email.com' }),
password: CredentialSchema.password(),
url: CredentialSchema.url({
placeholder: "https://api.example.com",
placeholder: 'https://api.example.com',
}),
apiVersion: CredentialSchema.options({
label: "API Version",
description: "The version of api to be used",
label: 'API Version',
description: 'The version of api to be used',
options: [
{
label: "Version 4",
value: "v4",
description: "API version supported by Strapi 4",
label: 'Version 4',
value: 'v4',
description: 'API version supported by Strapi 4',
},
{
label: "Version 3",
value: "v3",
label: 'Version 3',
value: 'v3',
default: true,
description: "API version supported by Strapi 3",
description: 'API version supported by Strapi 3',
},
],
}),
});
export type StrapiApiCredential = InferCredentialSchema<
typeof strapiApiCredentialSchema
>;
export type StrapiApiCredential = InferCredentialSchema<typeof strapiApiCredentialSchema>;
export class StrapiApi implements ICredentialType {
name = "strapiApi";
name = 'strapiApi';
displayName = "Strapi API";
displayName = 'Strapi API';
documentationUrl = "strapi";
documentationUrl = 'strapi';
properties = strapiApiCredentialSchema.toNodeProperties();

View file

@ -1,7 +1,11 @@
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType } from 'n8n-workflow';
import { CredentialSchema, type InferCredentialSchema } from '../utils/CredentialSchema';
import {
CredentialSchema,
type ICredentialType,
type IAuthenticateGeneric,
type ICredentialTestRequest,
} from 'n8n-workflow';
export const strapiTokenApiCredential = CredentialSchema.create({
const strapiTokenApiCredentialSchema = CredentialSchema.create({
apiToken: CredentialSchema.password({ label: 'API Token' }),
url: CredentialSchema.url({ placeholder: 'https://api.example.com' }),
apiVersion: CredentialSchema.options({
@ -23,8 +27,6 @@ export const strapiTokenApiCredential = CredentialSchema.create({
}),
});
export type StrapiTokenApiCredential = InferCredentialSchema<typeof strapiTokenApiCredential>;
export class StrapiTokenApi implements ICredentialType {
name = 'strapiTokenApi';
@ -32,7 +34,9 @@ export class StrapiTokenApi implements ICredentialType {
documentationUrl = 'strapi';
properties = strapiTokenApiCredential.toNodeProperties();
properties = strapiTokenApiCredentialSchema.toNodeProperties();
schema = strapiTokenApiCredentialSchema;
authenticate: IAuthenticateGeneric = {
type: 'generic',

View file

@ -7,6 +7,7 @@ import ftpClient from 'promise-ftp';
import sftpClient from 'ssh2-sftp-client';
import { BINARY_ENCODING, NodeApiError, NodeConnectionType } from 'n8n-workflow';
import type {
ICredentialDataDecryptedObject,
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject,
@ -17,8 +18,6 @@ import type {
INodeTypeDescription,
JsonObject,
} from 'n8n-workflow';
import type { FtpCredentialSchema } from '@credentials/Ftp.credentials';
import type { SftpCredentialSchema } from '@credentials/Sftp.credentials';
import { formatPrivateKey, generatePairedItemData } from '@utils/utilities';
interface ReturnFtpItem {
@ -440,14 +439,14 @@ export class Ftp implements INodeType {
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> {
const credentials = credential.data as FtpCredentialSchema;
const credentials = credential.data as ICredentialDataDecryptedObject;
const ftp = new ftpClient();
try {
await ftp.connect({
host: credentials.host,
port: credentials.port,
user: credentials.username,
password: credentials.password,
host: credentials.host as string,
port: credentials.port as number,
user: credentials.username as string,
password: credentials.password as string,
});
} catch (error) {
await ftp.end();
@ -466,24 +465,24 @@ export class Ftp implements INodeType {
this: ICredentialTestFunctions,
credential: ICredentialsDecrypted,
): Promise<INodeCredentialTestResult> {
const credentials = credential.data as SftpCredentialSchema;
const credentials = credential.data as ICredentialDataDecryptedObject;
const sftp = new sftpClient();
try {
if (credentials.privateKey) {
await sftp.connect({
host: credentials.host,
port: credentials.port,
username: credentials.username,
password: credentials.password || undefined,
privateKey: formatPrivateKey(credentials.privateKey),
passphrase: credentials.passphrase,
host: credentials.host as string,
port: credentials.port as number,
username: credentials.username as string,
password: (credentials.password as string) || undefined,
privateKey: formatPrivateKey(credentials.privateKey as string),
passphrase: credentials.passphrase as string | undefined,
});
} else {
await sftp.connect({
host: credentials.host,
port: credentials.port,
username: credentials.username,
password: credentials.password,
host: credentials.host as string,
port: credentials.port as number,
username: credentials.username as string,
password: credentials.password as string,
});
}
} catch (error) {
@ -507,9 +506,14 @@ export class Ftp implements INodeType {
let returnItems: INodeExecutionData[] = [];
const operation = this.getNodeParameter('operation', 0);
let credentials: ICredentialDataDecryptedObject | undefined = undefined;
const protocol = this.getNodeParameter('protocol', 0) as string;
const credentials = await this.getCredentials(protocol === 'sftp' ? 'sftp' : 'ftp');
if (protocol === 'sftp') {
credentials = await this.getCredentials<ICredentialDataDecryptedObject>('sftp');
} else {
credentials = await this.getCredentials<ICredentialDataDecryptedObject>('ftp');
}
let ftp: ftpClient;
let sftp: sftpClient;
@ -517,30 +521,30 @@ export class Ftp implements INodeType {
try {
if (protocol === 'sftp') {
sftp = new sftpClient();
if ('privateKey' in credentials && credentials.privateKey) {
if (credentials.privateKey) {
await sftp.connect({
host: credentials.host,
port: credentials.port,
username: credentials.username,
password: credentials.password || undefined,
privateKey: formatPrivateKey(credentials.privateKey),
passphrase: credentials.passphrase,
host: credentials.host as string,
port: credentials.port as number,
username: credentials.username as string,
password: (credentials.password as string) || undefined,
privateKey: formatPrivateKey(credentials.privateKey as string),
passphrase: credentials.passphrase as string | undefined,
});
} else {
await sftp.connect({
host: credentials.host,
port: credentials.port,
username: credentials.username,
password: credentials.password,
host: credentials.host as string,
port: credentials.port as number,
username: credentials.username as string,
password: credentials.password as string,
});
}
} else {
ftp = new ftpClient();
await ftp.connect({
host: credentials.host,
port: credentials.port,
user: credentials.username,
password: credentials.password,
host: credentials.host as string,
port: credentials.port as number,
user: credentials.username as string,
password: credentials.password as string,
});
}
} catch (error) {

View file

@ -1,8 +1,8 @@
import type {
IExecuteFunctions,
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject,
IExecuteFunctions,
INodeCredentialTestResult,
INodeExecutionData,
INodeType,
@ -19,8 +19,9 @@ import {
validateJSON,
} from './GenericFunctions';
import { StrapiApi, type StrapiApiCredential } from '@credentials/StrapiApi.credentials';
import { StrapiTokenApi } from '../../credentials/StrapiTokenApi.credentials';
import { entryFields, entryOperations } from './EntryDescription';
import type { StrapiApiCredential } from '@credentials/StrapiApi.credentials';
export class Strapi implements INodeType {
description: INodeTypeDescription = {
@ -148,10 +149,10 @@ export class Strapi implements INodeType {
if (authenticationMethod === 'password') {
const { jwt } = await getToken.call(this);
apiVersion = (await this.getCredentials('strapiApi')).apiVersion;
apiVersion = (await this.getCredentials(StrapiApi)).apiVersion;
headers.Authorization = `Bearer ${jwt}`;
} else {
apiVersion = (await this.getCredentials('strapiTokenApi')).apiVersion;
apiVersion = (await this.getCredentials(StrapiTokenApi)).apiVersion;
}
for (let i = 0; i < length; i++) {

View file

@ -1,24 +0,0 @@
import type { FtpCredentialSchema } from './credentials/Ftp.credentials';
import type { SftpCredentialSchema } from './credentials/Sftp.credentials';
import type { StrapiApiCredential } from './credentials/StrapiApi.credentials';
import type { StrapiTokenApiCredential } from './credentials/StrapiTokenApi.credentials';
type CredentialSchemaMap = {
strapiApi: StrapiApiCredential;
strapiTokenApi: StrapiTokenApiCredential;
ftp: FtpCredentialSchema;
sftp: SftpCredentialSchema;
};
declare module 'n8n-workflow' {
interface FunctionsBase {
getCredentials<Type extends keyof CredentialSchemaMap | {}>(
type: Type,
itemIndex?: number,
): Promise<
Type extends keyof CredentialSchemaMap
? CredentialSchemaMap[Type]
: ICredentialDataDecryptedObject
>;
}
}

View file

@ -1,8 +1,9 @@
import z, { type ZodOptional, type ZodType } from "zod";
import type { INodeProperties } from "@/Interfaces";
import z, { type ZodOptional, type ZodType } from 'zod';
import type { INodeProperties } from '@/Interfaces';
function isObject(value: unknown): value is object {
return typeof value === "object" && value !== null && !Array.isArray(value);
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function removeUndefinedProperties<T extends object>(obj: T): T {
@ -40,25 +41,19 @@ class CredentialSchemaRootObject<
);
}
getProperty<K extends keyof T>(key: K): T[K]["metadata"] {
getProperty<K extends keyof T>(key: K): T[K]['metadata'] {
return this.shape[key].metadata;
}
toNodeProperties() {
return Object.entries(this.shape).map(([key, schema]) =>
schema.toNodeProperties(key),
);
return Object.entries(this.shape).map(([key, schema]) => schema.toNodeProperties(key));
}
}
type ToZodSchemaReturnType<
M extends BaseMetadata = BaseMetadata,
S extends ZodType | null = ZodType,
> = M["optional"] extends true
? S extends null
? null
: ZodOptional<NonNullable<S>>
: S;
> = M['optional'] extends true ? (S extends null ? null : ZodOptional<NonNullable<S>>) : S;
abstract class CredentialSchemaProperty<
M extends BaseMetadata = BaseMetadata,
@ -82,8 +77,8 @@ abstract class CredentialSchemaProperty<
name,
displayName: this.metadata.label,
description: this.metadata.description,
default: "",
type: "string",
default: '',
type: 'string',
});
}
}
@ -102,7 +97,7 @@ class CredentialSchemaString<
toNodeProperties(name: string): INodeProperties {
return removeUndefinedProperties({
...super.toNodeProperties(name),
type: "string",
type: 'string',
placeholder: this.metadata.placeholder,
typeOptions: { password: this.metadata.password },
});
@ -123,7 +118,7 @@ class CredentialSchemaNumber<
toNodeProperties(name: string): INodeProperties {
return removeUndefinedProperties({
...super.toNodeProperties(name),
type: "number",
type: 'number',
default: this.metadata.default,
});
}
@ -145,22 +140,18 @@ class CredentialSchemaOptions<
const { options } = this.metadata;
return removeUndefinedProperties({
...super.toNodeProperties(name),
type: "options",
type: 'options',
options: options.map((option) => ({
name: option.label,
value: option.value,
description: option.description,
})),
default:
options.find((option) => option.default)?.value ?? options[0].value,
default: options.find((option) => option.default)?.value ?? options[0].value,
});
}
}
class CredentialSchemaNotice extends CredentialSchemaProperty<
BaseMetadata,
null
> {
class CredentialSchemaNotice extends CredentialSchemaProperty<BaseMetadata, null> {
constructor(public notice: string) {
super({ label: notice }, null);
}
@ -168,7 +159,7 @@ class CredentialSchemaNotice extends CredentialSchemaProperty<
toNodeProperties(name: string): INodeProperties {
return {
...super.toNodeProperties(name),
type: "notice",
type: 'notice',
};
}
}
@ -208,18 +199,18 @@ type Zodify<
M extends BaseMetadata,
S extends ZodType | null,
T extends CredentialSchemaProperty<M, S>,
> = ReturnType<T["toZodSchema"]> extends z.ZodType
? ReturnType<T["toZodSchema"]>
: never;
> = ReturnType<T['toZodSchema']> extends z.ZodType ? ReturnType<T['toZodSchema']> : never;
type ZodifyObject<
M extends BaseMetadata,
S extends ZodType | null,
T extends { [k: string]: CredentialSchemaProperty<M, S> },
> = {
[K in keyof T as ReturnType<T[K]["toZodSchema"]> extends z.ZodType
? K
: never]: Zodify<M, S, T[K]>;
[K in keyof T as ReturnType<T[K]['toZodSchema']> extends z.ZodType ? K : never]: Zodify<
M,
S,
T[K]
>;
};
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
@ -233,11 +224,11 @@ export const CredentialSchema = {
return new CredentialSchemaRootObject(shape);
},
password(options: Omit<Optional<StringMetadata, "label">, "password"> = {}) {
password(options: Omit<Optional<StringMetadata, 'label'>, 'password'> = {}) {
return new CredentialSchemaString(
{
password: true,
label: "Password",
label: 'Password',
...options,
},
z.string(),
@ -251,24 +242,18 @@ export const CredentialSchema = {
number<M extends NumberMetadata>(options: M) {
return new CredentialSchemaNumber(options, z.number());
},
url(options: Optional<StringMetadata, "label"> = {}) {
return new CredentialSchemaString(
{ label: "URL", ...options },
z.string().url(),
);
url(options: Optional<StringMetadata, 'label'> = {}) {
return new CredentialSchemaString({ label: 'URL', ...options }, z.string().url());
},
email(options: Optional<StringMetadata, "label"> = {}) {
return new CredentialSchemaString(
{ label: "Email", ...options },
z.string().email(),
);
email(options: Optional<StringMetadata, 'label'> = {}) {
return new CredentialSchemaString({ label: 'Email', ...options }, z.string().email());
},
options<V extends string, M extends OptionsMetadata<V>>(options: M) {
return new CredentialSchemaOptions(
options,
z.enum(
options.options.map((option) => option.value) as NonEmptyArray<
M["options"][number]["value"]
M['options'][number]['value']
>,
),
);
@ -278,11 +263,8 @@ export const CredentialSchema = {
},
};
export type TCredentialSchema = CredentialSchemaRootObject<
BaseMetadata,
ZodType | null
>;
export type TCredentialSchema = CredentialSchemaRootObject<BaseMetadata, ZodType | null>;
export type InferCredentialSchema<T extends TCredentialSchema> = z.infer<
ReturnType<T["toZodSchema"]>
ReturnType<T['toZodSchema']>
>;

View file

@ -13,6 +13,7 @@ import type { SecureContextOptions } from 'tls';
import type { URLSearchParams } from 'url';
import type { CODE_EXECUTION_MODES, CODE_LANGUAGES, LOG_LEVELS } from './Constants';
import type { InferCredentialSchema, TCredentialSchema } from './CredentialSchema/CredentialSchema';
import type { IDeferredPromise } from './DeferredPromise';
import type { ExecutionCancelledError } from './errors';
import type { ExpressionError } from './errors/expression.error';
@ -864,12 +865,12 @@ export interface FunctionsBase {
type: string,
itemIndex?: number,
): Promise<T>;
getCredentials<T extends ICredentialType>(
getCredentials<T extends new () => ICredentialType>(
type: T,
itemIndex?: number,
): Promise<
T['schema'] extends TCredentialSchema
? InferCredentialSchema<T['schema']>
InstanceType<T>['schema'] extends TCredentialSchema
? InferCredentialSchema<InstanceType<T>['schema']>
: ICredentialDataDecryptedObject
>;
getCredentialsProperties(type: string): INodeProperties[];

View file

@ -51,10 +51,7 @@ export { ExpressionExtensions } from './Extensions';
export * as ExpressionParser from './Extensions/ExpressionParser';
export { NativeMethods } from './NativeMethods';
export * from './NodeParameters/FilterParameter';
export {
CredentialSchema,
InferCredentialSchema,
} from './CredentialSchema/CredentialSchema';
export * from './CredentialSchema/CredentialSchema';
export type {
DocMetadata,

View file

@ -1,4 +1,4 @@
import { CredentialSchema } from '../CredentialSchema';
import { CredentialSchema } from '../src/CredentialSchema/CredentialSchema';
describe('CredentialSchema', () => {
test('should convert Strapi credential to node properties', () => {

View file

@ -1871,10 +1871,13 @@ importers:
xml2js:
specifier: 'catalog:'
version: 0.6.2
zod:
specifier: 'catalog:'
version: 3.23.8
devDependencies:
'@langchain/core':
specifier: 'catalog:'
version: 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
version: 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
'@types/deep-equal':
specifier: ^1.0.1
version: 1.0.1
@ -15085,41 +15088,6 @@ snapshots:
- langchain
- openai
'@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8))':
dependencies:
ansi-styles: 5.2.0
camelcase: 6.3.0
decamelize: 1.2.0
js-tiktoken: 1.0.12
langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8)))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8))
mustache: 4.2.0
p-queue: 6.6.2
p-retry: 4.6.2
uuid: 10.0.0
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
- langchain
- openai
optional: true
'@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)':
dependencies:
ansi-styles: 5.2.0
camelcase: 6.3.0
decamelize: 1.2.0
js-tiktoken: 1.0.12
langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
mustache: 4.2.0
p-queue: 6.6.2
p-retry: 4.6.2
uuid: 10.0.0
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
- langchain
- openai
'@langchain/core@0.2.31(langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
ansi-styles: 5.2.0
@ -15230,20 +15198,7 @@ snapshots:
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
js-tiktoken: 1.0.12
openai: 4.58.0(zod@3.23.8)
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
- encoding
- langchain
- supports-color
optional: true
'@langchain/openai@0.2.10(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))':
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8))
js-tiktoken: 1.0.12
openai: 4.58.0(zod@3.23.8)
openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
transitivePeerDependencies:
@ -15289,15 +15244,6 @@ snapshots:
- openai
optional: true
'@langchain/textsplitters@0.0.3(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)':
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
js-tiktoken: 1.0.12
transitivePeerDependencies:
- langchain
- openai
optional: true
'@langchain/textsplitters@0.0.3(langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))':
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
@ -22087,30 +22033,6 @@ snapshots:
- supports-color
optional: true
langchain@0.2.18(axios@1.7.4)(openai@4.58.0):
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
'@langchain/openai': 0.2.10(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))
'@langchain/textsplitters': 0.0.3(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
binary-extensions: 2.2.0
js-tiktoken: 1.0.12
js-yaml: 4.1.0
jsonpointer: 5.0.1
langsmith: 0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
openapi-types: 12.1.3
p-retry: 4.6.2
uuid: 10.0.0
yaml: 2.3.4
zod: 3.23.8
zod-to-json-schema: 3.23.2(zod@3.23.8)
optionalDependencies:
axios: 1.7.4(debug@4.3.6)
transitivePeerDependencies:
- encoding
- openai
- supports-color
optional: true
langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a):
dependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8))
@ -22184,34 +22106,7 @@ snapshots:
optionalDependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8)))(openai@4.58.0(zod@3.23.8))
langchain: 0.2.18(axios@1.7.4)(openai@4.58.0(zod@3.23.8))
openai: 4.58.0(zod@3.23.8)
langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8)))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8)):
dependencies:
'@types/uuid': 10.0.0
commander: 10.0.1
p-queue: 6.6.2
p-retry: 4.6.2
semver: 7.6.0
uuid: 10.0.0
optionalDependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0(zod@3.23.8))
langchain: 0.2.18(axios@1.7.4)(openai@4.58.0)
openai: 4.58.0(zod@3.23.8)
optional: true
langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0))(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0):
dependencies:
'@types/uuid': 10.0.0
commander: 10.0.1
p-queue: 6.6.2
p-retry: 4.6.2
semver: 7.6.0
uuid: 10.0.0
optionalDependencies:
'@langchain/core': 0.2.31(langchain@0.2.18(axios@1.7.4)(openai@4.58.0))(openai@4.58.0)
langchain: 0.2.18(axios@1.7.4)(openai@4.58.0)
openai: 4.58.0(zod@3.23.8)
openai: 4.58.0(encoding@0.1.13)(zod@3.23.8)
langsmith@0.1.51(@langchain/core@0.2.31(langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)))(langchain@0.2.18(svs4mugxvnip77thgyjbfeyt2a))(openai@4.58.0(encoding@0.1.13)(zod@3.23.8)):
dependencies:
@ -23542,24 +23437,6 @@ snapshots:
- encoding
- supports-color
openai@4.58.0(zod@3.23.8):
dependencies:
'@types/node': 18.16.16
'@types/node-fetch': 2.6.4
'@types/qs': 6.9.15
abort-controller: 3.0.0
agentkeepalive: 4.2.1
form-data-encoder: 1.7.2
formdata-node: 4.4.1
node-fetch: 2.7.0(encoding@0.1.13)
qs: 6.11.0
optionalDependencies:
zod: 3.23.8
transitivePeerDependencies:
- encoding
- supports-color
optional: true
openapi-sampler@1.4.0:
dependencies:
'@types/json-schema': 7.0.15