feat: resolveproject group in the segments middleware (CM-189)#4011
feat: resolveproject group in the segments middleware (CM-189)#4011
Conversation
|
|
There was a problem hiding this comment.
Pull request overview
This PR updates segment scoping so the frontend can send project group (non-leaf) segment IDs, and the backend middleware will resolve them into leaf sub-project segments for downstream queries.
Changes:
- Backend: enhance
segmentMiddlewareto expand non-leaf segment IDs into active leaf sub-project segments. - Frontend: stop expanding project groups into subproject segments in a few call sites and instead pass
[projectGroup.id]. - Frontend: adjust organization merge suggestions calls to use the selected project group ID.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/modules/organization/services/organization.api.service.ts | Uses selected project group ID(s) for merge suggestions / create payloads. |
| frontend/src/modules/organization/organization-service.js | Sends only selected project group ID for merge suggestions. |
| frontend/src/modules/lf/layout/components/lf-banners.vue | Integration listing now scoped by [projectGroup.id]. |
| frontend/src/modules/activity/components/activity-timeline.vue | Activity query now scoped by selected project group ID when no explicit segment is selected. |
| backend/src/middlewares/segmentMiddleware.ts | Resolves provided segment IDs to leaf sub-project segments before attaching req.currentSegments. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
765829e to
9d33a06
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ee72d68 to
5faf875
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (querySegments.length > 0) { | ||
| segments = { | ||
| rows: await resolveToLeafSegments(segmentRepository, querySegments, req), | ||
| } | ||
| } else if (bodySegments.length > 0) { | ||
| segments = { | ||
| rows: await resolveToLeafSegments(segmentRepository, bodySegments, req), | ||
| } | ||
| } else { | ||
| segments = await segmentRepository.querySubprojects({ limit: 1, offset: 0 }) | ||
| } | ||
|
|
||
| req.currentSegments = segments.rows | ||
| const options = req as unknown as IRepositoryOptions | ||
| options.currentSegments = segments.rows |
There was a problem hiding this comment.
If getSegmentSubprojects() resolves to an empty list (e.g. a project group with no active subprojects, or an invalid segment ID), the middleware sets currentSegments to []. Downstream DAL calls (e.g. queryActivities) explicitly throw when segmentIds is empty, which would turn a client scoping issue into a 500. Consider failing fast with a 400 when the request provided segments but they resolve to none, or falling back to the original fetched segments to keep currentSegments non-empty.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
9a013b6 to
e58e568
Compare
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
frontend/src/modules/organization/pages/organization-list-page.vue:153
organizationsQueryKeyno longer includesfilters(or any derived filter state), butqueryFnstill depends onfilters.valueviabuildApiFilter(...). With @tanstack/vue-query, changes tofilterswon’t trigger a refetch if the query key is unchanged, so the list can become stale when users adjust filters. Include a stable serialization of the active filters (or the transformed filter/orderBy) in the query key, or explicitly invalidate/refetch on filter changes (e.g., via a watch) to keep results consistent.
// Create a computed query key for organizations
const organizationsQueryKey = computed(() => [
TanstackKey.ORGANIZATIONS_LIST,
selectedProjectGroup.value?.id,
queryParams.value.search,
queryParams.value.offset,
queryParams.value.limit,
queryParams.value.orderBy,
queryParams.value.segments,
]);
frontend/src/modules/member/pages/member-list-page.vue:156
membersQueryKeyno longer includesfilters/customAttributesFilter, but thequeryFnstill builds the API filter from these reactive values. As a result, changing filters can leave the cached query “fresh” and prevent refetching, showing stale data. Add the relevant filter state (or its stable serialized form) tomembersQueryKey, or invalidate/refetch the query when filters change.
// Create a computed query key for members
const membersQueryKey = computed(() => [
TanstackKey.MEMBERS_LIST,
selectedProjectGroup.value?.id,
queryParams.value.search,
queryParams.value.offset,
queryParams.value.limit,
queryParams.value.orderBy,
queryParams.value.segments,
]);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
frontend/src/modules/organization/pages/organization-list-page.vue:153
organizationsQueryKeyno longer includes the active filter state (and the prior deepwatch(filters)reset was removed). Because TanStack Query only re-fetches onqueryKeychanges/invalidations, changing filters can leave the list showing stale results (especially when offset/search/orderBy don't change). Include a stable filter representation in the key (e.g. transformed filter + orderBy) or explicitlyinvalidateQueries/refetchwhenfilterschanges.
const organizationsQueryKey = computed(() => [
TanstackKey.ORGANIZATIONS_LIST,
selectedProjectGroup.value?.id,
queryParams.value.search,
queryParams.value.offset,
queryParams.value.limit,
queryParams.value.orderBy,
queryParams.value.segments,
]);
frontend/src/modules/member/pages/member-list-page.vue:156
membersQueryKeyno longer includes the active filter state (and the previous deepwatch(filters)reset was removed). With TanStack Query, filter changes won’t trigger a re-fetch unless thequeryKeychanges or you invalidate/refetch manually, so the members list can become stale. Add a stable filter representation to the key or invalidate/refetch onfiltersupdates.
// Create a computed query key for members
const membersQueryKey = computed(() => [
TanstackKey.MEMBERS_LIST,
selectedProjectGroup.value?.id,
queryParams.value.search,
queryParams.value.offset,
queryParams.value.limit,
queryParams.value.orderBy,
queryParams.value.segments,
]);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const body = req.body as Record<string, unknown> | ||
| body.segments = resolvedRows.map((s: any) => s.id) |
There was a problem hiding this comment.
segmentMiddleware rewrites req.body.segments to the resolved leaf sub-project IDs. This is a behavioral breaking change for many POST/PUT handlers/services that read req.body.segments[0] (or pass req.body.segments through) expecting the original segment level (e.g. project-group ID for list queries / exports). Instead of mutating the request body, keep the original segments intact and rely on options.currentSegments for leaf resolution (or store the resolved IDs in a separate field).
| const body = req.body as Record<string, unknown> | |
| body.segments = resolvedRows.map((s: any) => s.id) |
| const leafRecords = await segmentRepository.getSegmentSubprojects(segmentIds) | ||
|
|
There was a problem hiding this comment.
resolveToLeafSegments relies on segmentRepository.getSegmentSubprojects(segmentIds). That repository query currently does not constrain the final segments s selection by tenantId (it only filters input_segment), so slug collisions across tenants could cause cross-tenant segment expansion. Since this middleware is now the central expansion path, it would be safer to ensure getSegmentSubprojects filters s."tenantId" = :tenantId in its final WHERE clause.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a775562. Configure here.
| 'Non-leaf segments resolved to leaf sub-projects', | ||
| ) | ||
|
|
||
| return leafRecords.map(populateSegmentRelations) |
There was a problem hiding this comment.
Inconsistent populateSegmentRelations across resolution paths
Low Severity
resolveToLeafSegments returns raw DB rows when all input segments are already leaf (line 117), but applies populateSegmentRelations when any non-leaf segment triggers the expansion path (line 132). This means currentSegments objects will have an activityTypes property in one code path but not the other, producing inconsistent shapes depending on the caller's input.
Reviewed by Cursor Bugbot for commit a775562. Configure here.


Note
Medium Risk
Changes request scoping and permission-check inputs across many endpoints by altering how
segmentsare interpreted and expanded, so mistakes could lead to missing/extra data exposure within a tenant or unexpected query results.Overview
Backend now resolves non-leaf segment IDs (project groups/projects) into leaf sub-project segments for querying and permission checks. A new route-level
segmentByIdMiddlewareis added toGET/PUT /segment/:segmentIdto ensurecurrentSegmentsmatches the resource being accessed, and the globalsegmentMiddlewareis hardened to safely parsesegmentsand expand non-leaf IDs viagetSegmentSubprojects.Frontend stops expanding project groups into subproject IDs and instead sends only the selected project group id (or an empty list) across multiple flows (activities, integrations fetch, member/org lists and merge-suggestions, banners, axios interceptor), relying on the backend to resolve to leaf segments. Member/organization list pages also refactor query/filter initialization to store the already-built API filter/orderBy in
queryParamsfor more consistent caching.Separately,
createCollectionin the data-access layer now treatsslug/descriptionas nullable and explicitly normalizes optional fields tonullon insert.Reviewed by Cursor Bugbot for commit a775562. Bugbot is set up for automated code reviews on this repo. Configure here.