feat(caprover): allow human to mark an app as successfully updated

This commit is contained in:
Pierre Martin
2024-03-10 12:22:38 +01:00
parent 5c599842f6
commit 204efe8a8b
9 changed files with 215 additions and 12 deletions

View File

@@ -0,0 +1,15 @@
import DomainEvent from "../DomainEvent";
type ApplicationUpdateFinishedPayload = {
id: string;
};
export class ApplicationUpdateFinished
implements DomainEvent<ApplicationUpdateFinishedPayload>
{
readonly type = "ApplicationUpdateFinished" as const;
constructor(
public readonly payload: ApplicationUpdateFinishedPayload,
public readonly createdAt: Date
) {}
}

View File

@@ -0,0 +1,99 @@
import { beforeEach, describe, expect, it } from "bun:test";
import { TestApp } from "../testing/TestApp";
import { ApplicationUpdateStarted } from "../events/ApplicationUpdateStarted";
import { ApplicationUpdateFinished } from "../events/ApplicationUpdateFinished";
describe("ApplicationUpdates", () => {
describe("getPendingUpdates", () => {
it("should return an empty array when there are no pending updates", () => {
const app = new TestApp();
expect(app.projections.ApplicationUpdates.getPendingUpdates()).toEqual(
[]
);
});
it("should return all pending updates when several started", () => {
const app = new TestApp([
new ApplicationUpdateStarted(
{ id: "mail", newVersion: "1.0.0" },
new Date()
),
new ApplicationUpdateStarted(
{ id: "blog", newVersion: "10.0.1" },
new Date()
),
]);
const updates = app.projections.ApplicationUpdates.getPendingUpdates();
expect(updates).toEqual([
{ id: "mail", newVersion: "1.0.0" },
{ id: "blog", newVersion: "10.0.1" },
]);
});
it("should return all pending updates for an application while it isn't successfull", () => {
const app = new TestApp([
new ApplicationUpdateStarted(
{ id: "mail", newVersion: "1.0.0" },
new Date()
),
new ApplicationUpdateStarted(
{ id: "blog", newVersion: "10.0.1" },
new Date()
),
new ApplicationUpdateStarted(
{ id: "mail", newVersion: "1.0.1" },
new Date()
),
]);
const updates = app.projections.ApplicationUpdates.getPendingUpdates();
expect(updates).toEqual([
{ id: "mail", newVersion: "1.0.0" },
{ id: "blog", newVersion: "10.0.1" },
{ id: "mail", newVersion: "1.0.1" },
]);
});
it("should not return updates for applications after they were marked as successful", () => {
const app = new TestApp([
new ApplicationUpdateStarted(
{ id: "mail", newVersion: "1.0.0" },
new Date()
),
new ApplicationUpdateStarted(
{ id: "blog", newVersion: "10.0.1" },
new Date()
),
new ApplicationUpdateFinished({ id: "mail" }, new Date()),
]);
const updates = app.projections.ApplicationUpdates.getPendingUpdates();
expect(updates).toEqual([{ id: "blog", newVersion: "10.0.1" }]);
});
it("should consider the latest update for an application", () => {
const app = new TestApp([
new ApplicationUpdateStarted(
{ id: "mail", newVersion: "1.0.0" },
new Date()
),
new ApplicationUpdateStarted(
{ id: "blog", newVersion: "4.2.0" },
new Date()
),
new ApplicationUpdateFinished({ id: "mail" }, new Date()),
new ApplicationUpdateStarted(
{ id: "mail", newVersion: "1.0.1" },
new Date()
),
]);
const updates = app.projections.ApplicationUpdates.getPendingUpdates();
expect(updates).toEqual([
{ id: "blog", newVersion: "4.2.0" },
{ id: "mail", newVersion: "1.0.1" },
]);
});
});
});

View File

@@ -1,5 +1,7 @@
import DomainEvent from "../DomainEvent";
import DomainProjection from "../DomainProjection";
import { ApplicationUpdateFinished } from "../events/ApplicationUpdateFinished";
import { ApplicationUpdateStarted } from "../events/ApplicationUpdateStarted";
export type UpdateDefinition = {
id: string;
@@ -7,10 +9,14 @@ export type UpdateDefinition = {
};
export default class ApplicationUpdates implements DomainProjection {
private readonly pendingUpdates: UpdateDefinition[] = [];
private pendingUpdates: UpdateDefinition[] = [];
handle(event: DomainEvent<any>): void {
if (event.type === "ApplicationUpdateStarted") {
this.pendingUpdates.push(event.payload);
} else if (event.type === "ApplicationUpdateFinished") {
this.pendingUpdates = this.pendingUpdates.filter((pendingUpdate) => {
return pendingUpdate.id !== event.payload.id;
});
}
}