Skip to content

Commit

Permalink
fix: project access
Browse files Browse the repository at this point in the history
  • Loading branch information
IgorGoryany committed Oct 17, 2024
1 parent ef711ff commit 28b5aac
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,25 @@ export const ProjectListItemConnected: FC<ProjectListItemConnectedProps> = ({
const subNodes = useMemo(
() =>
subTree?.children
? Object.values(subTree.children).map(({ project: p }) => (
<ProjectListItemConnected
subTree={subTree?.children?.[p.id]}
key={p.id}
project={p}
parent={project}
filterPreset={filterPreset}
partnershipProject={partnershipProject}
titleSize={isKanbanView ? 'l' : 'm'}
actionButtonView={isKanbanView ? 'default' : 'icons'}
/>
))
? Object.values(subTree.children).map(({ project: p }) =>
nullable(p, (p) => (
<ProjectListItemConnected
subTree={subTree?.children?.[p.id]}
key={p.id}
project={p}
parent={project}
filterPreset={filterPreset}
partnershipProject={partnershipProject}
titleSize={isKanbanView ? 'l' : 'm'}
actionButtonView={isKanbanView ? 'default' : 'icons'}
/>
)),
)
: [],
[filterPreset, isKanbanView, partnershipProject, project, subTree?.children],
);

const showNoGoals = !subNodes.length && subTree?.count === undefined;
const showNoGoals = !subNodes.length;

useEffect(() => {
setIsProjectEmpty(getIsProjectEmptySetter(subTree));
Expand Down
3 changes: 0 additions & 3 deletions src/pages/projects/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ export const getServerSideProps = declareSsrProps(
id,
goalsQuery: queryState,
}),
ssrHelpers.v2.project.getProjectChildren.fetch({
id,
}),
ssrHelpers.v2.project.getProjectChildrenTree.fetch({
id,
goalsQuery: queryState,
Expand Down
66 changes: 8 additions & 58 deletions trpc/queries/projectV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ export const getProjectsByIds = (params: { in: Array<{ id: string }>; activityId
`.as('participants'),
])
.groupBy(['Project.id', 'user.id'])
.select([
jsonBuildObject({
stargizers: sql<number>`(select count("A") from "_projectStargizers" where "B" = "Project".id)`,
watchers: sql<number>`(select count("A") from "_projectWatchers" where "B" = "Project".id)`,
children: sql<number>`(select count("B") from "_parentChildren" where "A" = "Project".id)`,
participants: sql<number>`(select count("A") from "_projectParticipants" where "B" = "Project".id)`,
}).as('_count'),
])
.$if(params.in.length > 0, (qb) =>
qb.where((eb) =>
eb.or([
Expand Down Expand Up @@ -747,61 +755,3 @@ export const getProjectChildrenTreeQuery = ({ id, goalsQuery }: { id: string; go
.groupBy(['Project.id', 'ch.level', 'ch.parent_chain'])
.orderBy('ch.level asc');
};

export const getChildrenProjectQuery = ({ id, role, activityId }: { id: string; role: Role; activityId: string }) =>
db
.selectFrom(({ selectFrom }) =>
selectFrom('Project')
.leftJoinLateral(
() => getUserActivity().distinctOn('Activity.id').as('participants'),
(join) =>
join.onRef('participants.id', 'in', ({ selectFrom }) =>
selectFrom('_projectParticipants').select('A').whereRef('B', '=', 'Project.id'),
),
)
.selectAll('Project')
.select(({ val, cast, case: caseFn, fn, exists }) => [
caseFn()
.when(fn.count('participants.id'), '>', 0)
.then(fn.agg('array_agg', [fn.toJson('participants')]))
.else(null)
.end()
.as('participants'),
jsonBuildObject({
stargizers: sql<number>`(select count("A") from "_projectStargizers" where "B" = "Project".id)`,
watchers: sql<number>`(select count("A") from "_projectWatchers" where "B" = "Project".id)`,
children: sql<number>`(select count("B") from "_parentChildren" where "A" = "Project".id)`,
participants: sql<number>`(select count("A") from "_projectParticipants" where "B" = "Project".id)`,
goals: cast(val(0), 'integer'),
}).as('_count'),
exists(
selectFrom('_projectWatchers')
.select('B')
.where('A', '=', activityId)
.whereRef('B', '=', 'Project.id'),
)
.$castTo<boolean>()
.as('_isWatching'),
exists(
selectFrom('_projectStargizers')
.select('B')
.where('A', '=', activityId)
.whereRef('B', '=', 'Project.id'),
)
.$castTo<boolean>()
.as('_isStarred'),
sql<boolean>`("Project"."activityId" = ${val(activityId)})`.as('_isOwner'),
sql<boolean>`((${val(role === Role.ADMIN)} or "Project"."activityId" = ${val(
activityId,
)}) and not "Project"."personal")`.as('_isEditable'),
])
.where('Project.id', 'in', () => getChildrenProjectsId({ in: [{ id }] }))
.groupBy('Project.id')
.as('projects'),
)
.innerJoin(
() => getUserActivity().as('activity'),
(join) => join.onRef('projects.activityId', '=', 'activity.id'),
)
.selectAll('projects')
.select(({ fn }) => [fn.toJson('activity').as('activity')]);
140 changes: 64 additions & 76 deletions trpc/router/projectV2.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { z } from 'zod';
import { sql } from 'kysely';
import { jsonBuildObject } from 'kysely/helpers/postgres';
import { Role } from '@prisma/client';

import { router, protectedProcedure } from '../trpcBackend';
import { projectsChildrenIdsSchema, projectSuggestionsSchema, userProjectsSchema } from '../../src/schema/project';
Expand All @@ -13,7 +12,6 @@ import {
getWholeGoalCountByProjectIds,
getDeepChildrenProjectsId,
getAllProjectsQuery,
getChildrenProjectQuery,
getProjectChildrenTreeQuery,
} from '../queries/projectV2';
import { queryWithFiltersSchema, sortableProjectsPropertiesArraySchema } from '../../src/schema/common';
Expand Down Expand Up @@ -93,7 +91,7 @@ type ProjectGoal = ExtractTypeFromGenerated<Goal> & {

export interface ProjectTree {
[key: string]: {
project: ProjectResponse & Pick<DashboardProject, '_count'>;
project: (ProjectResponse & Pick<DashboardProject, '_count'>) | null;
count?: number;
children: ProjectTree | null;
};
Expand Down Expand Up @@ -161,14 +159,6 @@ export const project = router({
}

return getProjectsByIds({ activityId, in: projectIds, role })
.select([
jsonBuildObject({
stargizers: sql<number>`(select count("A") from "_projectStargizers" where "B" = "Project".id)`,
watchers: sql<number>`(select count("A") from "_projectWatchers" where "B" = "Project".id)`,
children: sql<number>`(select count("B") from "_parentChildren" where "A" = "Project".id)`,
participants: sql<number>`(select count("A") from "_projectParticipants" where "B" = "Project".id)`,
}).as('_count'),
])
.$castTo<ProjectResponse & Pick<DashboardProject, '_count'>>()
.execute();
} catch (e) {
Expand Down Expand Up @@ -283,76 +273,74 @@ export const project = router({
goalsQuery: queryWithFiltersSchema.optional(),
}),
)
.query(async ({ input, ctx }) => {
const rows = await getProjectChildrenTreeQuery(input).$castTo<ProjectTreeRow>().execute();
const projects = await getProjectsByIds({
in: rows.map(({ id }) => ({ id })),
...ctx.session.user,
})
.select([
jsonBuildObject({
stargizers: sql<number>`(select count("A") from "_projectStargizers" where "B" = "Project".id)`,
watchers: sql<number>`(select count("A") from "_projectWatchers" where "B" = "Project".id)`,
children: sql<number>`(select count("B") from "_parentChildren" where "A" = "Project".id)`,
participants: sql<number>`(select count("A") from "_projectParticipants" where "B" = "Project".id)`,
}).as('_count'),
])
.$castTo<ProjectResponse & Pick<DashboardProject, '_count'>>()
.execute();

const projectMap = new Map(projects.map((project) => [project.id, project]));

const map: ProjectTree = {};

rows.forEach(({ id, chain, goal_count: count, deep }) => {
const path = Array.from(chain);

path.reduce((acc, key, i) => {
if (!acc[key]) {
acc[key] = {
project: projectMap.get(key) as ProjectResponse & Pick<DashboardProject, '_count'>,
children: {
.query(
async ({
input,
ctx: {
session: { user },
},
}) => {
const rows = await getProjectChildrenTreeQuery(input).$castTo<ProjectTreeRow>().execute();
const projects = await getProjectsByIds({
in: rows.map(({ id }) => ({ id })),
...user,
})
.$if(user.role === Role.USER, (qb) =>
qb.where(({ or, eb, not, exists }) =>
or([
eb('Project.id', 'in', ({ selectFrom }) =>
selectFrom('_projectAccess').select('B').where('A', '=', user.activityId),
),
not(
exists(({ selectFrom }) =>
selectFrom('_projectAccess').select('B').whereRef('B', '=', 'Project.id'),
),
),
]),
),
)
.$castTo<ProjectResponse & Pick<DashboardProject, '_count'>>()
.execute();

const projectMap = new Map<string, ProjectTree[string]['project']>(
projects.map((project) => [project.id, project]),
);

const map: ProjectTree = {};

rows.forEach(({ id, chain, goal_count: count, deep }) => {
const path = Array.from(chain);

path.reduce((acc, key, i) => {
if (!acc[key]) {
acc[key] = {
project: projectMap.get(key) || null,
children: {
[id]: {
project: projectMap.get(id) || null,
count,
children: null,
},
},
};
} else if (i + 1 === deep) {
acc[key].children = {
...acc[key].children,
[id]: {
project: projectMap.get(id) as ProjectResponse & Pick<DashboardProject, '_count'>,
project: projectMap.get(id) || null,
count,
children: null,
},
},
};
} else if (i + 1 === deep) {
acc[key].children = {
...acc[key].children,
[id]: {
project: projectMap.get(id) as ProjectResponse & Pick<DashboardProject, '_count'>,
count,
children: null,
},
};
}

return acc[key].children as ProjectTree;
}, map);
});
};
}

return map;
}),

getProjectChildren: protectedProcedure
.input(
z.object({
id: z.string(),
}),
)
.query(async ({ input, ctx }) => {
const childrenQuery = getChildrenProjectQuery({
...ctx.session.user,
...input,
}).$castTo<Omit<ProjectResponse, 'goals'> & Pick<DashboardProject, '_count'>>();

const res = await childrenQuery.execute();
return acc[key].children as ProjectTree;
}, map);
});

return res;
}),
return map;
},
),

getProjectGoalsById: protectedProcedure
.input(
Expand Down

0 comments on commit 28b5aac

Please sign in to comment.