Skip to content
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

Hotfix/key thrown #110

Merged
merged 8 commits into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
Changes from 6 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
29 changes: 27 additions & 2 deletions src/file/file.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,39 @@ fileSchema.virtual('fullExtension')
});

// handleE11000 is called when there is a duplicateKey Error
const handleE11000 = function (error: MongoError, _: any, next: NextFunction) {
const handleE11000 = function (error: MongoError, _: any, next: NextFunction) : void {
if (error.name === 'MongoError' && error.code === 11000) {
next(new KeyAlreadyExistsError(this.key));
const retMessage : string = getMongoErrorIndices(error);
next(new KeyAlreadyExistsError(retMessage));
Copy link
Contributor

@yonatandt yonatandt Jul 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should create a different error for cases where the compound indexes already exist. We can accidentally confuse it with the key field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to 'UniqueIndexExistsError'

} else {
next();
}
};

/**
* Extracts the indices names and values thrown by the duplicate key error.
* @param error - the mongo error thrown.
* @return string with the index names and values.
*/
function getMongoErrorIndices(error: MongoError) : string {
// extract the indices names in the MongoError
const indicesRegex : RegExp = new RegExp(/index\:\ (?:.*\.)?\$?(?:([_a-z0-9]*)(?:_\d*)|([_a-z0-9]*))\s*dup key/i);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added documentation

const indicesMatch : RegExpMatchArray = error.message.match(indicesRegex);
let indexName : string = indicesMatch[1] || indicesMatch[2];

// prettify indices names
const re : RegExp = new RegExp('_1_', 'g');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added documentation

indexName = indexName.replace(re, ', ');

// extract the indices values of the error thrown
const valuesRE : RegExp = new RegExp(/{(.*?)}/);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added documentation

const valuesMatch : RegExpMatchArray = error.message.match(valuesRE);
let values : string = valuesMatch[0];
values = values.replace(new RegExp(' : ', 'g'), ' ');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WTF is this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added documentation


return `${indexName} : ${values}`;
}

fileSchema.post('save', handleE11000);
fileSchema.post('update', handleE11000);
fileSchema.post('findOneAndUpdate', handleE11000);
Expand Down
2 changes: 1 addition & 1 deletion src/file/file.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class FileRepository {
}

/**
* Changes 'deleted' flag to true. Does not delete from th DB.
* Deletes a file from the DB.
* @param id - the id of the file to be deleted.
*/
static deleteById(id: string): Promise<IFile | null> {
Expand Down
25 changes: 24 additions & 1 deletion src/file/file.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,14 +357,37 @@ describe('File Logic', () => {
expect(file2.parent).to.equal(file1.parent);
});

it('should throw an error when KEY already exist', async () => {
it('should throw an error when KEY already exists', async () => {
await FileService.create(bucket, 'tmp1', USER.id, 'text', null, KEY);
await FileService.create(bucket, 'tmp2', USER.id, 'text', null, KEY)
.should.eventually.be.rejectedWith(KeyAlreadyExistsError);
});

});

describe('#updateById', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does updateById tests related to this issue/hotfix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created tests that were missing and are related to this issue

it('should update a file', async () => {
const file: IFile = await FileService.create(bucket, 'file.txt', USER.id, 'text', KEY2, KEY, size);
await FileService.updateById(file.id, { name: 'changedFile', type: 'jpg' });
const changedFile : IFile = await FileService.getById(file.id);
expect(changedFile.name).to.equal('changedFile');
expect(changedFile.type).to.equal('jpg');
});

it('should throw an error when changing a file to unique properties of another (trinity)', async () => {
const file1: IFile = await FileService.create(bucket, 'file1.txt', USER.id, 'text', null, KEY);
const file2: IFile = await FileService.create(bucket, 'file2.txt', USER.id, 'text', null, KEY2);
await FileService.updateById(file1.id, { name: 'file2.txt' }).should.eventually.be.rejectedWith(KeyAlreadyExistsError);
});

it('should throw an error when changing a file to unique properties of another (key)', async () => {
const file1: IFile = await FileService.create(bucket, 'file1.txt', USER.id, 'text', null, KEY);
const file2: IFile = await FileService.create(bucket, 'file2.txt', USER.id, 'text', null, KEY2);
await FileService.updateById(file1.id, { key: KEY2 }).should.eventually.be.rejectedWith(KeyAlreadyExistsError);
});

});

describe('#getByID', () => {
it('should get an error when file does not exist', async () => {
await FileService.getByKey(KEY).should.eventually.be.rejectedWith(FileNotFoundError);
Expand Down
1 change: 0 additions & 1 deletion src/file/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export class FileService {
ownerID,
size,
_id: id,
deleted: false,
parent: parentID,
});

Expand Down
2 changes: 1 addition & 1 deletion src/utils/errors/client.error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class MailInvalidError extends ClientError {

export class KeyAlreadyExistsError extends ClientError {
constructor(key:string, message?: string) {
super(message || `key: ${key} is already in use`, grpc.status.INVALID_ARGUMENT);
super(message || `unique key '${key}' is already in use`, grpc.status.INVALID_ARGUMENT);
}
}

Expand Down