Skip to content

Commit

Permalink
feat: support for BigInt field types (#21)
Browse files Browse the repository at this point in the history
* Support for BigInt field types

* refactor: switch from instanceof to field.instance for type checks to support BigInts

* test: added BigInt tests

* refactor: allow incrementBy and startAt to be BigInt, change count to BigInt and cast

* test: added Simple tests and update snapshots

* refactor: move incrementBy and startAt BigInt conversions to pre save callback

* fix: type AutoIncrementIDTrackerSpec count is now bigint
  • Loading branch information
jonbarrow authored and hasezoey committed Sep 1, 2023
1 parent 5bd009d commit 24add0b
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 26 deletions.
22 changes: 16 additions & 6 deletions src/autoIncrement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,18 @@ export function AutoIncrementSimple(
throw new Error(`Field "${field.field}" does not exists on the Schema!`);
}
// check if the field is an number
if (!(schemaField instanceof mongoose.Schema.Types.Number)) {
throw new Error(`Field "${field.field}" is not an SchemaNumber!`);
if (schemaField.instance !== 'Number' && schemaField.instance !== 'BigInt') {
throw new Error(`Field "${field.field}" is not a Number or BigInt!`);
}

if (isNullOrUndefined(field.incrementBy)) {
logger.info('Field "%s" does not have an incrementBy defined, defaulting to %d', field.field, DEFAULT_INCREMENT);
field.incrementBy = DEFAULT_INCREMENT;

if (schemaField.instance === 'BigInt') {
field.incrementBy = BigInt(DEFAULT_INCREMENT);
} else {
field.incrementBy = DEFAULT_INCREMENT;
}
}
}
// to have an name to the function if debugging
Expand All @@ -65,7 +70,7 @@ const IDSchema = new mongoose.Schema<AutoIncrementIDTrackerSpec>(
{
field: String,
modelName: String,
count: Number,
count: BigInt,
},
{ versionKey: false }
);
Expand All @@ -92,8 +97,10 @@ export function AutoIncrementID(schema: mongoose.Schema<any>, options: AutoIncre
};

// check if the field is an number
if (!(schema.path(opt.field) instanceof mongoose.Schema.Types.Number)) {
throw new Error(`Field "${opt.field}" is not an SchemaNumber!`);
const schemaField = schema.path(opt.field);

if (schemaField.instance !== 'Number' && schemaField.instance !== 'BigInt') {
throw new Error(`Field "${opt.field}" is not a Number or BigInt!`);
}

let model: mongoose.Model<AutoIncrementIDTrackerSpec>;
Expand All @@ -103,6 +110,9 @@ export function AutoIncrementID(schema: mongoose.Schema<any>, options: AutoIncre
schema.pre('save', async function AutoIncrementPreSaveID(): Promise<void> {
logger.info('AutoIncrementID PreSave');

opt.incrementBy = BigInt(opt.incrementBy);
opt.startAt = BigInt(opt.startAt);

const originalModelName: string = (this.constructor as any).modelName;
let modelName: string;

Expand Down
8 changes: 4 additions & 4 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface AutoIncrementOptionsSimple {
* How much to increment the field by
* @default 1
*/
incrementBy?: number;
incrementBy?: number | bigint;
}

export type AutoIncrementSimplePluginOptions = AutoIncrementOptionsSimple | AutoIncrementOptionsSimple[];
Expand All @@ -17,7 +17,7 @@ export interface AutoIncrementIDOptions {
* How much to increment the field by
* @default 1
*/
incrementBy?: number;
incrementBy?: number | bigint;
/**
* Set the field to increment
* -> Only use this if it is not "_id"
Expand All @@ -38,7 +38,7 @@ export interface AutoIncrementIDOptions {
* the count should start at
* @default 0
*/
startAt?: number;
startAt?: number | bigint;
/**
* Overwrite what to use for the `modelName` property in the tracker document
* This can be overwritten when wanting to use a single tracker for multiple models
Expand All @@ -58,5 +58,5 @@ export interface AutoIncrementIDTrackerSpec {
/** The field in the schema */
field: string;
/** Current Tracker count */
count: number;
count: bigint;
}
2 changes: 1 addition & 1 deletion test/__snapshots__/basic.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ exports[`Basic Suite AutoIncrementID should throw a error if "overwriteModelName

exports[`Errors should Error if the schema path does not exist 1`] = `"Field \\"SomeNonExistingField\\" does not exists on the Schema!"`;

exports[`Errors should Error if the schema path is not an number 1`] = `"Field \\"nonNumberField\\" is not an SchemaNumber!"`;
exports[`Errors should Error if the schema path is not an number 1`] = `"Field \\"nonNumberField\\" is not a Number or BigInt!"`;
162 changes: 147 additions & 15 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { AutoIncrementID, AutoIncrementSimple } from '../src/autoIncrement';

describe('Basic Suite', () => {
describe('AutoIncrementSimple', () => {
it('Basic Function Mongoose', async () => {
it('Basic Function Mongoose (Number)', async () => {
const schema = new mongoose.Schema({
somefield: Number,
});
schema.plugin(AutoIncrementSimple, [{ field: 'somefield' }]);
const model = mongoose.model('AutoIncrementSimple-SomeModel', schema);
const model = mongoose.model('AutoIncrementSimple-SomeModel-Number', schema);

const doc = await model.create({ somefield: 10 });
expect(doc.somefield).toBe(10);
Expand All @@ -19,9 +19,9 @@ describe('Basic Suite', () => {
expect(doc.somefield).toBe(11);
});

it('Basic Function Typegoose', async () => {
it('Basic Function Typegoose (Number)', async () => {
@plugin(AutoIncrementSimple, [{ field: 'someIncrementedField' }])
@modelOptions({ options: { customName: 'AutoIncrementSimple-SomeClass' } })
@modelOptions({ options: { customName: 'AutoIncrementSimple-SomeClass-Number' } })
class SomeClass {
@prop({ required: true })
public someIncrementedField: number;
Expand All @@ -35,16 +35,47 @@ describe('Basic Suite', () => {
await doc.save();
expect(doc.someIncrementedField).toBe(11);
});

it('Basic Function Mongoose (BigInt)', async () => {
const schema = new mongoose.Schema({
somefield: BigInt,
});
schema.plugin(AutoIncrementSimple, [{ field: 'somefield' }]);
const model = mongoose.model('AutoIncrementSimple-SomeModel-BigInt', schema);

const doc = await model.create({ somefield: 10n });
expect(doc.somefield).toBe(10n);

await doc.save();
expect(doc.somefield).toBe(11n);
});

it('Basic Function Typegoose (BigInt)', async () => {
@plugin(AutoIncrementSimple, [{ field: 'someIncrementedField' }])
@modelOptions({ options: { customName: 'AutoIncrementSimple-SomeClass-BigInt' } })
class SomeClass {
@prop({ required: true })
public someIncrementedField: bigint;
}

const SomeModel = getModelForClass(SomeClass);

const doc = await SomeModel.create({ someIncrementedField: 10n });
expect(doc.someIncrementedField).toBe(10n);

await doc.save();
expect(doc.someIncrementedField).toBe(11n);
});
});

describe('AutoIncrementID', () => {
it('Basic Function Mongoose', async () => {
it('Basic Function Mongoose (Number)', async () => {
const schema = new mongoose.Schema({
_id: Number,
somefield: Number,
});
schema.plugin(AutoIncrementID, {});
const model = mongoose.model('AutoIncrementID-SomeModel', schema);
const model = mongoose.model('AutoIncrementID-SomeModel-Number', schema);

// test initial 0
{
Expand Down Expand Up @@ -82,13 +113,13 @@ describe('Basic Suite', () => {
const trackerModel = mongoose.connection.models['identitycounter'];
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-SomeModel' }).orFail();
expect(foundTracker.count).toEqual(2);
const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-SomeModel-Number' }).orFail();
expect(foundTracker.count).toEqual(2n);
});

it('Basic Function Typegoose', async () => {
it('Basic Function Typegoose (Number)', async () => {
@plugin(AutoIncrementID, {})
@modelOptions({ options: { customName: 'AutoIncrementID-SomeClass' } })
@modelOptions({ options: { customName: 'AutoIncrementID-SomeClass-Number' } })
class SomeClass {
@prop()
public _id?: number;
Expand Down Expand Up @@ -135,8 +166,109 @@ describe('Basic Suite', () => {
const trackerModel = mongoose.connection.models['identitycounter'];
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-SomeClass' }).orFail();
expect(foundTracker.count).toEqual(2);
const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-SomeClass-Number' }).orFail();
expect(foundTracker.count).toEqual(2n);
});

it('Basic Function Mongoose (BigInt)', async () => {
const schema = new mongoose.Schema({
_id: BigInt,
somefield: Number,
});
schema.plugin(AutoIncrementID, {});
const model = mongoose.model('AutoIncrementID-SomeModel-BigInt', schema);

// test initial 0
{
const doc = await model.create({ somefield: 10 });
expect(doc.somefield).toBe(10);
expect(doc._id).toBe(0n);

await doc.save();
expect(doc.somefield).toBe(10);
expect(doc._id).toBe(0n);
}

// test add 1
{
const doc = await model.create({ somefield: 20 });
expect(doc.somefield).toBe(20);
expect(doc._id).toBe(1n);

await doc.save();
expect(doc.somefield).toBe(20);
expect(doc._id).toBe(1n);
}

// test add another 1
{
const doc = await model.create({ somefield: 30 });
expect(doc.somefield).toBe(30);
expect(doc._id).toBe(2n);

await doc.save();
expect(doc.somefield).toBe(30);
expect(doc._id).toBe(2n);
}

const trackerModel = mongoose.connection.models['identitycounter'];
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-SomeModel-BigInt' }).orFail();
expect(foundTracker.count).toEqual(2n);
});

it('Basic Function Typegoose (BigInt)', async () => {
@plugin(AutoIncrementID, {})
@modelOptions({ options: { customName: 'AutoIncrementID-SomeClass-BigInt' } })
class SomeClass {
@prop()
public _id?: bigint;

@prop({ required: true })
public someIncrementedField!: number;
}

const SomeModel = getModelForClass(SomeClass);

// test initial 0
{
const doc = await SomeModel.create({ someIncrementedField: 10 });
expect(doc.someIncrementedField).toBe(10);
expect(doc._id).toBe(0n);

await doc.save();
expect(doc.someIncrementedField).toBe(10);
expect(doc._id).toBe(0n);
}

// test add 1
{
const doc = await SomeModel.create({ someIncrementedField: 20 });
expect(doc.someIncrementedField).toBe(20);
expect(doc._id).toBe(1n);

await doc.save();
expect(doc.someIncrementedField).toBe(20);
expect(doc._id).toBe(1n);
}

// test add another 1
{
const doc = await SomeModel.create({ someIncrementedField: 30 });
expect(doc.someIncrementedField).toBe(30);
expect(doc._id).toBe(2n);

await doc.save();
expect(doc.someIncrementedField).toBe(30);
expect(doc._id).toBe(2n);
}

const trackerModel = mongoose.connection.models['identitycounter'];
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-SomeClass-BigInt' }).orFail();
expect(foundTracker.count).toEqual(2n);
});

it('Basic Function Mongoose With startAt', async () => {
Expand Down Expand Up @@ -263,7 +395,7 @@ describe('Basic Suite', () => {
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'TestOverwrite' }).orFail();
expect(foundTracker.count).toEqual(2);
expect(foundTracker.count).toEqual(2n);
});

it('should use modelName if "overwriteModelName" is falsy', async () => {
Expand All @@ -286,7 +418,7 @@ describe('Basic Suite', () => {
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-EOMN' }).orFail();
expect(foundTracker.count).toEqual(0);
expect(foundTracker.count).toEqual(0n);
});

it('should support "overwriteModelName" being a function', async () => {
Expand Down Expand Up @@ -323,7 +455,7 @@ describe('Basic Suite', () => {
expect(Object.getPrototypeOf(trackerModel)).toStrictEqual(mongoose.Model);

const foundTracker = await trackerModel.findOne({ modelName: 'AutoIncrementID-OMNF-test' }).orFail();
expect(foundTracker.count).toEqual(0);
expect(foundTracker.count).toEqual(0n);
});

it('should throw a error if "overwriteModelName" is a function but returns a empty string', async () => {
Expand Down

0 comments on commit 24add0b

Please sign in to comment.