Skip to content

Commit

Permalink
Additoinal Mongodb adapter tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
daffl committed Apr 29, 2022
1 parent 6b864ed commit bf16fb0
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 64 deletions.
101 changes: 44 additions & 57 deletions packages/mongodb/src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
})
}

get Model () {
return this.options.Model
}

set Model (value) {
this.options.Model = value
}

getObjectId (id: Id|ObjectId) {
if (this.options.disableObjectify) {
return id
Expand Down Expand Up @@ -80,11 +72,11 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
return select
}

async _findOrGet (id: NullableId, params: P) {
async $findOrGet (id: NullableId, params: P) {
return id === null ? await this.$find(params) : await this.$get(id, params)
}

_normalizeId (id: NullableId, data: Partial<D>): Partial<D> {
normalizeId (id: NullableId, data: Partial<D>): Partial<D> {
if (this.id === '_id') {
// Default Mongo IDs cannot be updated. The Mongo library handles
// this automatically.
Expand All @@ -100,30 +92,6 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
return data
}

// Map stray records into $set
_remapModifiers (data: { [key: string]: any }) {
let set: { [key: string]: any } = {}
// Step through the rooot
for (const key of Object.keys(data)) {
// Check for keys that aren't modifiers
if (key.charAt(0) !== '$') {
// Move them to set, and remove their record
set[key] = data[key]
delete data[key]
}
// If the '$set' modifier is used, add that to the temp variable
if (key === '$set') {
set = Object.assign(set, data[key])
delete data[key]
}
}
// If we have a $set, then attach to the data object
if (Object.keys(set).length > 0) {
(data as any).$set = set
}
return data
}

async $get (id: Id, params: P = {} as P): Promise<T> {
const { Model } = this.getOptions(params);
const { query, filters: { $select } } = this.filterQuery(id, params);
Expand Down Expand Up @@ -215,10 +183,10 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
}
const promise = Array.isArray(data)
? model.insertMany(data.map(setId), writeOptions).then(async result =>
Promise.all(Object.values(result.insertedIds).map(async _id => await model.findOne({ _id })))
Promise.all(Object.values(result.insertedIds).map(async _id => model.findOne({ _id })))
)
: model.insertOne(setId(data), writeOptions).then(async result =>
await model.findOne({ _id: result.insertedId })
model.findOne({ _id: result.insertedId })
)

return promise.then(select(params, this.id)).catch(errorHandler)
Expand All @@ -227,31 +195,49 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
async $patch (id: null, data: Partial<D>, params?: P): Promise<T[]>;
async $patch (id: Id, data: Partial<D>, params?: P): Promise<T>;
async $patch (id: NullableId, data: Partial<D>, _params?: P): Promise<T|T[]>;
async $patch (id: NullableId, data: Partial<D>, params: P = {} as P): Promise<T|T[]> {
async $patch (id: NullableId, _data: Partial<D>, params: P = {} as P): Promise<T|T[]> {
const data = this.normalizeId(id, _data)
const { Model } = this.getOptions(params)
const model = await Promise.resolve(Model)
const { query } = this.filterQuery(id, params)
const { query, filters: { $select, $limit } } = this.filterQuery(id, params)
const updateOptions = { ...params.mongodb }
const remapModifier = this._remapModifiers(this._normalizeId(id, data))
const idParams = {
const modifier = Object.keys(data).reduce((current, key) => {
const value = (data as any)[key]

if (key.charAt(0) !== '$') {
current.$set = {
...current.$set,
[key]: value
}
} else {
current[key] = value
}

return current
}, {} as any)
const originalIds = await this.$findOrGet(id, {
...params,
query,
query: {
...query,
$select: [ this.id ]
},
paginate: false
}
const originalItems = await this._findOrGet(id, idParams)
const items = Array.isArray(originalItems) ? originalItems : [originalItems]
})
const items = (Array.isArray(originalIds) ? originalIds : [originalIds])
const idList = items.map((item: any) => item[this.id])
const findParams = {
...params,
paginate: false,
query: { [this.id]: { $in: idList } }
query: {
...($limit === 0 ? { $limit: 0 } : {}),
[this.id]: { $in: idList },
$select
}
}

await model.updateMany(query, remapModifier, updateOptions)
await model.updateMany(query, modifier, updateOptions)

return this._findOrGet(id, findParams)
.then(select(params, this.id))
.catch(errorHandler)
return this.$findOrGet(id, findParams).catch(errorHandler)
}

async $update (id: Id, data: D, params: P = {} as P): Promise<T> {
Expand All @@ -260,11 +246,9 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
const { query } = this.filterQuery(id, params)
const replaceOptions = { ...params.mongodb }

await model.replaceOne(query, this._normalizeId(id, data), replaceOptions)
await model.replaceOne(query, this.normalizeId(id, data), replaceOptions)

return this._findOrGet(id, params)
.then(select(params, this.id))
.catch(errorHandler)
return this.$findOrGet(id, params).catch(errorHandler)
}

async $remove (id: null, params?: P): Promise<T[]>;
Expand All @@ -273,20 +257,23 @@ export class MongoDbAdapter<T, D = Partial<T>, P extends MongoDBAdapterParams =
async $remove (id: NullableId, params: P = {} as P): Promise<T|T[]> {
const { Model } = this.getOptions(params)
const model = await Promise.resolve(Model)
const { query } = this.filterQuery(id, params)
const { query, filters: { $select, $limit } } = this.filterQuery(id, params)
const deleteOptions = { ...params.mongodb }
const findParams = {
...params,
paginate: false,
query
query: {
...query,
...($limit === 0 ? { $limit: 0 } : {}),
$select
}
}

return this._findOrGet(id, findParams)
return this.$findOrGet(id, findParams)
.then(async items => {
await model.deleteMany(query, deleteOptions)
return items
})
.then(select(params, this.id))
.catch(errorHandler)
}
}
6 changes: 3 additions & 3 deletions packages/mongodb/src/error-handler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import errors from '@feathersjs/errors'
import { GeneralError } from '@feathersjs/errors'
import { MongoError } from 'mongodb'

export function errorHandler (error: MongoError): any {
// See https://github.com/mongodb/mongo/blob/master/docs/errors.md
if (error.name === 'MongoError') {
throw new errors.GeneralError(error, {
if (error && error.name && error.name.startsWith('Mongo')) {
throw new GeneralError(error, {
name: error.name,
code: error.code
})
Expand Down
22 changes: 18 additions & 4 deletions packages/mongodb/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Db, MongoClient, ObjectId } from 'mongodb'
import adapterTests from '@feathersjs/adapter-tests'
import assert from 'assert'
import { MongoMemoryServer } from 'mongodb-memory-server';

import { feathers } from '@feathersjs/feathers'
import errors from '@feathersjs/errors'
import { MongoDBService } from '../src'


const testSuite = adapterTests([
'.options',
'.events',
Expand Down Expand Up @@ -106,15 +108,18 @@ describe('Feathers MongoDB Service', () => {

let db: Db
let mongoClient: MongoClient
let mongod: MongoMemoryServer

before(async () => {
const client = await MongoClient.connect('mongodb://localhost:27017/feathers-test')
mongod = await MongoMemoryServer.create();

const client = await MongoClient.connect(mongod.getUri())

mongoClient = client
db = client.db('feathers-test')

app.use('people', new MongoDBService({
Model: db.collection('people-customid'),
Model: db.collection('people'),
events: ['testing']
}))
app.use('people-customid', new MongoDBService({
Expand All @@ -123,8 +128,6 @@ describe('Feathers MongoDB Service', () => {
events: ['testing']
}))

app.service('people').Model = db.collection('people')

db.collection('people-customid').deleteMany({})
db.collection('people').deleteMany({})
db.collection('todos').deleteMany({})
Expand All @@ -138,6 +141,7 @@ describe('Feathers MongoDB Service', () => {
after(async () => {
await db.dropDatabase();
await mongoClient.close();
await mongod.stop();
})

describe('Service utility functions', () => {
Expand Down Expand Up @@ -261,6 +265,16 @@ describe('Feathers MongoDB Service', () => {
assert.strictEqual(results.length, 1)
})

it('handles errors', async () => {
await assert.rejects(() => peopleService.create({
name: 'Dave'
}, {
mongodb: { collation: { locale: 'fdsfdsfds', strength: 1 } }
}), {
name: 'GeneralError'
})
})

it('updates with default behavior without collation param', async () => {
const query = { name: { $gt: 'AAA' } }

Expand Down

0 comments on commit bf16fb0

Please sign in to comment.