Skip to content

feat: resolveproject group in the segments middleware (CM-189)#4011

Open
ulemons wants to merge 15 commits intomainfrom
feat/standardize-segments-api
Open

feat: resolveproject group in the segments middleware (CM-189)#4011
ulemons wants to merge 15 commits intomainfrom
feat/standardize-segments-api

Conversation

@ulemons
Copy link
Copy Markdown
Contributor

@ulemons ulemons commented Apr 9, 2026

Note

Medium Risk
Changes request scoping and permission-check inputs across many endpoints by altering how segments are 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 segmentByIdMiddleware is added to GET/PUT /segment/:segmentId to ensure currentSegments matches the resource being accessed, and the global segmentMiddleware is hardened to safely parse segments and expand non-leaf IDs via getSegmentSubprojects.

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 queryParams for more consistent caching.

Separately, createCollection in the data-access layer now treats slug/description as nullable and explicitly normalizes optional fields to null on insert.

Reviewed by Cursor Bugbot for commit a775562. Bugbot is set up for automated code reviews on this repo. Configure here.

@ulemons ulemons self-assigned this Apr 9, 2026
@ulemons ulemons added the Feature Created by Linear-GitHub Sync label Apr 9, 2026
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Comment thread backend/src/middlewares/segmentMiddleware.ts Fixed
Copilot AI review requested due to automatic review settings April 17, 2026 07:20
@ulemons ulemons marked this pull request as ready for review April 17, 2026 07:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 segmentMiddleware to 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.

Comment thread frontend/src/modules/activity/components/activity-timeline.vue Outdated
Comment thread backend/src/middlewares/segmentMiddleware.ts Outdated
Comment thread backend/src/middlewares/segmentMiddleware.ts Outdated
Comment thread frontend/src/modules/organization/organization-service.js
Comment thread frontend/src/modules/activity/components/activity-timeline.vue Outdated
@ulemons ulemons force-pushed the feat/standardize-segments-api branch from 765829e to 9d33a06 Compare April 17, 2026 09:32
Copilot AI review requested due to automatic review settings April 17, 2026 09:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread services/libs/data-access-layer/src/collections/index.ts
Comment thread backend/src/middlewares/segmentMiddleware.ts
Copilot AI review requested due to automatic review settings April 17, 2026 09:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread backend/src/middlewares/segmentMiddleware.ts
Comment thread backend/src/middlewares/segmentMiddleware.ts
Comment thread frontend/src/modules/organization/organization-service.js
Comment thread backend/src/middlewares/segmentMiddleware.ts
@ulemons ulemons force-pushed the feat/standardize-segments-api branch from ee72d68 to 5faf875 Compare April 17, 2026 12:14
Copilot AI review requested due to automatic review settings April 17, 2026 12:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +28 to +41
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
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread backend/src/middlewares/segmentMiddleware.ts
Comment thread backend/src/middlewares/segmentMiddleware.ts
ulemons and others added 7 commits April 17, 2026 15:03
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>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
@ulemons ulemons force-pushed the feat/standardize-segments-api branch from 9a013b6 to e58e568 Compare April 17, 2026 13:03
Comment thread backend/src/middlewares/segmentMiddleware.ts
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings April 17, 2026 14:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread backend/src/middlewares/segmentMiddleware.ts
Comment thread backend/src/middlewares/segmentMiddleware.ts
ulemons added 2 commits April 17, 2026 17:10
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings April 17, 2026 15:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread backend/src/middlewares/segmentMiddleware.ts
Comment thread backend/src/middlewares/segmentMiddleware.ts
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Comment thread frontend/src/modules/member/pages/member-list-page.vue
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings April 17, 2026 16:25
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • organizationsQueryKey no longer includes filters (or any derived filter state), but queryFn still depends on filters.value via buildApiFilter(...). With @tanstack/vue-query, changes to filters won’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

  • membersQueryKey no longer includes filters / customAttributesFilter, but the queryFn still 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) to membersQueryKey, 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.

Comment thread backend/src/middlewares/segmentMiddleware.ts
ulemons added 2 commits April 17, 2026 18:32
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copilot AI review requested due to automatic review settings April 20, 2026 08:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • organizationsQueryKey no longer includes the active filter state (and the prior deep watch(filters) reset was removed). Because TanStack Query only re-fetches on queryKey changes/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 explicitly invalidateQueries/refetch when filters changes.
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

  • membersQueryKey no longer includes the active filter state (and the previous deep watch(filters) reset was removed). With TanStack Query, filter changes won’t trigger a re-fetch unless the queryKey changes or you invalidate/refetch manually, so the members list can become stale. Add a stable filter representation to the key or invalidate/refetch on filters updates.
// 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.

Comment on lines +59 to +60
const body = req.body as Record<string, unknown>
body.segments = resolvedRows.map((s: any) => s.id)
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
const body = req.body as Record<string, unknown>
body.segments = resolvedRows.map((s: any) => s.id)

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +121
const leafRecords = await segmentRepository.getSegmentSubprojects(segmentIds)

Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a775562. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Created by Linear-GitHub Sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants