Skip to content

Commit 70b5ffa

Browse files
authored
use relation for collaborators (#146)
1 parent 6304cf0 commit 70b5ffa

File tree

6 files changed

+90
-73
lines changed

6 files changed

+90
-73
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `collaboratorIds` on the `Repo` table. All the data in the column will be lost.
5+
6+
*/
7+
-- DropIndex
8+
DROP INDEX "Repo_name_userId_key";
9+
10+
-- AlterTable
11+
ALTER TABLE "Repo" DROP COLUMN "collaboratorIds";
12+
13+
-- CreateTable
14+
CREATE TABLE "_COLLABORATOR" (
15+
"A" TEXT NOT NULL,
16+
"B" TEXT NOT NULL
17+
);
18+
19+
-- CreateIndex
20+
CREATE UNIQUE INDEX "_COLLABORATOR_AB_unique" ON "_COLLABORATOR"("A", "B");
21+
22+
-- CreateIndex
23+
CREATE INDEX "_COLLABORATOR_B_index" ON "_COLLABORATOR"("B");
24+
25+
-- AddForeignKey
26+
ALTER TABLE "_COLLABORATOR" ADD CONSTRAINT "_COLLABORATOR_A_fkey" FOREIGN KEY ("A") REFERENCES "Repo"("id") ON DELETE CASCADE ON UPDATE CASCADE;
27+
28+
-- AddForeignKey
29+
ALTER TABLE "_COLLABORATOR" ADD CONSTRAINT "_COLLABORATOR_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

api/prisma/schema.prisma

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,20 @@ model User {
4444
hashedPassword String?
4545
posts Post[]
4646
profile Profile?
47-
Repo Repo[]
47+
Repo Repo[] @relation("OWNER")
48+
sharedRepos Repo[] @relation("COLLABORATOR")
4849
}
4950

5051
model Repo {
51-
id String @id
52-
name String?
52+
id String @id
53+
name String?
5354
// fullname String @unique
54-
owner User @relation(fields: [userId], references: [id])
55-
userId String
56-
pods Pod[] @relation("BELONG")
57-
podsId String[]
58-
public Boolean @default(false)
59-
collaboratorIds String[]
60-
61-
@@unique([name, userId])
55+
owner User @relation("OWNER", fields: [userId], references: [id])
56+
userId String
57+
pods Pod[] @relation("BELONG")
58+
podsId String[]
59+
public Boolean @default(false)
60+
collaborators User[] @relation("COLLABORATOR")
6261
}
6362

6463
enum PodType {

api/src/resolver_repo.ts

Lines changed: 38 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,32 @@ const { PrismaClient } = Prisma;
88

99
const prisma = new PrismaClient();
1010

11-
// console.log("resolver_repo.ts", Prisma.RepoInclude);
12-
13-
function checkWriteAccess({ repo, userId }) {
14-
console.log(repo, userId);
15-
if (repo.userId !== userId && repo.collaboratorIds.indexOf(userId) === -1) {
16-
throw new Error("You do not have write access to this repo.");
11+
async function ensureRepoEditAccess({ repoId, userId }) {
12+
let repo = await prisma.repo.findFirst({
13+
where: {
14+
id: repoId,
15+
OR: [
16+
{ owner: { id: userId || "undefined" } },
17+
{ collaborators: { some: { id: userId || "undefined" } } },
18+
],
19+
},
20+
});
21+
if (!repo) {
22+
// this might be caused by creating a pod and update it too soon before it
23+
// is created on server, which is a time sequence bug
24+
throw new Error("Repo not exists.");
1725
}
1826
}
1927

20-
async function ensurePodAccess({ id, userId, read = true }) {
28+
async function ensurePodEditAccess({ id, userId }) {
2129
let pod = await prisma.pod.findFirst({
22-
where: { id },
23-
// HEBI: select is used to select a subset of fields
24-
// select: {
25-
// repo: {
26-
// select: {
27-
// owner: true,
28-
// },
29-
// },
30-
// },
31-
// HEBI: include is used to include additional fields
32-
// Both include and select can go through relations, but they cannot be used
33-
// at the same time.
34-
include: {
30+
where: {
31+
id,
3532
repo: {
36-
include: {
37-
owner: true,
38-
},
33+
OR: [
34+
{ owner: { id: userId || "undefined" } },
35+
{ collaborators: { some: { id: userId || "undefined" } } },
36+
],
3937
},
4038
},
4139
});
@@ -44,16 +42,6 @@ async function ensurePodAccess({ id, userId, read = true }) {
4442
// is created on server, which is a time sequence bug
4543
throw new Error("Pod not exists.");
4644
}
47-
// public repo can be accessed by everyone
48-
// if the user is the owner or one of the collaborators, then it is ok
49-
if (
50-
pod.repo.owner.id !== userId &&
51-
pod.repo.collaboratorIds.indexOf(userId) === -1
52-
) {
53-
if (!read || !pod.repo.public) {
54-
throw new Error("You do not have access to this pod.");
55-
}
56-
}
5745
}
5846

5947
export async function repos() {
@@ -80,11 +68,10 @@ export async function myRepos(_, __, { userId }) {
8068

8169
export async function myCollabRepos(_, __, { userId }) {
8270
if (!userId) throw Error("Unauthenticated");
83-
// console.log("myCollabRepos", userId);
8471
const repos = await prisma.repo.findMany({
8572
where: {
86-
collaboratorIds: {
87-
has: userId,
73+
collaborators: {
74+
some: { id: userId },
8875
},
8976
},
9077
});
@@ -98,11 +85,12 @@ export async function repo(_, { id }, { userId }) {
9885
OR: [
9986
{ id, public: true },
10087
{ id, owner: { id: userId || "undefined" } },
101-
{ id, collaboratorIds: { has: userId || "undefined" } },
88+
{ id, collaborators: { some: { id: userId || "undefined" } } },
10289
],
10390
},
10491
include: {
10592
owner: true,
93+
collaborators: true,
10694
pods: {
10795
include: {
10896
children: true,
@@ -210,25 +198,28 @@ export async function addCollaborator(_, { repoId, email }, { userId }) {
210198
id: repoId,
211199
owner: { id: userId },
212200
},
201+
include: {
202+
collaborators: true,
203+
},
213204
});
214205
if (!repo) throw new Error("Repo not found or you are not the owner.");
215206
// 2. find the user
216-
const user = await prisma.user.findFirst({
207+
const other = await prisma.user.findFirst({
217208
where: {
218209
email,
219210
},
220211
});
221-
if (!user) throw new Error("User not found");
222-
if (user.id === userId) throw new Error("You are already the owner.");
223-
if (repo.collaboratorIds.indexOf(user.id) !== -1)
212+
if (!other) throw new Error("User not found");
213+
if (other.id === userId) throw new Error("You are already the owner.");
214+
if (repo.collaborators.findIndex((user) => user.id === other.id) !== -1)
224215
throw new Error("The user is already a collaborator.");
225216
// 3. add the user to the repo
226217
const res = await prisma.repo.update({
227218
where: {
228219
id: repoId,
229220
},
230221
data: {
231-
collaboratorIds: { push: user.id },
222+
collaborators: { connect: { id: other.id } },
232223
},
233224
});
234225
return true;
@@ -237,20 +228,13 @@ export async function addCollaborator(_, { repoId, email }, { userId }) {
237228
export async function addPod(_, { repoId, parent, index, input }, { userId }) {
238229
// make sure the repo is writable by this user
239230
if (!userId) throw new Error("Not authenticated.");
240-
// 1. find the repo
241-
const repo = await prisma.repo.findFirst({
242-
where: {
243-
id: repoId,
244-
},
245-
});
246-
if (!repo) throw new Error("Repo not found");
231+
await ensureRepoEditAccess({ repoId, userId });
247232

248-
checkWriteAccess({ repo, userId });
249233
// update all other records
250234
await prisma.pod.updateMany({
251235
where: {
252236
repo: {
253-
id: repo.id,
237+
id: repoId,
254238
},
255239
index: {
256240
gte: index,
@@ -279,7 +263,7 @@ export async function addPod(_, { repoId, parent, index, input }, { userId }) {
279263
index,
280264
repo: {
281265
connect: {
282-
id: repo.id,
266+
id: repoId,
283267
},
284268
},
285269
parent:
@@ -298,7 +282,7 @@ export async function addPod(_, { repoId, parent, index, input }, { userId }) {
298282

299283
export async function updatePod(_, { id, input }, { userId }) {
300284
if (!userId) throw new Error("Not authenticated.");
301-
await ensurePodAccess({ id, userId, read: false });
285+
await ensurePodEditAccess({ id, userId });
302286
const pod = await prisma.pod.update({
303287
where: {
304288
id,
@@ -318,13 +302,12 @@ export async function updatePod(_, { id, input }, { userId }) {
318302
},
319303
},
320304
});
321-
console.log("Updated pod", pod);
322305
return true;
323306
}
324307

325308
export async function deletePod(_, { id, toDelete }, { userId }) {
326309
if (!userId) throw new Error("Not authenticated.");
327-
await ensurePodAccess({ id, userId, read: false });
310+
await ensurePodEditAccess({ id, userId });
328311
// find all children of this ID
329312
// FIXME how to ensure atomic
330313
// 1. find the parent of this node

api/src/typedefs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const typeDefs = gql`
1919
name: String
2020
pods: [Pod]
2121
userId: ID!
22-
collaboratorIds: [ID!]
22+
collaborators: [User]
2323
public: Boolean
2424
}
2525

ui/src/lib/fetch.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ export async function doRemoteLoadRepo({ id, client }) {
1717
id
1818
name
1919
userId
20-
collaboratorIds
20+
collaborators {
21+
id
22+
}
2123
pods {
2224
id
2325
type
@@ -68,11 +70,11 @@ export async function doRemoteLoadRepo({ id, client }) {
6870
name: res.data.repo.name,
6971
error: null,
7072
userId: res.data.repo.userId,
71-
collaboratorIds: res.data.repo.collaboratorIds,
73+
collaborators: res.data.repo.collaborators,
7274
};
7375
} catch (e) {
7476
console.log(e);
75-
return { pods: [], name: "", error: e, userId: null, collaboratorIds: [] };
77+
return { pods: [], name: "", error: e, userId: null, collaborators: [] };
7678
}
7779
}
7880

ui/src/lib/store.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -723,8 +723,9 @@ const createRepoSlice: StateCreator<
723723
);
724724
},
725725
loadRepo: async (client, id) => {
726-
const { pods, name, error, userId, collaboratorIds } =
727-
await doRemoteLoadRepo({ id, client });
726+
const { pods, name, error, userId, collaborators } = await doRemoteLoadRepo(
727+
{ id, client }
728+
);
728729
set(
729730
produce((state) => {
730731
// TODO the children ordered by index
@@ -739,7 +740,10 @@ const createRepoSlice: StateCreator<
739740
// set the user role in this repo
740741
if (userId === state.user.id) {
741742
state.role = RoleType.OWNER;
742-
} else if (state.user && collaboratorIds.indexOf(state.user.id) >= 0) {
743+
} else if (
744+
state.user &&
745+
collaborators.findIndex(({ id }) => state.user.id === id) >= 0
746+
) {
743747
state.role = RoleType.COLLABORATOR;
744748
} else {
745749
state.role = RoleType.GUEST;

0 commit comments

Comments
 (0)