2022-03-10 06:59:48 +00:00
|
|
|
import { QueryResult, QueryService, QueryError, ConnectionTestResult } from '@tooljet-plugins/common';
|
2021-09-21 13:48:28 +00:00
|
|
|
const { MongoClient } = require('mongodb');
|
2022-01-14 08:23:57 +00:00
|
|
|
const JSON5 = require('json5');
|
|
|
|
|
import { EJSON } from 'bson';
|
2022-03-10 06:59:48 +00:00
|
|
|
import { SourceOptions, QueryOptions } from './types';
|
2021-07-16 11:34:06 +00:00
|
|
|
|
|
|
|
|
export default class MongodbService implements QueryService {
|
2022-01-24 13:59:21 +00:00
|
|
|
async run(sourceOptions: SourceOptions, queryOptions: QueryOptions, dataSourceId: string): Promise<QueryResult> {
|
2022-01-14 08:23:57 +00:00
|
|
|
const { db, close } = await this.getConnection(sourceOptions);
|
2021-09-21 13:48:28 +00:00
|
|
|
let result = {};
|
2021-07-17 07:59:28 +00:00
|
|
|
const operation = queryOptions.operation;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
switch (operation) {
|
|
|
|
|
case 'list_collections':
|
|
|
|
|
result = await db.listCollections().toArray();
|
2021-09-21 13:48:28 +00:00
|
|
|
break;
|
2021-07-17 07:59:28 +00:00
|
|
|
case 'insert_one':
|
2022-01-14 08:23:57 +00:00
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.insertOne(this.parseEJSON(queryOptions.document), this.parseEJSON(queryOptions.options));
|
2021-07-17 07:59:28 +00:00
|
|
|
break;
|
|
|
|
|
case 'insert_many':
|
2022-01-14 08:23:57 +00:00
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.insertMany(this.parseEJSON(queryOptions.documents), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'find_one':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.findOne(this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'find_many':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.find(this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options))
|
|
|
|
|
.toArray();
|
|
|
|
|
break;
|
|
|
|
|
case 'count_total':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.estimatedDocumentCount(this.parseEJSON(queryOptions.options));
|
|
|
|
|
result = { count: result };
|
|
|
|
|
break;
|
|
|
|
|
case 'count':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.countDocuments(this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options));
|
|
|
|
|
result = { count: result };
|
|
|
|
|
break;
|
|
|
|
|
case 'distinct':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.distinct(queryOptions.field, this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'update_one':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.updateOne(
|
|
|
|
|
this.parseEJSON(queryOptions.filter),
|
|
|
|
|
this.parseEJSON(queryOptions.update),
|
|
|
|
|
this.parseEJSON(queryOptions.options)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 'update_many':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.updateMany(
|
|
|
|
|
this.parseEJSON(queryOptions.filter),
|
|
|
|
|
this.parseEJSON(queryOptions.update),
|
|
|
|
|
this.parseEJSON(queryOptions.options)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 'replace_one':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.replaceOne(
|
|
|
|
|
this.parseEJSON(queryOptions.filter),
|
|
|
|
|
this.parseEJSON(queryOptions.replacement),
|
|
|
|
|
this.parseEJSON(queryOptions.options)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 'find_one_replace':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.findOneAndReplace(
|
|
|
|
|
this.parseEJSON(queryOptions.filter),
|
|
|
|
|
this.parseEJSON(queryOptions.replacement),
|
|
|
|
|
this.parseEJSON(queryOptions.options)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 'find_one_update':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.findOneAndUpdate(
|
|
|
|
|
this.parseEJSON(queryOptions.filter),
|
|
|
|
|
this.parseEJSON(queryOptions.update),
|
|
|
|
|
this.parseEJSON(queryOptions.options)
|
|
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case 'find_one_delete':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.findOneAndDelete(this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'delete_one':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.deleteOne(this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'delete_many':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.deleteMany(this.parseEJSON(queryOptions.filter), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'bulk_write':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.bulkWrite(this.parseEJSON(queryOptions.operations), this.parseEJSON(queryOptions.options));
|
|
|
|
|
break;
|
|
|
|
|
case 'aggregate':
|
|
|
|
|
result = await db
|
|
|
|
|
.collection(queryOptions.collection)
|
|
|
|
|
.aggregate(this.parseEJSON(queryOptions.pipeline), this.parseEJSON(queryOptions.options))
|
|
|
|
|
.toArray();
|
2021-09-21 13:48:28 +00:00
|
|
|
break;
|
2021-07-17 07:59:28 +00:00
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.log(err);
|
2022-01-14 08:23:57 +00:00
|
|
|
throw new QueryError('Query could not be completed', err.message, {});
|
|
|
|
|
} finally {
|
|
|
|
|
await close();
|
2021-07-17 07:59:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
status: 'ok',
|
2021-09-21 13:48:28 +00:00
|
|
|
data: result,
|
|
|
|
|
};
|
2021-07-18 07:43:19 +00:00
|
|
|
}
|
|
|
|
|
|
2022-01-14 08:23:57 +00:00
|
|
|
parseEJSON(maybeEJSON?: string): any {
|
|
|
|
|
if (!maybeEJSON) return {};
|
|
|
|
|
|
|
|
|
|
return EJSON.parse(JSON.stringify(JSON5.parse(maybeEJSON)));
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-24 13:59:21 +00:00
|
|
|
async testConnection(sourceOptions: SourceOptions): Promise<ConnectionTestResult> {
|
2022-01-14 08:23:57 +00:00
|
|
|
const { db, close } = await this.getConnection(sourceOptions);
|
2021-07-18 07:43:19 +00:00
|
|
|
await db.listCollections().toArray();
|
2022-01-14 08:23:57 +00:00
|
|
|
await close();
|
2021-07-18 07:43:19 +00:00
|
|
|
|
|
|
|
|
return {
|
2021-09-21 13:48:28 +00:00
|
|
|
status: 'ok',
|
|
|
|
|
};
|
2021-07-17 07:59:28 +00:00
|
|
|
}
|
2021-07-16 11:34:06 +00:00
|
|
|
|
2022-01-24 13:59:21 +00:00
|
|
|
async getConnection(sourceOptions: SourceOptions): Promise<any> {
|
2022-01-14 08:23:57 +00:00
|
|
|
let db = null,
|
|
|
|
|
client;
|
2021-07-17 07:59:28 +00:00
|
|
|
const connectionType = sourceOptions['connection_type'];
|
2021-07-16 11:34:06 +00:00
|
|
|
|
2021-09-21 13:48:28 +00:00
|
|
|
if (connectionType === 'manual') {
|
2021-07-16 11:34:06 +00:00
|
|
|
const database = sourceOptions.database;
|
|
|
|
|
const host = sourceOptions.host;
|
|
|
|
|
const port = sourceOptions.port;
|
2023-03-13 09:23:22 +00:00
|
|
|
const username = encodeURIComponent(sourceOptions.username);
|
|
|
|
|
const password = encodeURIComponent(sourceOptions.password);
|
2021-07-16 11:34:06 +00:00
|
|
|
|
|
|
|
|
const needsAuthentication = username !== '' && password !== '';
|
2021-09-21 13:48:28 +00:00
|
|
|
const uri = needsAuthentication
|
|
|
|
|
? `mongodb://${username}:${password}@${host}:${port}`
|
|
|
|
|
: `mongodb://${host}:${port}`;
|
2021-07-16 11:34:06 +00:00
|
|
|
|
2022-01-14 08:23:57 +00:00
|
|
|
client = new MongoClient(uri, {
|
2021-09-21 13:48:28 +00:00
|
|
|
directConnection: true,
|
2021-07-16 11:34:06 +00:00
|
|
|
});
|
|
|
|
|
await client.connect();
|
|
|
|
|
|
|
|
|
|
db = client.db(database);
|
2021-09-21 13:48:28 +00:00
|
|
|
} else {
|
2021-07-16 11:34:06 +00:00
|
|
|
const connectionString = sourceOptions['connection_string'];
|
2023-03-13 09:23:22 +00:00
|
|
|
|
|
|
|
|
const password = connectionString.match(/(?<=:\/\/)(.*):(.*)@/)[2];
|
|
|
|
|
|
|
|
|
|
const encodedPassword = encodeURIComponent(password);
|
|
|
|
|
|
|
|
|
|
const encodedConnectionString = connectionString.replace(password, encodedPassword);
|
|
|
|
|
|
|
|
|
|
client = new MongoClient(encodedConnectionString, { useNewUrlParser: true, useUnifiedTopology: true });
|
2021-07-16 11:34:06 +00:00
|
|
|
await client.connect();
|
|
|
|
|
db = client.db();
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-14 08:23:57 +00:00
|
|
|
return {
|
|
|
|
|
db,
|
|
|
|
|
close: async () => {
|
|
|
|
|
await client?.close?.();
|
|
|
|
|
},
|
|
|
|
|
};
|
2021-07-16 11:34:06 +00:00
|
|
|
}
|
|
|
|
|
}
|