Skip to content

Commit

Permalink
fix(adapters): update lowdb to v7
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Oct 6, 2024
1 parent 99ec03a commit 163e68e
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 88 deletions.
2 changes: 1 addition & 1 deletion packages/orm/adapters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"change-case": "^5.4.4",
"fs-extra": "11.2.0",
"lodash": "^4.17.21",
"lowdb": "1.0.0",
"lowdb": "7.0.1",
"tslib": "2.7.0",
"uuid": "^10.0.0"
},
Expand Down
24 changes: 11 additions & 13 deletions packages/orm/adapters/src/adapters/FileSyncAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {nameOf} from "@tsed/core";
import {Configuration, Injectable, Opts, ProviderScope, Scope} from "@tsed/di";
import fs from "fs-extra";
import low from "lowdb";
import FileSync from "lowdb/adapters/FileSync.js";
import {LowSync} from "lowdb";
import {JSONFileSync} from "lowdb/node";
import {dirname} from "path";

import {AdapterConstructorOptions} from "../domain/Adapter.js";
import {AdapterModel, LowDbAdapter} from "./LowDbAdapter.js";
import {AdapterModel, LowDbAdapter, type LowModel} from "./LowDbAdapter.js";

export interface FileSyncAdapterConstructorOptions extends AdapterConstructorOptions {
readOnly: true;
Expand All @@ -20,19 +20,17 @@ export class FileSyncAdapter<T extends AdapterModel> extends LowDbAdapter<T> {

fs.ensureDirSync(dirname(this.dbFilePath));

const file = new FileSync<{collection: T[]}>(this.dbFilePath);
const file = new JSONFileSync<LowModel<T>>(this.dbFilePath);

this.db = low(file);
this.db
.defaults({
collectionName: this.collectionName,
modelName: nameOf(this.model),
collection: []
})
.write();
this.db = new LowSync<LowModel<T>>(file, {
collectionName: this.collectionName,
modelName: nameOf(this.model),
collection: []
});
this.db.write();

if (options.readOnly) {
file.write = () => {};
file.write = (() => {}) as any;
}
}
}
205 changes: 205 additions & 0 deletions packages/orm/adapters/src/adapters/LowDbAdapter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import {faker} from "@faker-js/faker";
import {PlatformTest} from "@tsed/common";
import {deserialize} from "@tsed/json-mapper";
import {Format, Name, Property} from "@tsed/schema";

import {Adapter, Adapters, MemoryAdapter} from "../../src/index.js";

class BaseClient {
@Format("date-time")
createdAt: Date;
}

class Client extends BaseClient {
@Name("id")
_id: string;

@Property()
name: string;
}

describe("MemoryAdapter", () => {
let adapter: Adapter<Client>;
beforeEach(() => PlatformTest.create());
afterEach(() => PlatformTest.reset());
beforeEach(() => {
adapter = PlatformTest.get<Adapters>(Adapters).invokeAdapter<any>({
collectionName: "clients",
model: Client,
adapter: MemoryAdapter
});
});

describe("create()", () => {
it("should create a new instance", async () => {
const base = deserialize(
{
name: faker.person.firstName(),
createdAt: faker.date.past()
},
{type: Client}
);

const client = await adapter.create(base);

expect(client).toBeInstanceOf(Client);
expect(typeof client._id).toBe("string");
expect(client.name).toBe(base.name);
expect(client.createdAt).toEqual(base.createdAt);
});

it("should create a new instance with expireAt", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.create(base, new Date());

expect(client).toBeInstanceOf(Client);
expect(typeof client._id).toBe("string");
expect(client.name).toBe(base.name);
});
});

describe("upsert()", () => {
it("should create a new instance if not exists", async () => {
const base: any = {
name: faker.person.firstName()
};

const client = await adapter.upsert(base._id, base);

expect(client).toBeInstanceOf(Client);
expect(typeof client._id).toBe("string");
expect(client.name).toBe(base.name);
});

it("should update instance if exists", async () => {
const base: any = {
name: faker.person.firstName()
};

const client = await adapter.upsert(base._id, base);
const client2 = await adapter.upsert(client._id, client);

expect(client2).toBeInstanceOf(Client);
expect(typeof client2._id).toBe("string");
expect(client2.name).toBe(base.name);
});
});

describe("updateOne()", () => {
it("should update an instance", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.create(base);

const update = {
_id: client._id,
name: faker.person.firstName()
};

const client2 = await adapter.updateOne({_id: client._id}, update);

expect(client2).toBeInstanceOf(Client);
expect(typeof client2?._id).toBe("string");
expect(client2?.name).not.toBe(base.name);
expect(client2?.name).toBe(update.name);
});
it("should return undefined", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.updateOne({_id: faker.string.uuid()}, base);

expect(client).toBeUndefined();
});
});

describe("findById()", () => {
it("should find by ID", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.create(base);
const result = await adapter.findById(client._id);

expect(result).toBeInstanceOf(Client);
expect(result?._id).toBe(client._id);
expect(result?.name).toBe(base.name);
});
});

describe("findOne()", () => {
it("should find one item", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.create(base);

const result = await adapter.findOne({
name: base.name
});

expect(result).toBeInstanceOf(Client);
expect(result?._id).toBe(client._id);
expect(result?.name).toBe(base.name);
});
});
describe("findAll()", () => {
it("should find all items", async () => {
const base = {
name: faker.person.firstName()
};

await adapter.create(base);

const result = await adapter.findAll({
name: base.name
});

expect(result[0]).toBeInstanceOf(Client);
expect(result[0].name).toBe(base.name);
});
});
describe("deleteById()", () => {
it("should delete an item by id", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.create(base);

const result = await adapter.deleteById(client._id);

expect(result).toBeInstanceOf(Client);
expect(result?.name).toBe(base.name);
});
});
describe("deleteMany()", () => {
it("should delete many", async () => {
const base = {
name: faker.person.firstName()
};

const client = await adapter.create(base);

await adapter.create({
name: faker.person.firstName()
});
await adapter.create({
name: faker.person.firstName()
});

const result = await adapter.deleteMany(client);

expect(result[0]).toBeInstanceOf(Client);
expect(result[0]?.name).toBe(base.name);
});
});
});
49 changes: 25 additions & 24 deletions packages/orm/adapters/src/adapters/LowDbAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {cleanObject} from "@tsed/core";
import _ from "lodash";
import isMatch from "lodash/isMatch.js";
import low from "lowdb";
import type {Low, LowSync} from "lowdb";
import {v4 as uuid} from "uuid";

import {Adapter} from "../domain/Adapter.js";
Expand All @@ -12,11 +13,17 @@ export interface AdapterModel {
[key: string]: any;
}

export interface LowModel<T> {
collection: T[];
collectionName?: string;
modelName?: string;
}

export class LowDbAdapter<T extends AdapterModel> extends Adapter<T> {
protected db: low.LowdbSync<{collection: T[]}>;
protected db: LowSync<LowModel<T>> | Low<LowModel<T>>;

get collection() {
return this.db.get("collection");
return this.db.data!.collection!;
}

protected get dbFilePath() {
Expand All @@ -33,7 +40,7 @@ export class LowDbAdapter<T extends AdapterModel> extends Adapter<T> {

await this.validate(payload as T);

await this.collection.push(this.serialize(payload) as T).write();
await this.db.update(({collection}) => collection.push(this.serialize(payload)));

return this.deserialize(payload);
}
Expand All @@ -49,9 +56,9 @@ export class LowDbAdapter<T extends AdapterModel> extends Adapter<T> {
const item = this.serialize(payload);
item.expires_at = expiresAt;

await this.collection.push(item).write();
await this.db.update(({collection}) => collection.push(item));

return this.deserialize(payload);
return this.deserialize(item);
}

return (await this.update(id, payload, expiresAt)) as T;
Expand All @@ -62,27 +69,26 @@ export class LowDbAdapter<T extends AdapterModel> extends Adapter<T> {
}

public async updateOne(predicate: Partial<T & any>, payload: T, expiresAt?: Date): Promise<T | undefined> {
let index = this.collection.findIndex(cleanObject(predicate)).value();
let index = _.findIndex(this.collection, cleanObject(predicate));

if (index === -1) {
return;
}

let item = this.deserialize(this.collection.get(index).value());
let item = this.deserialize(this.collection[index]);

Object.assign(item, payload, {_id: item._id});

await this.validate(item as T);

item.expires_at = expiresAt || item.expires_at;

await this.collection.set(index, item).write();
this.db.update(({collection}) => (collection[index] = item));

return this.deserialize(item);
}

findOne(predicate: Partial<T & any>): Promise<T | undefined> {
const item = this.collection.find(cleanObject(predicate)).value();
const item = _.find(this.collection, cleanObject(predicate));

return this.deserialize(item);
}
Expand All @@ -91,20 +97,15 @@ export class LowDbAdapter<T extends AdapterModel> extends Adapter<T> {
return this.findOne({_id});
}

public findAll(predicate: Partial<T & any> = {}): Promise<T[]> {
return Promise.resolve(
this.collection
.filter(cleanObject(predicate))
.value()
.map((item) => this.deserialize(item))
);
public async findAll(predicate: Partial<T & any> = {}): Promise<T[]> {
return _.filter(this.collection, cleanObject(predicate)).map((item) => this.deserialize(item));
}

public deleteOne(predicate: Partial<T & any>): Promise<T | undefined> {
const item = this.collection.find(cleanObject(predicate)).value();
const item = _.find<T>(this.collection, cleanObject(predicate));

if (item) {
this.collection.remove(({_id}) => _id === item._id).write();
_.remove(this.collection, ({_id}) => _id === item._id);

return Promise.resolve(this.deserialize(item));
}
Expand All @@ -119,15 +120,15 @@ export class LowDbAdapter<T extends AdapterModel> extends Adapter<T> {
public async deleteMany(predicate: Partial<T>): Promise<T[]> {
let removedItems: T[] = [];

await this.collection
.remove((item) => {
this.db.update((data) => {
_.remove(data.collection, (item) => {
if (isMatch(item, cleanObject(predicate))) {
removedItems.push(this.deserialize(item));
return true;
}
return false;
})
.write();
});
});

return removedItems;
}
Expand Down
Loading

0 comments on commit 163e68e

Please sign in to comment.