Skip to content

Commit

Permalink
feat: update to mongodb driver v4
Browse files Browse the repository at this point in the history
changed mongodb driver types, parsing insert results returns inserted count

BREAKING CHANGE: parsing inserts no longer accepts a projection and returns inserted count instead
of inserted documents
  • Loading branch information
valverdealbo committed Jul 25, 2021
1 parent 0079176 commit 41439a1
Show file tree
Hide file tree
Showing 9 changed files with 12,635 additions and 12,241 deletions.
1 change: 1 addition & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24,739 changes: 12,596 additions & 12,143 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 4 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@
"devDependencies": {
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "^9.0.0",
"@semantic-release/npm": "^7.1.1",
"@semantic-release/release-notes-generator": "^9.0.2",
"@types/jest": "^26.0.22",
"@types/lodash": "^4.14.168",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"commitizen": "^4.2.3",
Expand All @@ -40,19 +37,17 @@
"eslint-plugin-jest": "^24.3.5",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^5.1.0",
"jest": "^26.6.3",
"mongodb-memory-server": "^6.9.3",
"jest": "^27.0.6",
"mongodb-memory-server": "^7.3.2",
"npm": "^7.10.0",
"prettier": "^2.2.1",
"rimraf": "^3.0.2",
"semantic-release": "^17.4.2",
"ts-jest": "^26.5.4",
"ts-jest": "^27.0.4",
"typescript": "^4.2.4"
},
"dependencies": {
"@types/mongodb": "^3.6.12",
"lodash": "^4.17.20",
"mongodb": "^3.6.6"
"mongodb": "^4.0.1"
},
"scripts": {
"build": "rimraf ./dist && tsc",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { OmitProjection, parseResult } from './parse-result';
export { parseResult } from './parse-result';
export { Transaction, withTransaction } from './with-transaction';
export { throwIfNull } from './throw-if-null';
export { throwIfDuplicated } from './throw-if-duplicated';
45 changes: 13 additions & 32 deletions src/parse-result.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,26 @@
/* eslint-disable @typescript-eslint/ban-types */
import omit from 'lodash/omit';
import keys from 'lodash/keys';
import { DeleteWriteOpResultObject, FindAndModifyWriteOpResultObject, InsertOneWriteOpResult, InsertWriteOpResult, UpdateWriteOpResult, WithId } from 'mongodb';
import { DeleteResult, InsertManyResult, InsertOneResult, ModifyResult, UpdateResult, Document } from 'mongodb';

type OptionalKeys<T> = { [K in keyof T]: {} extends Pick<T, K> ? K : never }[keyof T];

export type OmitProjection<T> = { [K in OptionalKeys<T>]?: 0 };

export async function parseResult<T>(promise: Promise<InsertOneWriteOpResult<WithId<T>>>, projection?: OmitProjection<WithId<T>>): Promise<T>;
export async function parseResult<T>(promise: Promise<InsertWriteOpResult<WithId<T>>>, projection?: OmitProjection<WithId<T>>): Promise<T[]>;
export async function parseResult<T>(promise: Promise<FindAndModifyWriteOpResultObject<T>>): Promise<T | null>;
export async function parseResult(promise: Promise<UpdateWriteOpResult>): Promise<number>;
export async function parseResult(promise: Promise<DeleteWriteOpResultObject>): Promise<number>;
export async function parseResult<T extends { _id: unknown }>(promise: Promise<InsertOneResult<T>>): Promise<number>;
export async function parseResult<T extends { _id: unknown }>(promise: Promise<InsertManyResult<T>>): Promise<number>;
export async function parseResult<T>(promise: Promise<ModifyResult<T>>): Promise<T | null>;
export async function parseResult(promise: Promise<UpdateResult | Document>): Promise<number>;
export async function parseResult(promise: Promise<DeleteResult>): Promise<number>;

export async function parseResult<T>(
promise:
| Promise<InsertOneWriteOpResult<WithId<T>>>
| Promise<InsertWriteOpResult<WithId<T>>>
| Promise<FindAndModifyWriteOpResultObject<T>>
| Promise<UpdateWriteOpResult>
| Promise<DeleteWriteOpResultObject>,
projection?: OmitProjection<WithId<T>>,
): Promise<T | T[] | number | null> {
promise: Promise<InsertOneResult<T> | InsertManyResult<T> | ModifyResult<T> | Document | UpdateResult | DeleteResult>,
): Promise<number | T | null> {
const result = await promise;
if ('modifiedCount' in result) {
return result.modifiedCount;
}
if ('deletedCount' in result) {
return result.deletedCount;
}
if ('insertedId' in result) {
if (projection === undefined) {
return result.ops[0] as T;
}
return omit(result.ops[0], keys(projection)) as T;
return 1;
}
if ('insertedIds' in result) {
if (projection === undefined) {
return result.ops as T[];
}
return result.ops.map(op => omit(op, keys(projection)) as T);
}
if ('result' in result) {
return result.deletedCount as number;
return result.insertedCount;
}
return result.value ?? null;
}
47 changes: 9 additions & 38 deletions test/parse-result.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MongoClient, ObjectId, Collection } from 'mongodb';
import { MongoMemoryReplSet } from 'mongodb-memory-server';
import { parseResult, OmitProjection } from '../src';
import { parseResult } from '../src/parse-result';

interface Doc {
_id: ObjectId;
Expand All @@ -12,20 +12,16 @@ interface Doc {

const doc1: Doc = { _id: new ObjectId(), name: 'one', age: 25, friends: 1, tag: 'one' };
const doc2: Doc = { _id: new ObjectId(), name: 'two', age: 45, friends: 2, tag: 'two' };
const projection: OmitProjection<Doc> = { tag: 0, friends: 0 };

describe('parseResult()', () => {
let server: MongoMemoryReplSet;
let client: MongoClient;
let collection: Collection<Doc>;

beforeAll(async () => {
server = new MongoMemoryReplSet({ replSet: { storageEngine: 'wiredTiger' }, binary: { version: '4.4.5' } });
await server.waitUntilRunning();
const uri = await server.getUri();
client = await MongoClient.connect(uri, { useUnifiedTopology: true });
const dbName = await server.getDbName();
collection = client.db(dbName).collection('docs');
server = await MongoMemoryReplSet.create({ replSet: { storageEngine: 'wiredTiger' }, binary: { version: '4.4.5' } });
client = await MongoClient.connect(server.getUri());
collection = client.db(server.replSetOpts.dbName).collection('docs');
});

beforeEach(async () => {
Expand All @@ -38,43 +34,18 @@ describe('parseResult()', () => {
});

test('should parse the result of insertOne', async () => {
const inserted = await parseResult(collection.insertOne(doc1));
expect(inserted).toEqual(doc1);
});

test('should omit the projection keys on insertOne', async () => {
const inserted = await parseResult(collection.insertOne(doc1), projection);
expect(inserted).toEqual({
_id: doc1._id,
name: doc1.name,
age: doc1.age,
});
const insertedCount = await parseResult(collection.insertOne(doc1));
expect(insertedCount).toEqual(1);
});

test('should parse the result of insertMany', async () => {
const inserted = await parseResult(collection.insertMany([doc1, doc2]));
expect(inserted).toEqual([doc1, doc2]);
});

test('should omit the projection keys on insertMany', async () => {
const inserted = await parseResult(collection.insertMany([doc1, doc2]), projection);
expect(inserted).toEqual([
{
_id: doc1._id,
name: doc1.name,
age: doc1.age,
},
{
_id: doc2._id,
name: doc2.name,
age: doc2.age,
},
]);
const insertedCount = await parseResult(collection.insertMany([doc1, doc2]));
expect(insertedCount).toEqual(2);
});

test('should return the document when findOneAndUpdate finds it', async () => {
await collection.insertOne(doc1);
const updated = await parseResult(collection.findOneAndUpdate({ _id: doc1._id }, { $set: { name: 'updated' } }, { returnOriginal: false }));
const updated = await parseResult(collection.findOneAndUpdate({ _id: doc1._id }, { $set: { name: 'updated' } }, { returnDocument: 'after' }));
expect(updated).toEqual({ ...doc1, name: 'updated' });
});

Expand Down
2 changes: 1 addition & 1 deletion test/throw-if-duplicated.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MongoError } from 'mongodb';
import { throwIfDuplicated } from '../src';
import { throwIfDuplicated } from '../src/throw-if-duplicated';

describe('throwIfDuplicated()', () => {
const ifDuplicatedError = Error('value is duplicated');
Expand Down
2 changes: 1 addition & 1 deletion test/throw-if-null.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { throwIfNull } from '../src';
import { throwIfNull } from '../src/throw-if-null';

describe('throwIfNull()', () => {
const ifNullError = Error('value is null or undefined');
Expand Down
25 changes: 9 additions & 16 deletions test/with-transaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MongoClient, ObjectId, Collection } from 'mongodb';
import { MongoMemoryReplSet } from 'mongodb-memory-server';
import { withTransaction, Transaction } from '../src';
import { withTransaction, Transaction } from '../src/with-transaction';

interface Doc {
_id: ObjectId;
Expand All @@ -20,12 +20,9 @@ describe('withTransaction()', () => {
let collection: Collection<Doc>;

beforeAll(async () => {
server = new MongoMemoryReplSet({ replSet: { storageEngine: 'wiredTiger' }, binary: { version: '4.4.5' } });
await server.waitUntilRunning();
const uri = await server.getUri();
client = await MongoClient.connect(uri, { useUnifiedTopology: true });
const dbName = await server.getDbName();
collection = client.db(dbName).collection('docs');
server = await MongoMemoryReplSet.create({ replSet: { storageEngine: 'wiredTiger' }, binary: { version: '4.4.5' } });
client = await MongoClient.connect(server.getUri());
collection = client.db(server.replSetOpts.dbName).collection('docs');
});

beforeEach(async () => {
Expand All @@ -38,22 +35,18 @@ describe('withTransaction()', () => {
});

test('should commit a transaction', async () => {
const transaction: Transaction<Doc> = async session => {
await withTransaction(client, async session => {
await collection.insertOne(doc1, { session });
const result = await collection.insertOne(doc2, { session });
return result.ops[0];
};
const inserted = await withTransaction(client, transaction);
expect(inserted.name).toBe(doc2.name);
await collection.insertOne(doc2, { session });
});
const found = await collection.find().toArray();
expect(found).toHaveLength(2);
});

test('should abort a transaction', async () => {
const transaction: Transaction<Doc> = async session => {
const transaction: Transaction<void> = async session => {
await collection.insertOne(doc1, { session });
const result = await collection.insertOne(duplicatedDoc, { session });
return result.ops[0];
await collection.insertOne(duplicatedDoc, { session });
};
await expect(withTransaction(client, transaction)).rejects.toMatchObject({ code: 11000 });
const found = await collection.find().toArray();
Expand Down

0 comments on commit 41439a1

Please sign in to comment.