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

Add support for custom field names and custom schema #109

Closed
Show file tree
Hide file tree
Changes from all 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
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,23 @@ PetSchema.plugin(mongoose_delete, { indexFields: ['deletedAt'] });

```

### Custom field names or schema type definition

```javascript
var mongoose_delete = require('mongoose-delete');

var PetSchema = new Schema({
name: String
});

// Add a custom name for each property, will create alias for the original name
PetSchema.plugin(mongoose_delete, { deletedBy: 'deleted_by', deletedAt: 'deleted_at' });

// Use custom schema type definition by supplying an object
PetSchema.plugin(mongoose_delete, { deletedBy: { name: 'deleted_by', default: 'None', type: String }, deletedAt: { alias: 'deletedTimestamp' } });
```
Expects a Mongoose [Schema Types](https://mongoosejs.com/docs/schematypes.html#schematype-options) object with the added option of `name`.

## License

The MIT License
Expand Down
76 changes: 55 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,34 @@ function createSchemaObject (typeKey, typeValue, options) {
return options;
}

function deletedFieldName(deletedValue, fallbackName) {
if (typeof deletedValue === 'string') {
return deletedValue;
} else if (typeof deletedValue === 'object' && deletedValue.name) {
return deletedValue.name;
} else if (deletedValue === true || typeof deletedValue === 'object') {
return fallbackName;
}
}

function addDeletedFieldIfExist(schema, deletedOption, deletedFieldOriginalName, typeKey, type, index) {
const name = deletedFieldName(deletedOption, deletedFieldOriginalName);
if (name) {
const schemaOptions = {
[typeKey]: type,
index
};
if (name !== deletedFieldOriginalName) {
schemaOptions.alias = deletedFieldOriginalName;
}
if (typeof deletedOption === 'object') {
const { name, ...options } = deletedOption;
Object.assign(schemaOptions, options);
}
schema.add({ [name]: schemaOptions });
}
}

module.exports = function (schema, options) {
options = options || {};
var indexFields = parseIndexFields(options);
Expand All @@ -84,6 +112,9 @@ module.exports = function (schema, options) {
var mainUpdateMethod = mongooseMajorVersion < 5 ? 'update' : 'updateMany';
var mainUpdateWithDeletedMethod = mainUpdateMethod + 'WithDeleted';

const deletedAtField = deletedFieldName(options.deletedAt, 'deletedAt');
const deletedByField = deletedFieldName(options.deletedBy, 'deletedBy');

function updateDocumentsByQuery(schema, conditions, updateQuery, callback) {
if (schema[mainUpdateWithDeletedMethod]) {
return schema[mainUpdateWithDeletedMethod](conditions, updateQuery, { multi: true }, callback);
Expand All @@ -93,14 +124,8 @@ module.exports = function (schema, options) {
}

schema.add({ deleted: createSchemaObject(typeKey, Boolean, { default: false, index: indexFields.deleted }) });

if (options.deletedAt === true) {
schema.add({ deletedAt: createSchemaObject(typeKey, Date, { index: indexFields.deletedAt }) });
}

if (options.deletedBy === true) {
schema.add({ deletedBy: createSchemaObject(typeKey, options.deletedByType || Schema.Types.ObjectId, { index: indexFields.deletedBy }) });
}
addDeletedFieldIfExist(schema, options.deletedAt, 'deletedAt', typeKey, Date, indexFields.deletedAt);
addDeletedFieldIfExist(schema, options.deletedBy, 'deletedBy', typeKey, options.deletedByType || Schema.Types.ObjectId, indexFields.deletedBy);

var use$neOperator = true;
if (options.use$neOperator !== undefined && typeof options.use$neOperator === "boolean") {
Expand Down Expand Up @@ -238,12 +263,12 @@ module.exports = function (schema, options) {

this.deleted = true;

if (schema.path('deletedAt')) {
this.deletedAt = new Date();
if (deletedAtField && schema.path(deletedAtField)) {
this[deletedAtField] = new Date();
}

if (schema.path('deletedBy')) {
this.deletedBy = deletedBy;
if (deletedByField && schema.path(deletedByField)) {
this[deletedByField] = deletedBy;
}

if (options.validateBeforeDelete === false) {
Expand All @@ -268,12 +293,12 @@ module.exports = function (schema, options) {
deleted: true
};

if (schema.path('deletedAt')) {
doc.deletedAt = new Date();
if (deletedAtField && schema.path(deletedAtField)) {
doc[deletedAtField] = new Date();
}

if (schema.path('deletedBy')) {
doc.deletedBy = deletedBy;
if (deletedByField && schema.path(deletedByField)) {
doc[deletedByField] = deletedBy;
}

return updateDocumentsByQuery(this, conditions, doc, callback);
Expand All @@ -294,8 +319,12 @@ module.exports = function (schema, options) {

schema.methods.restore = function (callback) {
this.deleted = false;
this.deletedAt = undefined;
this.deletedBy = undefined;
if (deletedAtField) {
this[deletedAtField] = undefined;
}
if (deletedByField) {
this[deletedByField] = undefined;
}
return this.save(callback);
};

Expand All @@ -306,11 +335,16 @@ module.exports = function (schema, options) {
}

var doc = {
deleted: false,
deletedAt: undefined,
deletedBy: undefined
deleted: false
};

if (deletedAtField) {
doc[deletedAtField] = undefined;
}
if (deletedByField) {
doc[deletedByField] = undefined;
}

return updateDocumentsByQuery(this, conditions, doc, callback);
};
};
151 changes: 151 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,55 @@ describe("mongoose_delete with options: { deletedAt : true }, using option: type
});
});

describe("mongoose_delete with options: { deletedAt : 'deleted_at' }, using custom field name", function () {
const Test2Schema = new Schema({name: String}, {collection: 'mongoose_delete_test2b'});
Test2Schema.plugin(mongoose_delete, {deletedAt: 'deleted_at'});
const Test2 = mongoose.model('Test2b', Test2Schema);
const puffy1 = new Test2({name: 'Puffy1'});
const puffy2 = new Test2({name: 'Puffy2'});

before(async function () {
await puffy1.save();
await puffy2.save()
});

after(async function () {
await mongoose.connection.db.dropCollection("mongoose_delete_test3b");
});

it("delete() -> should save 'deletedAt' key", async function () {
const puffy = await Test2.findOne({name: 'Puffy1'});
const success = await puffy.delete();

expect(success).to.have.property('deleted').that.equal(true);
expect(success).to.have.property('deleted_at').that.is.an('date')
expect(success.deletedAt).that.is.an('date')
});

it("deleteById() -> should save `deletedAt` key", async function () {
const documents = await Test2.deleteById(puffy2._id);

expect(documents).to.be.mongoose_ok();
expect(documents).to.be.mongoose_count(1);

const doc = await Test2.findOne({name: 'Puffy2'});

expect(doc).to.have.property('deleted').that.equal(true);
expect(doc).to.have.property('deleted_at').that.is.an('date')
expect(doc.deletedAt).that.is.an('date')
});

it("restore() -> should set deleted:false and delete `deletedAt` key", async function () {
const puffy = await Test2.findOne({name: 'Puffy1'})

const success = await puffy.restore();

expect(success).to.have.property('deleted').that.equal(false);
expect(success).to.have.property('deleted_at').that.not.exist;
expect(success.deletedAt).that.not.exist;
});
});

describe("mongoose_delete with options: { deletedBy : true }", function () {

var Test3Schema = new Schema({name: String}, {collection: 'mongoose_delete_test3'});
Expand Down Expand Up @@ -587,6 +636,108 @@ describe("mongoose_delete with options: { deletedBy : true, deletedByType: Strin
});
});

describe("mongoose_delete with options: { deletedBy : 'deleted_by' }, using custom field name", function () {

var Test3Schema = new Schema({name: String}, {collection: 'mongoose_delete_test3b'});
Test3Schema.plugin(mongoose_delete, {deletedBy: 'deleted_by'});
var Test3 = mongoose.model('Test3b', Test3Schema);
var puffy1 = new Test3({name: 'Puffy1'});
var puffy2 = new Test3({name: 'Puffy2'});

before(async function () {
await puffy1.save();
await puffy2.save()
});

after(async function () {
await mongoose.connection.db.dropCollection("mongoose_delete_test3b");
});

var id = mongoose.Types.ObjectId("53da93b16b4a6670076b16bf");

it("delete() -> should save 'deletedBy' key", async function () {
const puffy = await Test3.findOne({name: 'Puffy1'});
const success = await puffy.delete(id);

expect(success).to.have.property('deleted').that.equal(true);
expect(success).to.have.property('deleted_by').that.deep.equal(id);
expect(success.deletedBy).that.deep.equal(id);
});

it("deleteById() -> should save `deletedBy` key", async function () {
const documents = await Test3.deleteById(puffy2._id, id);

expect(documents).to.be.mongoose_ok();
expect(documents).to.be.mongoose_count(1);

const doc = await Test3.findOne({name: 'Puffy2'});

expect(doc).to.have.property('deleted').that.equal(true);
expect(doc).to.have.property('deleted_by').that.deep.equal(id);
expect(doc.deletedBy).that.deep.equal(id);
});

it("restore() -> should set deleted:false and delete `deletedBy` key", async function () {
const puffy = await Test3.findOne({name: 'Puffy1'})

const success = await puffy.restore();

expect(success).to.have.property('deleted').that.equal(false);
expect(success).to.have.property('deleted_by').that.not.exist;
expect(success.deletedBy).that.not.exist;
});
});

describe("mongoose_delete with options: { deletedBy : { ... }, using custom schema", function () {

var Test3Schema = new Schema({name: String}, {collection: 'mongoose_delete_test3c'});
Test3Schema.plugin(mongoose_delete, {deletedBy: { name: 'deleted_by', get: (id) => { return id && { id }; }, type: String }});
var Test3 = mongoose.model('Test3c', Test3Schema);
var puffy1 = new Test3({name: 'Puffy1'});
var puffy2 = new Test3({name: 'Puffy2'});

before(async function () {
await puffy1.save();
await puffy2.save()
});

after(async function () {
await mongoose.connection.db.dropCollection("mongoose_delete_test3c");
});

var id = '53da93b16b4a6670076b16bf';

it("delete() -> should save 'deletedBy' key", async function () {
const puffy = await Test3.findOne({name: 'Puffy1'});
const success = await puffy.delete(id);

expect(success).to.have.property('deleted').that.equal(true);
expect(success.deletedBy).that.deep.equal({ id });
});

it("deleteById() -> should save `deletedBy` key", async function () {
const documents = await Test3.deleteById(puffy2._id, id);

expect(documents).to.be.mongoose_ok();
expect(documents).to.be.mongoose_count(1);

const doc = await Test3.findOne({name: 'Puffy2'});

expect(doc).to.have.property('deleted').that.equal(true);
expect(doc.deletedBy).that.deep.equal({ id });
});

it("restore() -> should set deleted:false and delete `deletedBy` key", async function () {
const puffy = await Test3.findOne({name: 'Puffy1'})

const success = await puffy.restore();

expect(success).to.have.property('deleted').that.equal(false);
expect(success).to.have.property('deletedBy').that.not.exist;
expect(success.deletedBy).that.not.exist;
});
});

describe("check not overridden static methods", function () {
var TestSchema = new Schema({name: String}, {collection: 'mongoose_delete_test'});
TestSchema.plugin(mongoose_delete);
Expand Down