Skip to content

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

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

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

Conversation

@ulemons
Copy link
Copy Markdown
Contributor

@ulemons ulemons commented Apr 9, 2026

Note

Medium Risk
Changes how segments are derived and applied for most API requests, which can affect data scoping/permissions and query results; also tweaks frontend caching keys for list pages.

Overview
Centralizes segment expansion on the backend: the global segmentMiddleware now safely parses segments from query/body and resolves any project group/project IDs into their leaf sub-projects (with debug logging), and a new route-level segmentByIdMiddleware is added to GET/PUT /segment/:segmentId to ensure permission checks use the target segment.

Updates the frontend to stop pre-expanding segment trees (getSegmentsFromProjectGroup) and instead send only the selected project-group id (or []) across activities, integrations fetches, merge suggestions, and the global auth-axios interceptor; data-quality merge suggestion components likewise scope requests to [projectGroup] only. Also hardens activity timeline timestamp initialization for invalid joinedAt, and adjusts member/org list query cache keys by removing filters-based invalidation.

In the data-access layer, collection creation now treats description/slug as nullable and explicitly normalizes optional fields to null when inserting.

Reviewed by Cursor Bugbot for commit 4263dbf. 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

req.currentSegments = segments.rows
const options = req as unknown as IRepositoryOptions
options.currentSegments = segments.rows
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Endpoints reading req.body.segments get unresolved project group IDs

High Severity

The frontend auth-axios interceptor now always sends [projectGroupId] as the segments fallback for all request types. The new backend middleware resolves this to leaf sub-projects in options.currentSegments, but does not modify req.body.segments. Multiple endpoints read req.body.segments directly — e.g., linkedinConnect.ts, redditOnboard.ts, organizationMerge.ts, memberExport.ts, and organizationExport.ts — and will now receive a project-group ID where they expect a leaf sub-project ID. Previously, getSegmentsFromProjectGroup expanded to sub-project IDs on the frontend for non-GET requests, so these endpoints worked correctly.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e58e568. Configure here.

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 on lines +119 to +132
const leafRecords = await segmentRepository.getSegmentSubprojects(segmentIds)

log.debug(
{
api: `${req.method} ${req.path}`,
input_segments: nonLeaf.map((s) => ({ id: s.id, name: s.name, level: segmentLevel(s) })),
resolved_leaf_segments: leafRecords.map((s: any) => ({ id: s.id, name: (s as any).name })),
resolved_count: leafRecords.length,
},
'Non-leaf segments resolved to leaf sub-projects',
)

return leafRecords.map(populateSegmentRelations)
}
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.

resolveToLeafSegments can introduce duplicate leaf segments when the input contains overlapping IDs (e.g. a projectGroup ID plus one of its subproject IDs). getSegmentSubprojects doesn't DISTINCT/dedupe, so currentSegments can end up with repeated IDs, which can break code that expects uniqueness (and may trip single-segment assertions). Consider deduplicating leafRecords by id (or adding DISTINCT/GROUP BY in the query) before returning.

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.

There are 2 total unresolved issues (including 1 from previous review).

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 f90e081. Configure here.

TanstackKey.MEMBERS_LIST,
selectedProjectGroup.value?.id,
queryParams.value.search,
filters.value, // Use filters.value directly to make it reactive
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Filters removed from query key break list filtering

High Severity

filters.value was removed from membersQueryKey (and the equivalent organizationsQueryKey), but the queryFn still reads filters.value to build the API call. When a user changes only a filter (without changing search, sort, pagination, or segments), none of the remaining query key fields change, so TanStack Query returns cached stale data instead of refetching. The previously existing watch on filters that reset pagination was also removed, compounding the issue.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f90e081. Configure here.

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 on lines +73 to +81
/**
* Safely extracts a string[] from an unknown query/body value.
* Rejects ParsedQs objects (e.g. ?segments[key]=val) that would cause type confusion.
*/
function toStringArray(value: unknown): string[] {
if (value === undefined || value === null) return []
const items = Array.isArray(value) ? value : [value]
return items.filter((v): v is string => typeof v === 'string')
}
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.

The JSDoc for toStringArray says it “Rejects ParsedQs objects”, but the implementation silently filters non-strings and returns [] (which is not an explicit rejection and can change behavior by falling back to the default segment). Either update the comment to reflect the actual behavior, or explicitly reject non-string values (e.g., by throwing a 400) when segments is present but contains non-string entries.

Copilot uses AI. Check for mistakes.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
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