Skip to content

Commit 0e5f874

Browse files
committed
feat: delete base
1 parent 723b8c4 commit 0e5f874

File tree

11 files changed

+167
-6
lines changed

11 files changed

+167
-6
lines changed

apps/frontend/src/lib/components/blocks/base/base-setting.svelte

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { toggleModal } from "$lib/store/modal.store"
2+
import * as AlertDialog from "$lib/components/ui/alert-dialog/index.js"
33
import { trpc } from "$lib/trpc/client"
44
import { createMutation } from "@tanstack/svelte-query"
55
import type { IBaseDTO } from "@undb/base"
@@ -9,8 +9,13 @@
99
import * as Form from "$lib/components/ui/form"
1010
import { Switch } from "$lib/components/ui/switch"
1111
import Input from "$lib/components/ui/input/input.svelte"
12+
import { hasPermission } from "$lib/store/space-member.store"
13+
import { Button } from "$lib/components/ui/button"
14+
import { toast } from "svelte-sonner"
15+
import { goto, invalidateAll } from "$app/navigation"
1216
1317
export let base: Omit<IBaseDTO, "spaceId">
18+
let deleteConfirm = ""
1419
1520
const updateBaseMutation = createMutation({
1621
mutationKey: ["base", base.id, "updateBase"],
@@ -40,9 +45,18 @@
4045
},
4146
)
4247
const { enhance, form: formData } = form
48+
49+
const deleteSpaceMutation = createMutation({
50+
mutationFn: trpc.base.delete.mutate,
51+
async onSuccess() {
52+
toast.success("Base deleted successfully")
53+
await invalidateAll()
54+
goto("/")
55+
},
56+
})
4357
</script>
4458

45-
<section>
59+
<section class="space-y-6">
4660
<form class="max-w-4xl space-y-4" method="POST" use:enhance>
4761
<legend class="mb-4 text-lg font-medium"> Base Setting </legend>
4862
<Form.Field {form} name="name" class="rounded-lg border p-4">
@@ -76,4 +90,46 @@
7690

7791
<Form.Button size="sm">Submit</Form.Button>
7892
</form>
93+
94+
<div class="max-w-4xl space-y-3 rounded-md border-2 border-red-500 p-4">
95+
<p class="text-red-500">Danger Zone</p>
96+
<div>Delete Base</div>
97+
98+
<AlertDialog.Root>
99+
<AlertDialog.Trigger asChild let:builder>
100+
<Button variant="destructive" builders={[builder]} disabled={!$hasPermission("base:delete")}>
101+
Delete Base
102+
</Button>
103+
</AlertDialog.Trigger>
104+
<AlertDialog.Content>
105+
<AlertDialog.Header>
106+
<AlertDialog.Title>Are you absolutely sure to delete base?</AlertDialog.Title>
107+
<AlertDialog.Description>
108+
This action cannot be undone. This will permanently delete your database state and remove your data from our
109+
servers.
110+
</AlertDialog.Description>
111+
</AlertDialog.Header>
112+
113+
<p>Please type <span class="text-red-500">DELETE</span> to confirm.</p>
114+
<Input bind:value={deleteConfirm} placeholder="DELETE" />
115+
116+
<AlertDialog.Footer>
117+
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
118+
<AlertDialog.Action let:builder asChild>
119+
<Button
120+
variant="destructive"
121+
builders={[builder]}
122+
disabled={//
123+
$deleteSpaceMutation.isPending || deleteConfirm !== "DELETE" || !$hasPermission("space:delete")}
124+
on:click={async () => {
125+
await $deleteSpaceMutation.mutateAsync({ id: base.id })
126+
}}
127+
>
128+
Delete Base
129+
</Button>
130+
</AlertDialog.Action>
131+
</AlertDialog.Footer>
132+
</AlertDialog.Content>
133+
</AlertDialog.Root>
134+
</div>
79135
</section>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { z } from "@undb/zod"
2+
import { baseIdSchema } from "../value-objects"
3+
4+
export const deleteBaseDTO = z.object({
5+
id: baseIdSchema,
6+
})
7+
8+
export type IDeleteBaseDTO = z.infer<typeof deleteBaseDTO>

packages/base/src/dto/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from "./base.dto"
22
export * from "./create-base.dto"
3+
export * from "./delete-base.dto"
34
export * from "./duplicate-base.dto"
45
export * from "./update-base.dto"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { injectBaseRepository, type IBaseRepository } from "@undb/base"
2+
import { DeleteBaseCommand } from "@undb/commands"
3+
import { commandHandler } from "@undb/cqrs"
4+
import { singleton } from "@undb/di"
5+
import { type ICommandHandler } from "@undb/domain"
6+
7+
@commandHandler(DeleteBaseCommand)
8+
@singleton()
9+
export class DeleteBaseCommandHandler implements ICommandHandler<DeleteBaseCommand, any> {
10+
constructor(
11+
@injectBaseRepository()
12+
private readonly repository: IBaseRepository,
13+
) {}
14+
15+
async execute(command: DeleteBaseCommand): Promise<any> {
16+
const base = (await this.repository.findOneById(command.id)).expect("base not found")
17+
18+
await this.repository.deleteOneById(base.id.value)
19+
20+
return base.id.value
21+
}
22+
}

packages/command-handlers/src/handlers/duplicate-base.command-handler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export class DuplicateBaseCommandHandler implements ICommandHandler<DuplicateBas
2222
async execute(command: DuplicateBaseCommand): Promise<any> {
2323
const base = (await this.baseRepository.findOneById(command.id)).expect("Base not found")
2424

25-
const duplicatedBase = await this.tableService.duplicateBase(base, mustGetCurrentSpaceId(), command)
25+
const spaceId = mustGetCurrentSpaceId()
26+
const duplicatedBase = await this.tableService.duplicateBase(base, spaceId, spaceId, command)
2627

2728
return duplicatedBase.id.value
2829
}

packages/command-handlers/src/handlers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { CreateTableFormCommandHandler } from "./create-table-form.command-handl
1313
import { CreateTableViewCommandHandler } from "./create-table-view.command-handler"
1414
import { CreateTableCommandHandler } from "./create-table.command-handler"
1515
import { CreateWebhookCommandHandler } from "./create-webhook.command-handler"
16+
import { DeleteBaseCommandHandler } from "./delete-base.command-handler"
1617
import { DeleteInvitationCommandHandler } from "./delete-invitation.command-handler"
1718
import { DeleteRecordCommandHandler } from "./delete-record.command-handler"
1819
import { DeleteSpaceCommandHandler } from "./delete-space.command-handler"
@@ -94,4 +95,5 @@ export const commandHandlers = [
9495
DeleteWebhookCommandHandler,
9596
DuplicateTableCommandHandler,
9697
DuplicateBaseCommandHandler,
98+
DeleteBaseCommandHandler,
9799
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { deleteBaseDTO } from "@undb/base"
2+
import { Command, type CommandProps } from "@undb/domain"
3+
import { z } from "@undb/zod"
4+
5+
export const deleteBaseCommand = deleteBaseDTO
6+
7+
export type IDeleteBaseCommand = z.infer<typeof deleteBaseCommand>
8+
9+
export class DeleteBaseCommand extends Command implements IDeleteBaseCommand {
10+
public readonly id: string
11+
12+
constructor(props: CommandProps<IDeleteBaseCommand>) {
13+
super(props)
14+
this.id = props.id
15+
}
16+
}

packages/commands/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export * from "./create-table-form.command"
1313
export * from "./create-table-view.command"
1414
export * from "./create-table.command"
1515
export * from "./create-webhook.command"
16+
export * from "./delete-base.command"
1617
export * from "./delete-invitation.command"
1718
export * from "./delete-record.command"
1819
export * from "./delete-space.command"

packages/persistence/src/base/base.repository.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
2+
injectBaseOutboxService,
23
WithBaseId,
34
WithBaseSpaceId,
4-
injectBaseOutboxService,
55
type Base,
66
type IBaseOutboxService,
77
type IBaseRepository,
@@ -10,9 +10,11 @@ import {
1010
import { executionContext, mustGetCurrentSpaceId } from "@undb/context/server"
1111
import { inject, singleton } from "@undb/di"
1212
import { None, Some, type Option } from "@undb/domain"
13+
import { injectTableRepository, TableBaseIdSpecification, type ITableRepository } from "@undb/table"
1314
import { getCurrentTransaction } from "../ctx"
1415
import type { IQueryBuilder } from "../qb"
1516
import { injectQueryBuilder } from "../qb.provider"
17+
import { UnderlyingTableService } from "../underlying/underlying-table.service"
1618
import { BaseFilterVisitor } from "./base.filter-visitor"
1719
import { BaseMapper } from "./base.mapper"
1820
import { BaseMutateVisitor } from "./base.mutate-visitor"
@@ -26,6 +28,10 @@ export class BaseRepository implements IBaseRepository {
2628
private readonly outboxService: IBaseOutboxService,
2729
@injectQueryBuilder()
2830
private readonly qb: IQueryBuilder,
31+
@injectTableRepository()
32+
private readonly tableRepository: ITableRepository,
33+
@inject(UnderlyingTableService)
34+
private readonly underlyingTableService: UnderlyingTableService,
2935
) {}
3036

3137
async find(spec: IBaseSpecification): Promise<Base[]> {
@@ -101,7 +107,37 @@ export class BaseRepository implements IBaseRepository {
101107
await this.outboxService.save(base)
102108
}
103109

104-
deleteOneById(id: string): Promise<void> {
105-
throw new Error("Method not implemented.")
110+
async deleteOneById(id: string): Promise<void> {
111+
const trx = getCurrentTransaction()
112+
113+
const tables = await this.tableRepository.find(Some(new TableBaseIdSpecification(id)))
114+
const tableIds = tables.map((t) => t.id.value)
115+
116+
await trx
117+
.deleteFrom("undb_table_id_mapping")
118+
.where((eb) => eb.eb("table_id", "in", tableIds))
119+
.execute()
120+
121+
await trx
122+
.deleteFrom("undb_rollup_id_mapping")
123+
.where((eb) => eb.eb("table_id", "in", tableIds))
124+
.execute()
125+
126+
await trx
127+
.deleteFrom("undb_reference_id_mapping")
128+
.where((eb) => eb.eb("table_id", "in", tableIds))
129+
.execute()
130+
131+
await trx
132+
.deleteFrom("undb_table")
133+
.where((eb) => eb.eb("id", "in", tableIds))
134+
.execute()
135+
136+
await trx
137+
.deleteFrom("undb_base")
138+
.where((eb) => eb.eb("id", "=", id))
139+
.execute()
140+
141+
await this.underlyingTableService.deleteTables(tables)
106142
}
107143
}

packages/persistence/src/underlying/underlying-table.service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createLogger } from "@undb/logger"
33
import type { TableComositeSpecification, TableDo } from "@undb/table"
44
import type { CompiledQuery } from "kysely"
55
import { getAnonymousTransaction, getCurrentTransaction } from "../ctx"
6+
import { JoinTable } from "./reference/join-table"
67
import { UnderlyingTable } from "./underlying-table"
78
import { UnderlyingTableFieldVisitor } from "./underlying-table-field.visitor"
89
import { UnderlyingTableSpecVisitor } from "./underlying-table-spec.visitor"
@@ -46,5 +47,16 @@ export class UnderlyingTableService {
4647
const t = new UnderlyingTable(table)
4748
const trx = getCurrentTransaction()
4849
await trx.schema.dropTable(t.name).execute()
50+
const referenceFields = table.schema.getReferenceFields()
51+
for (const field of referenceFields) {
52+
const joinTable = new JoinTable(table, field)
53+
await trx.schema.dropTable(joinTable.getTableName()).execute()
54+
}
55+
}
56+
57+
async deleteTables(tables: TableDo[]) {
58+
for (const table of tables) {
59+
await this.delete(table)
60+
}
4961
}
5062
}

0 commit comments

Comments
 (0)