Skip to content

Commit

Permalink
Fixed Workspace Member Management in SQL
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed Jun 29, 2024
1 parent dac79da commit b62ab5f
Show file tree
Hide file tree
Showing 22 changed files with 197 additions and 113 deletions.
12 changes: 8 additions & 4 deletions code/client/src/services/workspace/workspaceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ function workspaceService(http: HttpCommunication, errorHandler: ErrorHandler) {
}

async function addWorkspaceMember(id: string, email: string): Promise<void> {
validateEmail(email);
return errorHandler(async () => await http.post(`/workspaces/${id}/members`, { email }));
return errorHandler(async () => {
validateEmail(email);
await http.post(`/workspaces/${id}/members`, { email });
});
}

async function removeWorkspaceMember(id: string, email: string): Promise<void> {
validateEmail(email);
return errorHandler(async () => await http.delete(`/workspaces/${id}/members`, { email }));
return errorHandler(async () => {
validateEmail(email);
await http.delete(`/workspaces/${id}/members`, { email });
});
}

async function getWorkspaces() {
Expand Down
19 changes: 19 additions & 0 deletions code/client/src/ui/hooks/useAuthRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useAuth } from '@/contexts/auth/useAuth';
import { useNavigate } from 'react-router-dom';
import useError from '@/contexts/error/useError';
import { useEffect } from 'react';

function useAuthRedirect() {
const { isLoggedIn } = useAuth();
const navigate = useNavigate();
const { publishError } = useError();

useEffect(() => {
if (!isLoggedIn) {
publishError(Error('You need to be logged in to access this page'));
navigate('/login');
}
}, [isLoggedIn, navigate, publishError]);
}

export default useAuthRedirect;
3 changes: 3 additions & 0 deletions code/client/src/ui/pages/recent/Recent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Link } from 'react-router-dom';
import { formatTimePassed } from '@/utils/utils';
import { useCommunication } from '@/contexts/communication/useCommunication';
import useError from '@/contexts/error/useError';
import useAuthRedirect from '@ui/hooks/useAuthRedirect';
import './Recent.scss';

function Recent() {
Expand All @@ -13,6 +14,8 @@ function Recent() {
const { publishError } = useError();
const { loading, startLoading, stopLoading, spinner } = useLoading();

useAuthRedirect();

useEffect(() => {
async function fetchRecentDocuments() {
try {
Expand Down
5 changes: 4 additions & 1 deletion code/client/src/ui/pages/workspaces/Workspaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MdDelete } from 'react-icons/md';
import { useEffect, useState } from 'react';
import { sortWorkspaces } from '@domain/workspaces/utils';
import { useCommunication } from '@/contexts/communication/useCommunication';
import useAuthRedirect from '@ui/hooks/useAuthRedirect';
import './Workspaces.scss';

function Workspaces() {
Expand All @@ -14,6 +15,8 @@ function Workspaces() {
const [rows, setRows] = useState(workspaces);
const { socket } = useCommunication();

useAuthRedirect();

useEffect(() => {
socket.connect();
return () => socket.disconnect();
Expand All @@ -25,7 +28,7 @@ function Workspaces() {

return (
<div className="workspaces">
<h2>Workspaces</h2>
<h2>My Workspaces</h2>
<DataTable
columns={['Name', 'Members', 'Created', 'Privacy']}
hasSelected={selected.length > 0}
Expand Down
14 changes: 10 additions & 4 deletions code/server/sql/create_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ CREATE TABLE IF NOT EXISTS workspace (
id CHAR(16) PRIMARY KEY DEFAULT encode(gen_random_bytes(8), 'hex'),
name TEXT NOT NULL,
"isPrivate" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
members TEXT[] NOT NULL DEFAULT '{}'::TEXT[] -- references "user"(email)
"createdAt" TIMESTAMP NOT NULL DEFAULT now()
);

-- Create resource table
Expand All @@ -24,7 +23,7 @@ CREATE TABLE IF NOT EXISTS resource (
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP NOT NULL DEFAULT now(),
parent CHAR(16) DEFAULT NULL REFERENCES resource(id) ON DELETE CASCADE,
children CHAR(16)[] NOT NULL DEFAULT '{}'::CHAR(16)[] -- references resource(id)
children CHAR(16)[] NOT NULL DEFAULT '{}'::CHAR(16)[]
);

-- Create user table
Expand All @@ -35,7 +34,14 @@ CREATE TABLE IF NOT EXISTS "user" (
"createdAt" TIMESTAMP NOT NULL DEFAULT now()
);

-- Trigger functions
CREATE TABLE IF NOT EXISTS workspace_member (
wid CHAR(16) NOT NULL REFERENCES workspace(id) ON DELETE CASCADE,
uid CHAR(28) NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
PRIMARY KEY (wid, uid)
);


-- Triggers

-- Resource is deleted -> Remove self from parent's children array
create or replace function on_child_removed() returns trigger as $$
Expand Down
15 changes: 9 additions & 6 deletions code/server/sql/drop_tables.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
begin ;
drop table if exists "user";
drop table if exists resource cascade;
drop table if exists workspace cascade;
drop type if exists resource_type cascade;
commit ;
begin;

drop table if exists workspace_member cascade;
drop table if exists resource cascade;
drop table if exists workspace cascade;
drop table if exists "user" cascade;
drop type if exists resource_type cascade;

commit;
4 changes: 2 additions & 2 deletions code/server/src/databases/memory/MemoryResourcesDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ export class MemoryResourcesDB implements ResourcesRepository {
throw new NotFoundError(`Resource not found`);
}

async getRecentDocuments(email: string): Promise<DocumentResource[]> {
async getRecentDocuments(userId: string): Promise<DocumentResource[]> {
return Object.values(Memory.workspaces)
.filter(workspace => workspace.members.includes(email))
.filter(workspace => workspace.members.includes(userId))
.flatMap(workspace => Object.values(workspace.resources))
.filter(resource => resource.type === ResourceType.DOCUMENT)
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())
Expand Down
13 changes: 10 additions & 3 deletions code/server/src/databases/memory/MemoryWorkspacesDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@ export class MemoryWorkspacesDB implements WorkspacesRepository {
async getWorkspaces(email?: string): Promise<WorkspaceMeta[]> {
return Object.values(Memory.workspaces)
.filter(workspace => (email ? workspace.members.includes(email) : !workspace.isPrivate))
.map(props => omit(props, ['resources']));
.map(props => {
const w = omit(props, ['resources']);
return { ...w, members: w.members?.map(id => Memory.users[id].email) || [] };
});
}

async getWorkspace(id: string): Promise<Workspace> {
const workspace = Memory.workspaces[id];
if (!workspace) throw new NotFoundError(`Workspace not found`);
const resources = Object.values(workspace.resources);
return { ...workspace, resources };
return { ...workspace, resources, members: workspace.members.map(id => Memory.users[id].email) };
}

async getResources(wid: string): Promise<Resource[]> {
Expand Down Expand Up @@ -80,6 +83,10 @@ export class MemoryWorkspacesDB implements WorkspacesRepository {
.filter(workspace => (query ? workspace.name.toLowerCase().includes(query.toLowerCase()) : true)) // search by name
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) // sort results by creation date (newest first)
.slice(skip, skip + limit) // paginate results
.map(workspace => omit(workspace, ['resources'])); // convert to WorkspaceMeta
.map(workspace => {
// convert to WorkspaceMeta
const w = omit(workspace, ['resources']);
return { ...w, members: w.members?.map(id => Memory.users[id].email) || [] };
});
}
}
24 changes: 12 additions & 12 deletions code/server/src/databases/postgres/PostgresResourcesDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ export class PostgresResourcesDB implements ResourcesRepository {
if (isEmpty(results)) throw new NotFoundError('Resource not found');
}

async getRecentDocuments(email: string): Promise<DocumentResource[]> {
async getRecentDocuments(userId: string): Promise<DocumentResource[]> {
const results = await sql`
select row_to_json(t) as resource
from (
select *
from resource
where type = 'D' and workspace in (
select id from workspace where ${email} = any(members)
)
order by "updatedAt" desc
limit 10
) as t
`;
select row_to_json(t) as resource
from (
select *
from resource
where type = 'D' and workspace in (
select workspace_id from workspace_member where user_id = ${userId}
)
order by "updatedAt" desc
limit 10
) as t
`;
return results.map(r => r.resource);
}
}
Loading

0 comments on commit b62ab5f

Please sign in to comment.