Skip to content

mongo operations, connection provider #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 30, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions packages/external-db-mongo/lib/connection_provider.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
const { MongoClient } = require('mongodb')
const SchemaProvider = require('./mongo_schema_provider')
const DataProvider = require('./mongo_data_provider')
const DataProvider = require('./mongo_data_provider')
const FilterParser = require('./sql_filter_transformer')
const DatabaseOperations = require('./mongo_operations')
const { notConnectedPool } = require('./mongo_utils')

const init = async (cfg) => {
const uri = `mongodb://${cfg.user}:${cfg.password}@${cfg.host}/${cfg.db}`
const client = new MongoClient(uri)
const pool = await client.connect()

const { pool, cleanup } = await client.connect().then((res) => {
return { pool: res, cleanup: async () => await pool.close() }
}).catch((e) => {
return {
pool: notConnectedPool(e),
cleanup: () => { }
}
})
const databaseOperations = new DatabaseOperations(client)

const filterParser = new FilterParser()
const dataProvider = new DataProvider(pool, filterParser)
const schemaProvider = new SchemaProvider(pool)

return { dataProvider: dataProvider, schemaProvider: schemaProvider, databaseOperations, connection: pool, cleanup: async () => await pool.close() }
return { dataProvider: dataProvider, schemaProvider: schemaProvider, databaseOperations, connection: pool, cleanup }
}

module.exports = init
23 changes: 12 additions & 11 deletions packages/external-db-mongo/lib/mongo_data_provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,34 @@ class DataProvider {
constructor(client, filterParser) {
this.client = client
this.filterParser = filterParser
this.pool = this.client.db()
}

async find(collectionName, filter, sort, skip, limit) {
const { filterExpr } = this.filterParser.transform(filter)
const { sortExpr } = this.filterParser.orderBy(sort)

return await this.pool.collection(collectionName)
return await this.client.db().collection(collectionName)
.find(filterExpr,
Object.assign({}, { skip, limit }, sortExpr))
.toArray()
}

async count(collectionName, filter) {
const { filterExpr } = this.filterParser.transform(filter)
return await this.pool.collection(collectionName)
.count(filterExpr)
const { filterExpr } = this.filterParser.transform(filter)

const result = await this.client.db().collection(collectionName)
.count(filterExpr)
return result
}

async insert(collectionName, items) {
const rss = await this.pool.collection(collectionName)
const rss = await this.client.db().collection(collectionName)
.insertMany(items)
return rss.insertedCount
}

async update(collectionName, items) {
const result = await this.pool.collection(collectionName)
const result = await this.client.db().collection(collectionName)
.bulkWrite(items.map((item)=>this.updateSingleObj(item)))
return result.nModified
}
Expand All @@ -48,20 +49,20 @@ class DataProvider {


async delete(collectionName, itemIds) {
const rss = await this.pool.collection(collectionName)
const rss = await this.client.db().collection(collectionName)
.remove({ _id: { $in: itemIds } })
return rss.deletedCount
}

async truncate(collectionName) {
await this.pool.collection(collectionName)
await this.client.db().collection(collectionName)
.remove({})
}

async aggregate(collectionName, filter, aggregation) {
const { fieldsStatement, havingFilter } = this.filterParser.parseAggregation(aggregation.processingStep, aggregation.postFilteringStep)
const { filterExpr } = this.filterParser.transform(filter)
const aggregateResult = await this.pool.collection(collectionName)
const aggregateResult = await this.client.db().collection(collectionName)
.aggregate( [ { $match: filterExpr },
fieldsStatement,
havingFilter
Expand All @@ -73,7 +74,7 @@ class DataProvider {
Object.assign(result,result._id)
if (isObject(result._id)) delete result._id
}
})
}) //todo - refactor this
return aggregateResult

}
Expand Down
25 changes: 8 additions & 17 deletions packages/external-db-mongo/lib/mongo_operations.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
const { notThrowingTranslateErrorCodes } = require('./sql_exception_translator')
const { DbConnectionError } = require('velo-external-db-commons').errors

class DatabaseOperations {
constructor(pool) {
this.sql = pool
constructor(client) {
this.client = client
}

async validateConnection() {
// try {
// const sql = await this.sql
// return await sql.query('SELECT 1')
// .then(() => { return { valid: true } })
// .catch((e) => { return { valid: false, error: notThrowingTranslateErrorCodes(e) } })
// } catch (err) {
// switch (err.code) {
// case 'ELOGIN':
// return { valid: false, error: new DbConnectionError(`Access to database denied - probably wrong credentials, sql message: ${err.message}`) }
// case 'ESOCKET':
// return { valid: false, error: new DbConnectionError(`Access to database denied - host is unavailable, sql message: ${err.message}`) }
// }
// throw new Error(`Unrecognized error: ${err.message}`)
// }
try {
await this.client.connect()
return { valid: true }
} catch (err) {
return { valid: false, error: new DbConnectionError(err.message) }
}
}
}

Expand Down
14 changes: 6 additions & 8 deletions packages/external-db-mongo/lib/mongo_utils.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// const SqlString = require('tsqlstring')

const escapeId = s => s //SqlString.escapeId(s)
const escape = s => s //SqlString.escape(s)
const patchFieldName = s => s //`x${SqlString.escape(s).substring(1).slice(0, -1)}`
const validateLiteral = s => s //`@${patchFieldName(s)}`
const EMPTY_FILTER = {filterExpr:{}}

module.exports = { escapeId, validateLiteral, patchFieldName, escape, EMPTY_FILTER }
const notConnectedPool = (err) => {
return {
db: ()=> { throw err },
}
}
module.exports = { EMPTY_FILTER, notConnectedPool }
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
// const { ConnectionPool } = require('mongodb')
const DatabaseOperations = require('../../lib/mongo_operations')
const { MongoClient } = require('mongodb')


const createPool = modify => {
// const config = {
// user: 'sa',
// password: 't9D4:EHfU6Xgccs-',
// database: 'tempdb',
// server: 'localhost',
// port: 1433,
// pool: {
// max: 1,
// min: 0,
// idleTimeoutMillis: 30000
// },
// options: {
// // encrypt: true, // for azure
// trustServerCertificate: true // change to true for local dev / self-signed certs
// }
// }
//
// const _pool = new ConnectionPool(Object.assign({}, config, modify ))
// return _pool.connect()
}
const config = {
user: 'root',
password: 'pass',
database: 'testdb',
host:'localhost'
}
const modifiedConfig = Object.assign({},config,modify)
const uri = `mongodb://${modifiedConfig.user}:${modifiedConfig.password}@${modifiedConfig.host}/${modifiedConfig.database}`
const client = new MongoClient(uri, { serverSelectionTimeoutMS: 5000 })
return client

}
const dbOperationWithMisconfiguredPassword = () => new DatabaseOperations(createPool( { password: 'wrong'} ))

const dbOperationWithMisconfiguredDatabase = () => new DatabaseOperations(createPool( { database: 'wrong'} ))

const dbOperationWithMisconfiguredHost = () => new DatabaseOperations(createPool( { server: 'wrong'} ))
const dbOperationWithMisconfiguredHost = () => new DatabaseOperations(createPool( { host: 'wrong'} ))

const dbOperationWithValidDB = () => {
const connection = createPool({ } )
const dbOperations = new DatabaseOperations( connection )

return { dbOperations, cleanup: async () => await (await connection).close()}
return { dbOperations, cleanup: async () => await connection.close()}
}

const dbOperationWithMisconfiguredConfig = [() => dbOperationWithMisconfiguredPassword(),
() => dbOperationWithMisconfiguredDatabase(),
() => dbOperationWithMisconfiguredHost()]

module.exports = {
dbOperationWithMisconfiguredPassword, dbOperationWithMisconfiguredDatabase,
dbOperationWithMisconfiguredHost, dbOperationWithValidDB
dbOperationWithMisconfiguredHost, dbOperationWithValidDB, dbOperationWithMisconfiguredConfig
}
4 changes: 2 additions & 2 deletions packages/external-db-mssql/lib/mssql_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const validateLiteral = s => `@${patchFieldName(s)}`
const notConnectedPool = (pool, err) => {
return {
...pool,
query: async () => { throw err },
request: async () => { throw err },
query: () => { throw err },
request: () => { throw err },
connect: async () => { return await pool.connect() }
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/velo-external-db/test/env/env.db.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const mysql = require('../resources/engines/mysql_resources')
const spanner = require('../resources/engines/spanner_resources')
const firestore = require('../resources/engines/firestore_resources')
const mssql = require('../resources/engines/mssql_resources')
const mongo = require ('../resources/engines/mongo_resources')
const { sleep } = require('test-commons')

module.exports = async () => {
Expand All @@ -28,6 +29,9 @@ module.exports = async () => {
case 'mssql':
await mssql.initEnv()
break
case 'mongo':
await mongo.initEnv()
break

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const spanner = require('external-db-spanner')
const postgres = require('external-db-postgres')
const firestore = require('external-db-firestore')
const mssql = require('external-db-mssql')
const mongo = require ('external-db-mongo')

const env = {
driver: Uninitialized,
Expand All @@ -20,13 +21,15 @@ const mysqlTestEnvInit = async () => await init(mysql)
const spannerTestEnvInit = async () => await init(spanner)
const firestoreTestEnvInit = async () => await init(firestore)
const mssqlTestEnvInit = async () => await init(mssql)
const mongoTestEnvInit = async () => await init(mongo)

const testSuits = () => [
['MySql', mysqlTestEnvInit],
['Postgres', postgresTestEnvInit],
['Spanner', spannerTestEnvInit],
['Firestore', firestoreTestEnvInit],
['Sql Server', mssqlTestEnvInit],
['Mongo', mongoTestEnvInit]
].filter( ([name]) => name.toLowerCase() === process.env.TEST_ENGINE || (name === 'Sql Server' && process.env.TEST_ENGINE === 'mssql') )


Expand Down
35 changes: 28 additions & 7 deletions packages/velo-external-db/test/storage/database_operation.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { DbConnectionError } = require('velo-external-db-commons').errors
const each = require('jest-each').default;
const { env, testSuits } = require('../resources/operations_resources')

const { Uninitialized, gen } = require('test-commons')

describe('Check Pool Connection', () => {
each(testSuits()).describe('%s', (dbType, setup) => {
Expand All @@ -10,18 +10,30 @@ describe('Check Pool Connection', () => {
setup()
})

test('pool connection with wrong password will throw AccessDeniedError.', async () => {
//1
if (dbType === 'Mongo')
test('pool connection with wrong config will return object with valid equal to false and DbConnectionError.', async () => {
const dbOperation = (gen.randomObjectFromArray(env.driver.dbOperationWithMisconfiguredConfig))()
// todo - move randomObjectFromArray to beforeEach if we keeping this and implement dbOperationWithMisconfiguredConfig for every driver.

const validateConnection = await dbOperation.validateConnection()

expect(validateConnection.valid).toBeFalsy()
expect(validateConnection.error).toBeInstanceOf(DbConnectionError)
})

test('pool connection with wrong password will return appropriate error.', async () => {
const dbOperation = await env.driver.dbOperationWithMisconfiguredPassword()

const validateConnection = await dbOperation.validateConnection()

expect(validateConnection.valid).toBeFalsy()
expect(validateConnection.error).toBeInstanceOf(DbConnectionError)
expect(validateConnection.error.message).toContain('wrong credentials')
expect(validateConnection.error.message).toMatch(/(wrong credentials|Authentication failed)/i)
})

if (dbType !== 'Firestore') {
test('pool connection with wrong database will throw DatabaseDoesNotExists.', async () => {
if (dbType !== 'Firestore' && dbType !== 'Mongo') {
test('pool connection with wrong database will return appropriate error.', async () => {
const dbOperation = await env.driver.dbOperationWithMisconfiguredDatabase()

const validateConnection = await dbOperation.validateConnection()
Expand All @@ -30,7 +42,7 @@ describe('Check Pool Connection', () => {
expect(validateConnection.error).toBeInstanceOf(DbConnectionError)
})

test('pool connection with wrong host will throw HostDoesNotExists.', async () => {
test('pool connection with wrong host will return appropriate error.', async () => {
const dbOperation = await env.driver.dbOperationWithMisconfiguredHost()

const validateConnection = await dbOperation.validateConnection()
Expand All @@ -41,7 +53,8 @@ describe('Check Pool Connection', () => {
})
}

test('pool connection with valid DB will not throw', async () => {
//2
test('pool connection with valid DB will return object with without error', async () => {
const { dbOperations, cleanup } = await env.driver.dbOperationWithValidDB()

const validateConnection = await dbOperations.validateConnection()
Expand All @@ -51,5 +64,13 @@ describe('Check Pool Connection', () => {
await cleanup()
})
})

const ctx = {
// dbOperationWithMisconfiguredConfig: Uninitialized
}

beforeEach(() => {
// ctx.dbOperationWithMisconfiguredConfig = gen.randomObjectFromArray(env.driver.dbOperationWithMisconfiguredConfig)
});
})