mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(Snowflake Node): Add support for Key-Pair authentication (#14833)
This commit is contained in:
committed by
GitHub
parent
c02696241b
commit
4302c5f474
@@ -30,11 +30,33 @@ export class Snowflake implements ICredentialType {
|
|||||||
description:
|
description:
|
||||||
'The default virtual warehouse to use for the session after connecting. Used for performing queries, loading data, etc.',
|
'The default virtual warehouse to use for the session after connecting. Used for performing queries, loading data, etc.',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Authentication',
|
||||||
|
name: 'authentication',
|
||||||
|
type: 'options',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Password',
|
||||||
|
value: 'password',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Key-Pair',
|
||||||
|
value: 'keyPair',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
default: 'password',
|
||||||
|
description: 'The way to authenticate with Snowflake',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Username',
|
displayName: 'Username',
|
||||||
name: 'username',
|
name: 'username',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authentication: ['password'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Password',
|
displayName: 'Password',
|
||||||
@@ -44,6 +66,28 @@ export class Snowflake implements ICredentialType {
|
|||||||
password: true,
|
password: true,
|
||||||
},
|
},
|
||||||
default: '',
|
default: '',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authentication: ['password'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Private Key',
|
||||||
|
name: 'privateKey',
|
||||||
|
type: 'string',
|
||||||
|
typeOptions: {
|
||||||
|
password: true,
|
||||||
|
rows: 4,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
authentication: ['keyPair'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: 'Private PEM key for Key-pair authentication with Snowflake',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Schema',
|
displayName: 'Schema',
|
||||||
|
|||||||
@@ -1,5 +1,43 @@
|
|||||||
|
import pick from 'lodash/pick';
|
||||||
import type snowflake from 'snowflake-sdk';
|
import type snowflake from 'snowflake-sdk';
|
||||||
|
|
||||||
|
const commonConnectionFields = [
|
||||||
|
'account',
|
||||||
|
'database',
|
||||||
|
'schema',
|
||||||
|
'warehouse',
|
||||||
|
'role',
|
||||||
|
'clientSessionKeepAlive',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type SnowflakeCredential = Pick<
|
||||||
|
snowflake.ConnectionOptions,
|
||||||
|
(typeof commonConnectionFields)[number]
|
||||||
|
> &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
authentication: 'password';
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
authentication: 'keyPair';
|
||||||
|
privateKey: string;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getConnectionOptions = (credential: SnowflakeCredential) => {
|
||||||
|
const connectionOptions: snowflake.ConnectionOptions = pick(credential, commonConnectionFields);
|
||||||
|
if (credential.authentication === 'keyPair') {
|
||||||
|
connectionOptions.authenticator = 'SNOWFLAKE_JWT';
|
||||||
|
connectionOptions.privateKey = credential.privateKey;
|
||||||
|
} else {
|
||||||
|
connectionOptions.username = credential.username;
|
||||||
|
connectionOptions.password = credential.password;
|
||||||
|
}
|
||||||
|
return connectionOptions;
|
||||||
|
};
|
||||||
|
|
||||||
export async function connect(conn: snowflake.Connection) {
|
export async function connect(conn: snowflake.Connection) {
|
||||||
return await new Promise<void>((resolve, reject) => {
|
return await new Promise<void>((resolve, reject) => {
|
||||||
conn.connect((error) => (error ? reject(error) : resolve()));
|
conn.connect((error) => (error ? reject(error) : resolve()));
|
||||||
|
|||||||
@@ -10,7 +10,13 @@ import snowflake from 'snowflake-sdk';
|
|||||||
|
|
||||||
import { getResolvables } from '@utils/utilities';
|
import { getResolvables } from '@utils/utilities';
|
||||||
|
|
||||||
import { connect, destroy, execute } from './GenericFunctions';
|
import {
|
||||||
|
connect,
|
||||||
|
destroy,
|
||||||
|
execute,
|
||||||
|
getConnectionOptions,
|
||||||
|
type SnowflakeCredential,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
export class Snowflake implements INodeType {
|
export class Snowflake implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
@@ -164,16 +170,14 @@ export class Snowflake implements INodeType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
const credentials = (await this.getCredentials(
|
const credentials = await this.getCredentials<SnowflakeCredential>('snowflake');
|
||||||
'snowflake',
|
|
||||||
)) as unknown as snowflake.ConnectionOptions;
|
|
||||||
const returnData: INodeExecutionData[] = [];
|
|
||||||
let responseData;
|
|
||||||
|
|
||||||
const connection = snowflake.createConnection(credentials);
|
const connectionOptions = getConnectionOptions(credentials);
|
||||||
|
const connection = snowflake.createConnection(connectionOptions);
|
||||||
|
|
||||||
await connect(connection);
|
await connect(connection);
|
||||||
|
|
||||||
|
const returnData: INodeExecutionData[] = [];
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
const operation = this.getNodeParameter('operation', 0);
|
const operation = this.getNodeParameter('operation', 0);
|
||||||
|
|
||||||
@@ -189,7 +193,7 @@ export class Snowflake implements INodeType {
|
|||||||
query = query.replace(resolvable, this.evaluateExpression(resolvable, i) as string);
|
query = query.replace(resolvable, this.evaluateExpression(resolvable, i) as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
responseData = await execute(connection, query, []);
|
const responseData = await execute(connection, query, []);
|
||||||
const executionData = this.helpers.constructExecutionMetaData(
|
const executionData = this.helpers.constructExecutionMetaData(
|
||||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||||
{ itemData: { item: i } },
|
{ itemData: { item: i } },
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { getConnectionOptions } from '../GenericFunctions';
|
||||||
|
|
||||||
|
describe('getConnectionOptions', () => {
|
||||||
|
const commonOptions = {
|
||||||
|
account: 'test-account',
|
||||||
|
database: 'test-database',
|
||||||
|
schema: 'test-schema',
|
||||||
|
warehouse: 'test-warehouse',
|
||||||
|
role: 'test-role',
|
||||||
|
clientSessionKeepAlive: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('should return connection options', () => {
|
||||||
|
it('with username and password for password authentication', () => {
|
||||||
|
const result = getConnectionOptions({
|
||||||
|
...commonOptions,
|
||||||
|
authentication: 'password',
|
||||||
|
username: 'test-username',
|
||||||
|
password: 'test-password',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
...commonOptions,
|
||||||
|
username: 'test-username',
|
||||||
|
password: 'test-password',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with private key for keyPair authentication', () => {
|
||||||
|
const result = getConnectionOptions({
|
||||||
|
...commonOptions,
|
||||||
|
authentication: 'keyPair',
|
||||||
|
privateKey: 'test-private-key',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
...commonOptions,
|
||||||
|
authenticator: 'SNOWFLAKE_JWT',
|
||||||
|
privateKey: 'test-private-key',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user