feat(MongoDB Node): Add driver info to MongoDB nodes (#18615)

This commit is contained in:
Durran Jordan
2025-09-05 15:07:09 +02:00
committed by GitHub
parent 18408bcaa1
commit 9a2d942835
5 changed files with 52 additions and 16 deletions

View File

@@ -55,12 +55,16 @@ describe('VectorStoreMongoDBAtlas', () => {
connectionString: 'mongodb://localhost:27017', connectionString: 'mongodb://localhost:27017',
}); });
const client1 = await getMongoClient(mockContext); const client1 = await getMongoClient(mockContext, 1.1);
const client2 = await getMongoClient(mockContext); const client2 = await getMongoClient(mockContext, 1.1);
expect(MockMongoClient).toHaveBeenCalledTimes(1); expect(MockMongoClient).toHaveBeenCalledTimes(1);
expect(MockMongoClient).toHaveBeenCalledWith('mongodb://localhost:27017', { expect(MockMongoClient).toHaveBeenCalledWith('mongodb://localhost:27017', {
appName: 'devrel.integration.n8n_vector_integ', appName: 'devrel.integration.n8n_vector_integ',
driverInfo: {
name: 'n8n_vector',
version: '1.1',
},
}); });
expect(mockClient1.connect).toHaveBeenCalledTimes(1); expect(mockClient1.connect).toHaveBeenCalledTimes(1);
expect(mockClient1.close).not.toHaveBeenCalled(); expect(mockClient1.close).not.toHaveBeenCalled();
@@ -81,15 +85,23 @@ describe('VectorStoreMongoDBAtlas', () => {
connectionString: 'mongodb://different-host:27017', connectionString: 'mongodb://different-host:27017',
}); });
const client1 = await getMongoClient(mockContext); const client1 = await getMongoClient(mockContext, 1.1);
const client2 = await getMongoClient(mockContext); const client2 = await getMongoClient(mockContext, 1.1);
expect(MockMongoClient).toHaveBeenCalledTimes(2); expect(MockMongoClient).toHaveBeenCalledTimes(2);
expect(MockMongoClient).toHaveBeenNthCalledWith(1, 'mongodb://localhost:27017', { expect(MockMongoClient).toHaveBeenNthCalledWith(1, 'mongodb://localhost:27017', {
appName: 'devrel.integration.n8n_vector_integ', appName: 'devrel.integration.n8n_vector_integ',
driverInfo: {
name: 'n8n_vector',
version: '1.1',
},
}); });
expect(MockMongoClient).toHaveBeenNthCalledWith(2, 'mongodb://different-host:27017', { expect(MockMongoClient).toHaveBeenNthCalledWith(2, 'mongodb://different-host:27017', {
appName: 'devrel.integration.n8n_vector_integ', appName: 'devrel.integration.n8n_vector_integ',
driverInfo: {
name: 'n8n_vector',
version: '1.1',
},
}); });
expect(mockClient1.connect).toHaveBeenCalledTimes(1); expect(mockClient1.connect).toHaveBeenCalledTimes(1);
expect(mockClient1.close).toHaveBeenCalledTimes(1); expect(mockClient1.close).toHaveBeenCalledTimes(1);

View File

@@ -152,6 +152,7 @@ const insertFields: INodeProperties[] = [
export const mongoConfig = { export const mongoConfig = {
client: null as MongoClient | null, client: null as MongoClient | null,
connectionString: '', connectionString: '',
nodeVersion: 0,
}; };
/** /**
@@ -164,17 +165,26 @@ type IFunctionsContext = IExecuteFunctions | ISupplyDataFunctions | ILoadOptions
* @param context - The context. * @param context - The context.
* @returns the MongoClient for the node. * @returns the MongoClient for the node.
*/ */
export async function getMongoClient(context: any) { export async function getMongoClient(context: any, version: number) {
const credentials = await context.getCredentials(MONGODB_CREDENTIALS); const credentials = await context.getCredentials(MONGODB_CREDENTIALS);
const connectionString = credentials.connectionString as string; const connectionString = credentials.connectionString as string;
if (!mongoConfig.client || mongoConfig.connectionString !== connectionString) { if (
!mongoConfig.client ||
mongoConfig.connectionString !== connectionString ||
mongoConfig.nodeVersion !== version
) {
if (mongoConfig.client) { if (mongoConfig.client) {
await mongoConfig.client.close(); await mongoConfig.client.close();
} }
mongoConfig.connectionString = connectionString; mongoConfig.connectionString = connectionString;
mongoConfig.nodeVersion = version;
mongoConfig.client = new MongoClient(connectionString, { mongoConfig.client = new MongoClient(connectionString, {
appName: 'devrel.integration.n8n_vector_integ', appName: 'devrel.integration.n8n_vector_integ',
driverInfo: {
name: 'n8n_vector',
version: version.toString(),
},
}); });
await mongoConfig.client.connect(); await mongoConfig.client.connect();
} }
@@ -198,7 +208,7 @@ export async function getDatabase(context: IFunctionsContext, client: MongoClien
*/ */
export async function getCollections(this: ILoadOptionsFunctions) { export async function getCollections(this: ILoadOptionsFunctions) {
try { try {
const client = await getMongoClient(this); const client = await getMongoClient(this, this.getNode().typeVersion);
const db = await getDatabase(this, client); const db = await getDatabase(this, client);
const collections = await db.listCollections().toArray(); const collections = await db.listCollections().toArray();
const results = collections.map((collection) => ({ const results = collections.map((collection) => ({
@@ -308,7 +318,7 @@ export class VectorStoreMongoDBAtlas extends createVectorStoreNode({
sharedFields, sharedFields,
async getVectorStoreClient(context, _filter, embeddings, itemIndex) { async getVectorStoreClient(context, _filter, embeddings, itemIndex) {
try { try {
const client = await getMongoClient(context); const client = await getMongoClient(context, context.getNode().typeVersion);
const db = await getDatabase(context, client); const db = await getDatabase(context, client);
const collectionName = getCollectionName(context, itemIndex); const collectionName = getCollectionName(context, itemIndex);
const mongoVectorIndexName = getVectorIndexName(context, itemIndex); const mongoVectorIndexName = getVectorIndexName(context, itemIndex);
@@ -358,7 +368,7 @@ export class VectorStoreMongoDBAtlas extends createVectorStoreNode({
}, },
async populateVectorStore(context, embeddings, documents, itemIndex) { async populateVectorStore(context, embeddings, documents, itemIndex) {
try { try {
const client = await getMongoClient(context); const client = await getMongoClient(context, context.getNode().typeVersion);
const db = await getDatabase(context, client); const db = await getDatabase(context, client);
const collectionName = getCollectionName(context, itemIndex); const collectionName = getCollectionName(context, itemIndex);
const mongoVectorIndexName = getVectorIndexName(context, itemIndex); const mongoVectorIndexName = getVectorIndexName(context, itemIndex);

View File

@@ -153,8 +153,16 @@ export function stringifyObjectIDs(items: INodeExecutionData[]) {
return items; return items;
} }
export async function connectMongoClient(connectionString: string, credentials: IDataObject = {}) { export async function connectMongoClient(
connectionString: string,
nodeVersion: number,
credentials: IDataObject = {},
) {
let client: MongoClient; let client: MongoClient;
const driverInfo = {
name: 'n8n_crud',
version: nodeVersion > 0 ? nodeVersion.toString() : 'unknown',
};
if (credentials.tls) { if (credentials.tls) {
const ca = credentials.ca ? formatPrivateKey(credentials.ca as string) : undefined; const ca = credentials.ca ? formatPrivateKey(credentials.ca as string) : undefined;
@@ -172,10 +180,10 @@ export async function connectMongoClient(connectionString: string, credentials:
client = await MongoClient.connect(connectionString, { client = await MongoClient.connect(connectionString, {
tls: true, tls: true,
secureContext, secureContext,
driverInfo,
}); });
} else { } else {
client = await MongoClient.connect(connectionString); client = await MongoClient.connect(connectionString, { driverInfo });
} }
return client; return client;
} }

View File

@@ -75,7 +75,9 @@ export class MongoDb implements INodeType {
); );
} }
const client = await connectMongoClient(connectionString, credentials); // Note: ICredentialTestFunctions doesn't have a way to get the Node instance
// so we set the version to 0
const client = await connectMongoClient(connectionString, 0, credentials);
const { databases } = await client.db().admin().listDatabases(); const { databases } = await client.db().admin().listDatabases();
@@ -102,7 +104,8 @@ export class MongoDb implements INodeType {
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const credentials = await this.getCredentials('mongoDb'); const credentials = await this.getCredentials('mongoDb');
const { database, connectionString } = validateAndResolveMongoCredentials(this, credentials); const { database, connectionString } = validateAndResolveMongoCredentials(this, credentials);
const client = await connectMongoClient(connectionString, credentials); const nodeVersion = this.getNode().typeVersion;
const client = await connectMongoClient(connectionString, nodeVersion, credentials);
let returnData: INodeExecutionData[] = []; let returnData: INodeExecutionData[] = [];
try { try {
@@ -110,7 +113,6 @@ export class MongoDb implements INodeType {
const items = this.getInputData(); const items = this.getInputData();
const operation = this.getNodeParameter('operation', 0); const operation = this.getNodeParameter('operation', 0);
const nodeVersion = this.getNode().typeVersion;
let itemsLength = items.length ? 1 : 0; let itemsLength = items.length ? 1 : 0;
let fallbackPairedItems: IPairedItemData[] | null = null; let fallbackPairedItems: IPairedItemData[] | null = null;

View File

@@ -3,7 +3,11 @@ import { Collection, MongoClient } from 'mongodb';
import type { INodeParameters, WorkflowTestData } from 'n8n-workflow'; import type { INodeParameters, WorkflowTestData } from 'n8n-workflow';
MongoClient.connect = async function () { MongoClient.connect = async function () {
const client = new MongoClient('mongodb://localhost:27017'); const driverInfo = {
name: 'n8n_crud',
version: '1.2',
};
const client = new MongoClient('mongodb://localhost:27017', { driverInfo });
return client; return client;
}; };