Skip to content

Commit e085078

Browse files
committed
use repo in most places
1 parent e842cba commit e085078

3 files changed

Lines changed: 72 additions & 74 deletions

File tree

packages/web-backend/src/Folders/FoldersRepo.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { nanoId } from "@cap/database/helpers";
22
import * as Db from "@cap/database/schema";
33
import { Folder, type Organisation, type User } from "@cap/web-domain";
44
import * as Dz from "drizzle-orm";
5-
import { Effect, Option } from "effect";
5+
import { Array, Effect, Option } from "effect";
66
import type { Schema } from "effect/Schema";
77
import { Database } from "../Database.ts";
88

@@ -21,14 +21,24 @@ export class FoldersRepo extends Effect.Service<FoldersRepo>()("FoldersRepo", {
2121
/**
2222
* Gets a `Folder` by its ID.
2323
*/
24-
const getById = (id: Folder.FolderId) =>
25-
Effect.gen(function* () {
26-
const [folder] = yield* db.execute((db) =>
27-
db.select().from(Db.folders).where(Dz.eq(Db.folders.id, id)),
28-
);
29-
30-
return Option.fromNullable(folder);
31-
});
24+
const getById = (
25+
id: Folder.FolderId,
26+
filters?: { organizationId?: Organisation.OrganisationId },
27+
) =>
28+
db
29+
.execute((db) =>
30+
db
31+
.select()
32+
.from(Db.folders)
33+
.where(
34+
Dz.and(
35+
Dz.eq(Db.folders.id, id),
36+
filters?.organizationId &&
37+
Dz.eq(Db.folders.organizationId, filters.organizationId),
38+
),
39+
),
40+
)
41+
.pipe(Effect.map(Array.get(0)));
3242

3343
const delete_ = (id: Folder.FolderId) =>
3444
db.execute((db) => db.delete(Db.folders).where(Dz.eq(Db.folders.id, id)));

packages/web-backend/src/Folders/index.ts

Lines changed: 52 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ import * as Dz from "drizzle-orm";
1111
import { Effect, Option } from "effect";
1212
import { Database, type DatabaseError } from "../Database.ts";
1313
import { FoldersPolicy } from "./FoldersPolicy.ts";
14+
import { FoldersRepo } from "./FoldersRepo.ts";
1415

1516
// @effect-diagnostics-next-line leakingRequirements:off
1617
export class Folders extends Effect.Service<Folders>()("Folders", {
1718
effect: Effect.gen(function* () {
1819
const db = yield* Database;
1920
const policy = yield* FoldersPolicy;
21+
const repo = yield* FoldersRepo;
2022

2123
const deleteFolder = (folder: {
2224
id: Folder.FolderId;
@@ -78,45 +80,33 @@ export class Folders extends Effect.Service<Folders>()("Folders", {
7880

7981
if (Option.isSome(data.parentId)) {
8082
const parentId = data.parentId.value;
81-
const [parentFolder] = yield* db.execute((db) =>
82-
db
83-
.select()
84-
.from(Db.folders)
85-
.where(
86-
Dz.and(
87-
Dz.eq(Db.folders.id, parentId),
88-
Dz.eq(Db.folders.organizationId, user.activeOrganizationId),
83+
84+
yield* repo
85+
.getById(parentId, {
86+
organizationId: Organisation.OrganisationId.make(
87+
user.activeOrganizationId,
88+
),
89+
})
90+
.pipe(
91+
Policy.withPolicy(policy.canEdit(parentId)),
92+
Effect.flatMap(
93+
Effect.catchTag(
94+
"NoSuchElementException",
95+
() => new Folder.NotFoundError(),
8996
),
9097
),
91-
);
92-
93-
if (!parentFolder) return yield* new Folder.NotFoundError();
98+
);
9499
}
95100

96-
const folder = {
97-
id: Folder.FolderId.make(nanoId()),
101+
yield* repo.create({
98102
name: data.name,
99103
color: data.color,
100-
organizationId: user.activeOrganizationId,
101-
createdById: user.id,
102-
spaceId: data.spaceId,
103-
parentId: data.parentId,
104-
};
105-
106-
yield* db.execute((db) =>
107-
db.insert(Db.folders).values({
108-
...folder,
109-
spaceId: Option.getOrNull(folder.spaceId),
110-
parentId: Option.getOrNull(folder.parentId),
111-
}),
112-
);
113-
114-
return new Folder.Folder({
115-
...folder,
116104
organizationId: Organisation.OrganisationId.make(
117105
user.activeOrganizationId,
118106
),
119107
createdById: User.UserId.make(user.id),
108+
spaceId: data.spaceId,
109+
parentId: data.parentId,
120110
});
121111
}),
122112

@@ -140,12 +130,17 @@ export class Folders extends Effect.Service<Folders>()("Folders", {
140130
folderId: Folder.FolderId,
141131
data: Folder.FolderUpdate,
142132
) {
143-
const [folder] = yield* db
144-
.execute((db) =>
145-
db.select().from(Db.folders).where(Dz.eq(Db.folders.id, folderId)),
146-
)
147-
.pipe(Policy.withPolicy(policy.canEdit(folderId)));
148-
if (!folder) return yield* new Folder.NotFoundError();
133+
const folder = yield* repo
134+
.getById(folderId)
135+
.pipe(
136+
Policy.withPolicy(policy.canEdit(folderId)),
137+
Effect.flatMap(
138+
Effect.catchTag(
139+
"NoSuchElementException",
140+
() => new Folder.NotFoundError(),
141+
),
142+
),
143+
);
149144

150145
// If parentId is provided and not null, verify it exists and belongs to the same organization
151146
if (!data.parentId) return;
@@ -155,20 +150,21 @@ export class Folders extends Effect.Service<Folders>()("Folders", {
155150
if (parentId === folderId)
156151
return yield* new Folder.RecursiveDefinitionError();
157152

158-
const [parentFolder] = yield* db
159-
.execute((db) =>
160-
db
161-
.select()
162-
.from(Db.folders)
163-
.where(
164-
Dz.and(
165-
Dz.eq(Db.folders.id, parentId),
166-
Dz.eq(Db.folders.organizationId, folder.organizationId),
167-
),
153+
const parentFolder = yield* repo
154+
.getById(parentId, {
155+
organizationId: Organisation.OrganisationId.make(
156+
folder.organizationId,
157+
),
158+
})
159+
.pipe(
160+
Policy.withPolicy(policy.canEdit(parentId)),
161+
Effect.flatMap(
162+
Effect.catchTag(
163+
"NoSuchElementException",
164+
() => new Folder.ParentNotFoundError(),
168165
),
169-
)
170-
.pipe(Policy.withPolicy(policy.canEdit(parentId)));
171-
if (!parentFolder) return yield* new Folder.ParentNotFoundError();
166+
),
167+
);
172168

173169
// Check for circular references in the folder hierarchy
174170
let currentParentId = parentFolder.parentId;
@@ -177,21 +173,14 @@ export class Folders extends Effect.Service<Folders>()("Folders", {
177173
return yield* new Folder.RecursiveDefinitionError();
178174

179175
const parentId = currentParentId;
180-
const [nextParent] = yield* db.execute((db) =>
181-
db
182-
.select()
183-
.from(Db.folders)
184-
.where(
185-
Dz.and(
186-
Dz.eq(Db.folders.id, parentId),
187-
// This should be implied but extra tenant isolation can't hurt
188-
Dz.eq(Db.folders.organizationId, folder.organizationId),
189-
),
190-
),
191-
);
192-
193-
if (!nextParent) break;
194-
currentParentId = nextParent.parentId;
176+
const nextParent = yield* repo.getById(parentId, {
177+
organizationId: Organisation.OrganisationId.make(
178+
folder.organizationId,
179+
),
180+
});
181+
182+
if (Option.isNone(nextParent)) break;
183+
currentParentId = nextParent.value.parentId;
195184
}
196185

197186
yield* db.execute((db) =>

packages/web-domain/src/Folder.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ export class FolderRpcs extends RpcGroup.make(
6363
spaceId: Schema.OptionFromUndefinedOr(SpaceId),
6464
parentId: Schema.OptionFromUndefinedOr(FolderId),
6565
}),
66-
success: Folder,
67-
error: Schema.Union(NotFoundError, InternalError),
66+
error: Schema.Union(NotFoundError, InternalError, PolicyDeniedError),
6867
}).middleware(RpcAuthMiddleware),
6968
Rpc.make("FolderUpdate", {
7069
payload: FolderUpdate,

0 commit comments

Comments
 (0)