Skip to content

Commit

Permalink
feat: implement $isPersisted, $isNew and $isLocal model properties
Browse files Browse the repository at this point in the history
Closes: #107
Refs: #72
  • Loading branch information
targos committed Sep 16, 2021
1 parent 68d9f84 commit a066a08
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 13 deletions.
22 changes: 22 additions & 0 deletions adonis-typings/odm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,30 @@ declare module '@ioc:Zakodium/Mongodb/Odm' {
readonly createdAt: Date;
readonly updatedAt: Date;

/**
* `true` if the entry has been persisted to the database.
*/
readonly $isPersisted: boolean;

/**
* Opposite of `$isPersisted`.
*/
readonly $isNew: boolean;

/**
* `true` if the entry has been created locally. Similar to `$isNew`, but
* stays `true` after the entry is persisted to the database.
*/
readonly $isLocal: boolean;

/**
* `true` if the entry has been removed from the database.
*/
readonly $isDeleted: boolean;

/**
* `true` if the entry has been changed since it was loaded from the database.
*/
readonly $dirty: Partial<ModelAttributes<this>>;

/**
Expand Down
36 changes: 23 additions & 13 deletions src/Model/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,16 @@ export class BaseModel {
public readonly createdAt: Date;
public readonly updatedAt: Date;

public $isDeleted: boolean;
public $isPersisted = false;
public $isLocal = true;
public $isDeleted = false;

protected $collection: Collection<
ModelAttributes<MongodbDocument<unknown>>
> | null = null;
protected $originalData: Record<string, unknown>;
protected $currentData: Record<string, unknown>;
protected $options: InternalModelConstructorOptions;
protected $alreadySaved: boolean;

public constructor(
dbObj?: Record<string, unknown>,
Expand All @@ -201,8 +202,11 @@ export class BaseModel {
this.$collection = options.collection;
}

this.$alreadySaved = alreadyExists;
this.$isDeleted = false;
if (alreadyExists) {
this.$isPersisted = true;
this.$isLocal = false;
}

// eslint-disable-next-line no-constructor-return
return new Proxy(this, proxyHandler);
}
Expand Down Expand Up @@ -457,6 +461,10 @@ export class BaseModel {
};
}

public get $isNew(): boolean {
return !this.$isPersisted;
}

public get $dirty(): Partial<ModelAttributes<this>> {
return pickBy(this.$currentData, (value, key) => {
return (
Expand Down Expand Up @@ -490,7 +498,7 @@ export class BaseModel {
public $prepareToSet() {
const dirty = this.$dirty;
const dirtyEntries = Object.entries(dirty);
if (dirtyEntries.length === 0) {
if (dirtyEntries.length === 0 && this.$isPersisted) {
return null;
}

Expand Down Expand Up @@ -536,9 +544,10 @@ export class BaseModel {
...options?.driverOptions,
session: this.$options?.session,
};
if (this.$alreadySaved === false) {
if (!this.$isPersisted) {
const result = await collection.insertOne(toSet, driverOptions);
this.$currentData._id = result.insertedId;
this.$isPersisted = true;
} else {
await collection.updateOne(
{ _id: this.$currentData._id },
Expand All @@ -547,7 +556,6 @@ export class BaseModel {
);
}
this.$originalData = cloneDeep(this.$currentData);
this.$alreadySaved = true;
return true;
}

Expand Down Expand Up @@ -602,7 +610,6 @@ export class BaseAutoIncrementModel extends BaseModel {

const toSet = this.$prepareToSet();
if (toSet === null) return false;

const driverOptions = {
...options?.driverOptions,
session: this.$options?.session,
Expand All @@ -615,14 +622,17 @@ export class BaseAutoIncrementModel extends BaseModel {
);

const doc = await counterCollection.findOneAndUpdate(
{ _id: computeCollectionName(this.constructor.name) },
{ _id: (this.constructor as typeof BaseModel).collectionName },
{ $inc: { count: 1 } },
{ ...driverOptions, upsert: true },
{ ...driverOptions, upsert: true, returnDocument: 'after' },
);
const newCount = doc.value ? doc.value.count + 1 : 1;
toSet._id = newCount;
if (!doc.value) {
throw new Error('upsert should always create a document');
}
toSet._id = doc.value.count;
await collection.insertOne(toSet, driverOptions);
this.$currentData._id = newCount;
this.$currentData._id = doc.value.count;
this.$isPersisted = true;
} else {
await collection.updateOne(
{ _id: this.$currentData._id },
Expand Down
25 changes: 25 additions & 0 deletions src/Model/__tests__/Model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class Post extends BaseAutoIncrementModel {
public content: string;
}

class Empty extends BaseAutoIncrementModel {}
Empty.boot();

class Something extends BaseModel {
public static collectionName = 'somethingElse';

Expand Down Expand Up @@ -126,6 +129,28 @@ test('delete on model', async () => {
expect(sameUserButDeleted).toBeNull();
});

test('persistence boolean properties should behave correctly with new instances', async () => {
const user = new User();
user.username = nextUsername();
user.password = 'root';
expect(user.$isPersisted).toBe(false);
expect(user.$isNew).toBe(true);
expect(user.$isLocal).toBe(true);
await user.save();
expect(user.$isPersisted).toBe(true);
expect(user.$isNew).toBe(false);
expect(user.$isLocal).toBe(true);
});

test('create an empty document', async () => {
const empty = await Empty.create({});
expect(empty).toBeDefined();
expect(empty.$isNew).toBe(false);
expect(empty.$isPersisted).toBe(true);
expect(empty.$isLocal).toBe(true);
expect(empty.id).toBe(1);
});

test('id is a number on AutoIncrementModel', async () => {
const firstPost = await Post.create({
title: nextTitle(),
Expand Down

0 comments on commit a066a08

Please sign in to comment.