Skip to content

Commit 3f1f1c5

Browse files
authored
feat: Add project select to the contributors page gf-504 (#516)
* feat: add select project component gf-504 * feat: place loader inside table gf-504 * fix: display only contributors of selected project gf-504 * fix: findAllByProjectId so it returns all contributor's projects and not only selected gf-504 * fix: findAll methods so all of them orders results in descending order gf-504 * refactor: rid of code duplication and fix pagination when project selected gf-504 * fix: remove unneccessary changes and fix import gf-504 * fix: do not render untracked contributor cards gf-504 * fix: update contributor.controller findAll swagger comment gf-504 * feat: link Select with URL gf-504 * fix: include contributorName into queryToSend gf-504 * refactor: fix A10 QC gf-504 * fix: order of contributor cards gf-504 * refactor: use enum values gf-504 * refactor: rename RequestDto to queryParameters gf-504 * refactor: rename ContributorOrderBy to ContributorOrderByKey gf-504 * refactor: move contributor orderBy validation to schema gf-504 * refactor: make projectId be a number in ActivityLogQueryParameters type gf-504 * refactor: remove TODO comment gf-504
1 parent 4336d3c commit 3f1f1c5

File tree

29 files changed

+282
-272
lines changed

29 files changed

+282
-272
lines changed

apps/backend/src/modules/activity-logs/activity-log.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,12 @@ class ActivityLogController extends BaseController {
216216

217217
return {
218218
payload: await this.activityLogService.findAll({
219-
contributorName,
220219
endDate,
221220
hasRootPermission,
222-
projectId,
223221
startDate,
224222
userProjectIds,
223+
...(contributorName ? { contributorName } : {}),
224+
...(projectId ? { projectId: Number(projectId) } : {}),
225225
}),
226226
status: HTTPCode.OK,
227227
};

apps/backend/src/modules/activity-logs/activity-log.service.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -188,18 +188,11 @@ class ActivityLogService implements Service {
188188
item.toObject(),
189189
);
190190

191-
const allContributors = await (projectId
192-
? this.contributorService.findAllByProjectId({
193-
contributorName: contributorName ?? "",
194-
hasHidden: false,
195-
permittedProjectIds,
196-
projectId: Number(projectId),
197-
})
198-
: this.contributorService.findAllWithoutPagination({
199-
contributorName: contributorName ?? "",
200-
hasHidden: false,
201-
permittedProjectIds,
202-
}));
191+
const allContributors = await this.contributorService.findAll({
192+
contributorName,
193+
permittedProjectIds,
194+
projectId,
195+
});
203196

204197
const dateRange = getDateRange(formattedStartDate, formattedEndDate);
205198

apps/backend/src/modules/contributors/contributor.controller.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ import {
1111
} from "~/libs/modules/controller/controller.js";
1212
import { HTTPCode } from "~/libs/modules/http/http.js";
1313
import { type Logger } from "~/libs/modules/logger/logger.js";
14-
import { type PaginationQueryParameters } from "~/libs/types/types.js";
1514

1615
import { type ContributorService } from "./contributor.service.js";
1716
import { ContributorsApiPath } from "./libs/enums/enums.js";
1817
import {
18+
type ContributorGetAllQueryParameters,
1919
type ContributorMergeRequestDto,
2020
type ContributorPatchRequestDto,
2121
type ContributorSplitRequestDto,
2222
} from "./libs/types/types.js";
2323
import {
24+
contributorGetAllValidationSchema,
2425
contributorMergeValidationSchema,
2526
contributorPatchValidationSchema,
2627
contributorSplitValidationSchema,
@@ -71,7 +72,7 @@ class ContributorController extends BaseController {
7172
handler: (options) =>
7273
this.findAll(
7374
options as APIHandlerOptions<{
74-
query: { projectId?: string } & PaginationQueryParameters;
75+
query: ContributorGetAllQueryParameters;
7576
}>,
7677
),
7778
method: "GET",
@@ -94,6 +95,9 @@ class ContributorController extends BaseController {
9495
),
9596
),
9697
],
98+
validation: {
99+
query: contributorGetAllValidationSchema,
100+
},
97101
});
98102

99103
this.addRoute({
@@ -156,18 +160,39 @@ class ContributorController extends BaseController {
156160
* schema:
157161
* type: integer
158162
* description: The page number to retrieve
163+
* required: false
159164
* - in: query
160165
* name: pageSize
161166
* schema:
162167
* type: integer
163168
* description: Number of items per page
169+
* required: false
164170
* - name: projectId
165171
* in: query
166172
* description: Id of a project contributor should belong to
167173
* required: false
168174
* schema:
169175
* type: number
170176
* minimum: 1
177+
* - name: hasHidden
178+
* in: query
179+
* description: Determines whether include all contributors or tracked only
180+
* required: false
181+
* schema:
182+
* type: boolean
183+
* - name: contributorName
184+
* in: query
185+
* description: Contributor name search query
186+
* required: false
187+
* schema:
188+
* type: string
189+
* - name: orderBy
190+
* in: query
191+
* description: Field by which to sort contributors
192+
* required: false
193+
* schema:
194+
* type: string
195+
* enum: [created_at, last_activity_date]
171196
* responses:
172197
* 200:
173198
* description: Successful operation
@@ -183,29 +208,23 @@ class ContributorController extends BaseController {
183208
*/
184209
private async findAll(
185210
options: APIHandlerOptions<{
186-
query: { projectId?: string } & PaginationQueryParameters;
211+
query: ContributorGetAllQueryParameters;
187212
}>,
188213
): Promise<APIHandlerResponse> {
189-
const { page, pageSize, projectId } = options.query;
190-
191-
if (projectId) {
192-
return {
193-
payload: await this.contributorService.findAllByProjectId({
194-
projectId: Number(projectId),
195-
}),
196-
status: HTTPCode.OK,
197-
};
198-
}
214+
const { contributorName, hasHidden, orderBy, page, pageSize, projectId } =
215+
options.query;
199216

200-
if (!page || !pageSize) {
201-
return {
202-
payload: await this.contributorService.findAllWithoutPagination({}),
203-
status: HTTPCode.OK,
204-
};
205-
}
217+
const query = {
218+
...(contributorName ? { contributorName } : {}),
219+
...(hasHidden ? { hasHidden: (hasHidden as unknown) === "true" } : {}),
220+
...(orderBy ? { orderBy } : {}),
221+
...(page ? { page: Number(page) } : {}),
222+
...(pageSize ? { pageSize: Number(pageSize) } : {}),
223+
...(projectId ? { projectId: Number(projectId) } : {}),
224+
};
206225

207226
return {
208-
payload: await this.contributorService.findAll(options.query),
227+
payload: await this.contributorService.findAll(query),
209228
status: HTTPCode.OK,
210229
};
211230
}

apps/backend/src/modules/contributors/contributor.repository.ts

Lines changed: 29 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { raw } from "objection";
22

3-
import { EMPTY_LENGTH } from "~/libs/constants/constants.js";
3+
import { EMPTY_LENGTH, PAGE_INDEX_OFFSET } from "~/libs/constants/constants.js";
44
import { SortType } from "~/libs/enums/enums.js";
55
import {
66
type PaginationResponseDto,
@@ -10,6 +10,7 @@ import { type GitEmailModel } from "~/modules/git-emails/git-emails.js";
1010

1111
import { ContributorEntity } from "./contributor.entity.js";
1212
import { type ContributorModel } from "./contributor.model.js";
13+
import { ContributorOrderByKey } from "./libs/enums/enums.js";
1314
import {
1415
type ContributorGetAllQueryParameters,
1516
type ContributorMergeRequestDto,
@@ -58,57 +59,17 @@ class ContributorRepository implements Repository {
5859

5960
public async findAll({
6061
contributorName,
61-
hasHidden = true,
62+
hasHidden = false,
63+
orderBy = ContributorOrderByKey.CREATED_AT,
6264
page,
6365
pageSize,
64-
}: { hasHidden?: boolean } & ContributorGetAllQueryParameters): Promise<
65-
PaginationResponseDto<ContributorEntity>
66-
> {
67-
const query = this.contributorModel
68-
.query()
69-
.orderBy("createdAt", SortType.DESCENDING)
70-
.page(page, pageSize)
71-
.select("contributors.*")
72-
.select(
73-
raw(
74-
"COALESCE(ARRAY_AGG(DISTINCT jsonb_build_object('id', projects.id, 'name', projects.name)) FILTER (WHERE projects.id IS NOT NULL), '{}') AS projects",
75-
),
76-
)
77-
.leftJoin("git_emails", "contributors.id", "git_emails.contributor_id")
78-
.leftJoin("activity_logs", "git_emails.id", "activity_logs.git_email_id")
79-
.leftJoin("projects", "activity_logs.project_id", "projects.id")
80-
.groupBy("contributors.id")
81-
.withGraphFetched("gitEmails");
82-
83-
if (!hasHidden) {
84-
query.whereNull("contributors.hiddenAt");
85-
}
86-
87-
if (contributorName) {
88-
query.whereILike("contributors.name", `%${contributorName}%`);
89-
}
90-
91-
const { results, total } = await query.execute();
92-
93-
return {
94-
items: results.map((contributor) =>
95-
ContributorEntity.initialize(contributor),
96-
),
97-
totalItems: total,
98-
};
99-
}
100-
101-
public async findAllByProjectId({
102-
contributorName,
103-
hasHidden = true,
104-
permittedProjectIds,
66+
permittedProjectIds = [],
10567
projectId,
10668
}: {
107-
contributorName?: string;
108-
hasHidden?: boolean;
10969
permittedProjectIds?: number[] | undefined;
110-
projectId: number;
111-
}): Promise<{ items: ContributorEntity[] }> {
70+
} & ContributorGetAllQueryParameters): Promise<
71+
PaginationResponseDto<ContributorEntity>
72+
> {
11273
const query = this.contributorModel
11374
.query()
11475
.select("contributors.*")
@@ -125,18 +86,8 @@ class ContributorRepository implements Repository {
12586
.leftJoin("git_emails", "contributors.id", "git_emails.contributor_id")
12687
.leftJoin("activity_logs", "git_emails.id", "activity_logs.git_email_id")
12788
.leftJoin("projects", "activity_logs.project_id", "projects.id")
128-
.where("projects.id", projectId)
129-
.whereNull("contributors.hiddenAt")
13089
.groupBy("contributors.id")
131-
.withGraphFetched("gitEmails")
132-
.orderBy("last_activity_date", SortType.DESCENDING);
133-
134-
const hasPermissionedProjects =
135-
permittedProjectIds && permittedProjectIds.length !== EMPTY_LENGTH;
136-
137-
if (hasPermissionedProjects) {
138-
query.whereIn("projects.id", permittedProjectIds);
139-
}
90+
.withGraphFetched("gitEmails");
14091

14192
if (!hasHidden) {
14293
query.whereNull("contributors.hiddenAt");
@@ -146,59 +97,37 @@ class ContributorRepository implements Repository {
14697
query.whereILike("contributors.name", `%${contributorName}%`);
14798
}
14899

149-
const contributorsWithProjectsAndEmails = await query.execute();
150-
151-
return {
152-
items: contributorsWithProjectsAndEmails.map((contributor) => {
153-
return ContributorEntity.initialize(contributor);
154-
}),
155-
};
156-
}
157-
158-
public async findAllWithoutPagination({
159-
contributorName,
160-
hasHidden = true,
161-
permittedProjectIds,
162-
}: {
163-
contributorName?: string;
164-
hasHidden?: boolean;
165-
permittedProjectIds: number[] | undefined;
166-
}): Promise<{ items: ContributorEntity[] }> {
167-
const query = this.contributorModel
168-
.query()
169-
.select("contributors.*")
170-
.select(
171-
raw(
172-
"COALESCE(ARRAY_AGG(DISTINCT jsonb_build_object('id', projects.id, 'name', projects.name)) FILTER (WHERE projects.id IS NOT NULL), '{}') AS projects",
173-
),
174-
)
175-
.leftJoin("git_emails", "contributors.id", "git_emails.contributor_id")
176-
.leftJoin("activity_logs", "git_emails.id", "activity_logs.git_email_id")
177-
.leftJoin("projects", "activity_logs.project_id", "projects.id")
178-
.groupBy("contributors.id")
179-
.withGraphFetched("gitEmails");
180-
181-
if (!hasHidden) {
182-
query.whereNull("contributors.hiddenAt");
183-
}
184-
185-
if (contributorName) {
186-
query.whereILike("contributors.name", `%${contributorName}%`);
100+
if (projectId) {
101+
query.havingRaw("?? = ANY(ARRAY_AGG(projects.id))", projectId);
187102
}
188103

189-
const hasPermissionedProjects =
190-
permittedProjectIds && permittedProjectIds.length !== EMPTY_LENGTH;
104+
const hasPermissionedProjects = permittedProjectIds.length !== EMPTY_LENGTH;
191105

192106
if (hasPermissionedProjects) {
193107
query.whereIn("projects.id", permittedProjectIds);
194108
}
195109

196-
const results = await query.execute();
110+
query.orderBy(orderBy, SortType.DESCENDING);
111+
112+
let contributors, totalItems;
113+
114+
if (page && pageSize) {
115+
const { results, total } = await query.page(
116+
page - PAGE_INDEX_OFFSET,
117+
pageSize,
118+
);
119+
contributors = results;
120+
totalItems = total;
121+
} else {
122+
contributors = await query;
123+
totalItems = contributors.length;
124+
}
197125

198126
return {
199-
items: results.map((contributor) => {
127+
items: contributors.map((contributor) => {
200128
return ContributorEntity.initialize(contributor);
201129
}),
130+
totalItems,
202131
};
203132
}
204133

0 commit comments

Comments
 (0)