mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Merge pull request #277 from bknd-io/feat/timestamps-plugin
feat: timestamps plugin
This commit is contained in:
74
app/src/plugins/data/timestamp.plugin.spec.ts
Normal file
74
app/src/plugins/data/timestamp.plugin.spec.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
|
||||||
|
import { timestamps } from "./timestamps.plugin";
|
||||||
|
import { em, entity, text } from "bknd";
|
||||||
|
import { createApp } from "core/test/utils";
|
||||||
|
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
||||||
|
|
||||||
|
beforeAll(() => disableConsoleLog());
|
||||||
|
afterAll(enableConsoleLog);
|
||||||
|
|
||||||
|
describe("timestamps plugin", () => {
|
||||||
|
test("should ignore if no or invalid entities are provided", async () => {
|
||||||
|
const app = createApp({
|
||||||
|
options: {
|
||||||
|
plugins: [timestamps({ entities: [] })],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await app.build();
|
||||||
|
expect(app.em.entities.map((e) => e.name)).toEqual([]);
|
||||||
|
|
||||||
|
{
|
||||||
|
const app = createApp({
|
||||||
|
options: {
|
||||||
|
plugins: [timestamps({ entities: ["posts"] })],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await app.build();
|
||||||
|
expect(app.em.entities.map((e) => e.name)).toEqual([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should add timestamps to the specified entities", async () => {
|
||||||
|
const app = createApp({
|
||||||
|
config: {
|
||||||
|
data: em({
|
||||||
|
posts: entity("posts", {
|
||||||
|
title: text(),
|
||||||
|
}),
|
||||||
|
}).toJSON(),
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
plugins: [timestamps({ entities: ["posts", "invalid"] })],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await app.build();
|
||||||
|
expect(app.em.entities.map((e) => e.name)).toEqual(["posts"]);
|
||||||
|
expect(app.em.entity("posts")?.fields.map((f) => f.name)).toEqual([
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
]);
|
||||||
|
|
||||||
|
// insert
|
||||||
|
const mutator = app.em.mutator(app.em.entity("posts"));
|
||||||
|
const { data } = await mutator.insertOne({ title: "Hello" });
|
||||||
|
expect(data.created_at).toBeDefined();
|
||||||
|
expect(data.updated_at).toBeDefined();
|
||||||
|
expect(data.created_at).toBeInstanceOf(Date);
|
||||||
|
expect(data.updated_at).toBeInstanceOf(Date);
|
||||||
|
const diff = data.created_at.getTime() - data.updated_at.getTime();
|
||||||
|
expect(diff).toBeLessThan(10);
|
||||||
|
expect(diff).toBeGreaterThan(-1);
|
||||||
|
|
||||||
|
// update (set updated_at to null, otherwise it's too fast to test)
|
||||||
|
await app.em.connection.kysely
|
||||||
|
.updateTable("posts")
|
||||||
|
.set({ updated_at: null })
|
||||||
|
.where("id", "=", data.id)
|
||||||
|
.execute();
|
||||||
|
const { data: updatedData } = await mutator.updateOne(data.id, { title: "Hello 2" });
|
||||||
|
expect(updatedData.updated_at).toBeDefined();
|
||||||
|
expect(updatedData.updated_at).toBeInstanceOf(Date);
|
||||||
|
});
|
||||||
|
});
|
||||||
86
app/src/plugins/data/timestamps.plugin.ts
Normal file
86
app/src/plugins/data/timestamps.plugin.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { type App, type AppPlugin, em, entity, datetime, DatabaseEvents } from "bknd";
|
||||||
|
import { $console } from "bknd/utils";
|
||||||
|
|
||||||
|
export type TimestampsPluginOptions = {
|
||||||
|
entities: string[];
|
||||||
|
setUpdatedOnCreate?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This plugin adds `created_at` and `updated_at` fields to the specified entities.
|
||||||
|
* Add it to your plugins in `bknd.config.ts` like this:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* export default {
|
||||||
|
* plugins: [timestamps({ entities: ["posts"] })],
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function timestamps({
|
||||||
|
entities = [],
|
||||||
|
setUpdatedOnCreate = true,
|
||||||
|
}: TimestampsPluginOptions): AppPlugin {
|
||||||
|
return (app: App) => ({
|
||||||
|
name: "timestamps",
|
||||||
|
schema: () => {
|
||||||
|
if (entities.length === 0) {
|
||||||
|
$console.warn("No entities specified for timestamps plugin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appEntities = app.em.entities.map((e) => e.name);
|
||||||
|
|
||||||
|
return em(
|
||||||
|
Object.fromEntries(
|
||||||
|
entities
|
||||||
|
.filter((e) => appEntities.includes(e))
|
||||||
|
.map((e) => [
|
||||||
|
e,
|
||||||
|
entity(e, {
|
||||||
|
created_at: datetime(),
|
||||||
|
updated_at: datetime(),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onBuilt: async () => {
|
||||||
|
app.emgr.onEvent(
|
||||||
|
DatabaseEvents.MutatorInsertBefore,
|
||||||
|
(event) => {
|
||||||
|
const { entity, data } = event.params;
|
||||||
|
if (entities.includes(entity.name)) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
created_at: new Date(),
|
||||||
|
updated_at: setUpdatedOnCreate ? new Date() : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "sync",
|
||||||
|
id: "bknd-timestamps",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
app.emgr.onEvent(
|
||||||
|
DatabaseEvents.MutatorUpdateBefore,
|
||||||
|
async (event) => {
|
||||||
|
const { entity, data } = event.params;
|
||||||
|
if (entities.includes(entity.name)) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
updated_at: new Date(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: "sync",
|
||||||
|
id: "bknd-timestamps",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -7,3 +7,4 @@ export { showRoutes, type ShowRoutesOptions } from "./dev/show-routes.plugin";
|
|||||||
export { syncConfig, type SyncConfigOptions } from "./dev/sync-config.plugin";
|
export { syncConfig, type SyncConfigOptions } from "./dev/sync-config.plugin";
|
||||||
export { syncTypes, type SyncTypesOptions } from "./dev/sync-types.plugin";
|
export { syncTypes, type SyncTypesOptions } from "./dev/sync-types.plugin";
|
||||||
export { syncSecrets, type SyncSecretsOptions } from "./dev/sync-secrets.plugin";
|
export { syncSecrets, type SyncSecretsOptions } from "./dev/sync-secrets.plugin";
|
||||||
|
export { timestamps, type TimestampsPluginOptions } from "./data/timestamps.plugin";
|
||||||
|
|||||||
Reference in New Issue
Block a user