这是indexloc提供的服务,不要输入任何密码
Skip to content

feat: add list objects intersection exclusion #2543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jul 16, 2025

Conversation

Vic-Dev
Copy link
Contributor

@Vic-Dev Vic-Dev commented Jul 4, 2025

Description

Add intersection and exclusion handling to reverse expand weighted, where check calls are made at a per operator level rather than higher up in the model.

References

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

  • New Features

    • Enhanced reverse expansion logic to support intersection and exclusion operators, improving handling of complex authorization scenarios.
    • Expanded test coverage for reverse expansion, including unions, intersections, exclusions, recursion, cycles, and error cases.
  • Bug Fixes

    • Corrected userset construction logic for tuple-to-userset edges to align with model definitions.
  • Refactor

    • Centralized edge and node retrieval logic in the type system for improved maintainability.
    • Updated interfaces and mock implementations to support new reverse expansion features.
  • Documentation

    • Updated changelog to document changes to reverse expansion functionality.
  • Tests

    • Added comprehensive tests for new stack operations, error handling, and weighted graph traversal.

Copy link
Contributor

coderabbitai bot commented Jul 4, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This change refactors and extends the ReverseExpand functionality to support intersection and exclusion operators using weighted graph edges. It introduces new handler methods, updates interface types, adds stack manipulation helpers, and expands test coverage. Additionally, it centralizes edge retrieval logic in the typesystem and updates mock and interface definitions accordingly.

Changes

File(s) Change Summary
CHANGELOG.md Added changelog entry documenting ReverseExpand update for intersection/exclusion handler fast path check calls.
internal/graph/check.go Renamed checkRewrite to CheckRewrite and updated call sites, making it exported.
internal/graph/interface.go Added new CheckRewriteResolver interface with CheckRewrite method.
internal/graph/mock_check_resolver.go Added mock implementation for CheckRewriteResolver interface and related methods.
internal/graph/check_test.go Removed trailing blank line in a test; no functional changes.
pkg/server/commands/reverseexpand/reverse_expand.go Added stack cloning helpers, updated interface usage, added shallow clone method, wrapped context, updated edge traversal call signature.
pkg/server/commands/reverseexpand/reverse_expand_weighted.go Added intersection/exclusion handlers, refactored edge traversal, added stack utilities, updated concurrency and error handling.
pkg/server/commands/reverseexpand/reverse_expand_weighted_test.go Expanded test suite for intersections, exclusions, recursion, cycles, error handling, and stack utilities.
pkg/typesystem/typesystem.go Added GetEdgesFromWeightedGraph helper, refactored GetEdgesForListObjects to use it.
pkg/typesystem/typesystem_test.go Added tests for GetEdgesFromWeightedGraph error handling.
pkg/typesystem/weighted_graph.go Adjusted parent extraction logic in ConstructUserset for TTUEdge case.
pkg/typesystem/weighted_graph_test.go Updated expected userset in TTUEdge test case to match new parent extraction logic.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ReverseExpandQuery
    participant TypeSystem
    participant CheckRewriteResolver
    participant DataStore

    Client->>ReverseExpandQuery: Execute(request)
    ReverseExpandQuery->>TypeSystem: GetEdgesFromWeightedGraph(target, source)
    TypeSystem-->>ReverseExpandQuery: edges, node
    alt Operator is Intersection
        ReverseExpandQuery->>ReverseExpandQuery: intersectionHandler(...)
        ReverseExpandQuery->>TypeSystem: ConstructUserset(...)
        ReverseExpandQuery->>CheckRewriteResolver: CheckRewrite(ctx, req, userset)
        CheckRewriteResolver-->>ReverseExpandQuery: handler result
    else Operator is Exclusion
        ReverseExpandQuery->>ReverseExpandQuery: exclusionHandler(...)
        ReverseExpandQuery->>TypeSystem: ConstructUserset(...)
        ReverseExpandQuery->>CheckRewriteResolver: CheckRewrite(ctx, req, userset)
        CheckRewriteResolver-->>ReverseExpandQuery: handler result
    else Other
        ReverseExpandQuery->>DataStore: ReadTuples(...)
        DataStore-->>ReverseExpandQuery: tuples
    end
    ReverseExpandQuery-->>Client: ReverseExpandResult(s)
Loading

Possibly related PRs

  • openfga/openfga#2518: Introduces foundational weighted graph edge handling for intersection and exclusion in the typesystem, which this PR builds upon.
  • openfga/openfga#2529: Implements the core weighted graph ReverseExpand traversal, directly extended by this PR's intersection/exclusion handling.
  • openfga/openfga#2468: Adds the initial GetEdgesForListObjects method, which is refactored and built upon in this PR.

Suggested labels

Skip-Changelog

Suggested reviewers

  • justincoh
  • adriantam

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Jul 7, 2025

Codecov Report

Attention: Patch coverage is 84.16290% with 35 lines in your changes missing coverage. Please review.

Project coverage is 90.38%. Comparing base (6a81236) to head (3560618).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../commands/reverseexpand/reverse_expand_weighted.go 86.96% 20 Missing and 4 partials ⚠️
pkg/typesystem/typesystem.go 70.00% 4 Missing and 2 partials ⚠️
...kg/server/commands/reverseexpand/reverse_expand.go 61.54% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2543      +/-   ##
==========================================
- Coverage   90.46%   90.38%   -0.08%     
==========================================
  Files         140      140              
  Lines       19367    19565     +198     
==========================================
+ Hits        17518    17681     +163     
- Misses       1407     1436      +29     
- Partials      442      448       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Vic-Dev Vic-Dev changed the title Feat/add list objects intersection exclusion feat: add list objects intersection exclusion Jul 8, 2025
@Vic-Dev Vic-Dev marked this pull request as ready for review July 8, 2025 18:22
@Vic-Dev Vic-Dev requested a review from a team as a code owner July 8, 2025 18:22
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (9)
pkg/typesystem/typesystem.go (2)

1719-1722: Consider documenting the nil return behavior more explicitly.

When there's no path to the source type, the method returns (nil, nil, nil) which could be ambiguous. Consider either:

  1. Adding a comment explaining this is expected behavior when no path exists, or
  2. Returning a more explicit indicator (e.g., a named error like ErrNoPathToSource)
 	// This means we cannot reach the source type requested, so there are no relevant edges.
 	if !hasPathTo(currentNode, sourceType) {
+		// Return nil values to indicate no relevant edges exist (not an error condition)
 		return nil, nil, nil
 	}

1758-1763: Redundant nil check for currentNode.

The check if currentNode == nil appears redundant because:

  1. The helper method already validates node existence (line 1714-1717)
  2. If the helper returns nil for currentNode without an error, it means there's no path to the source type
  3. In this case, edges would also be nil, making the subsequent check unnecessary

Consider simplifying the error handling:

 	edges, currentNode, err := t.GetEdgesFromWeightedGraph(targetTypeRelation, sourceType)
 	if err != nil {
 		return nil, false, err
 	}
-	if currentNode == nil {
-		return nil, false, fmt.Errorf("could not find node with label: %s", targetTypeRelation)
-	}
-	if len(edges) == 0 {
-		return nil, false, fmt.Errorf("no outgoing edges from node: %s", currentNode.GetUniqueLabel())
+	if edges == nil {
+		// No path exists from the target to the source type
+		return nil, false, nil
 	}
pkg/server/commands/reverseexpand/reverse_expand_weighted_test.go (7)

1703-1768: Fix variable naming to follow Go conventions.

The test logic is correct, but variable names should use camelCase instead of underscores.

-func TestLoopOverEdges(t *testing.T) {
-	t.Run("returns_error_when_cannot_get_edges_from_intersection", func(t *testing.T) {
-		broken_model := `
+func TestLoopOverEdges(t *testing.T) {
+	t.Run("returns_error_when_cannot_get_edges_from_intersection", func(t *testing.T) {
+		brokenModel := `
 			model
 				schema 1.1
 			  type user

 			  type document
 				relations
 				  define viewer: [user2]
 				  define editor: [user]
 				  define admin: viewer and editor
 		`
-		working_model := `
+		workingModel := `
 			model
 				schema 1.1
 			  type user

 			  type document
 				relations
 				  define viewer: [user]
 				  define editor: [user]
 				  define admin: viewer and editor
 		`

Also update the usage of these variables:

-		storeID, authModel := storagetest.BootstrapFGAStore(t, ds, broken_model, tuples)
+		storeID, authModel := storagetest.BootstrapFGAStore(t, ds, brokenModel, tuples)
-			testutils.MustTransformDSLToProtoWithID(working_model),
+			testutils.MustTransformDSLToProtoWithID(workingModel),

1770-1835: Apply consistent variable naming in exclusion test.

-	t.Run("returns_error_when_cannot_get_edges_from_exclusion", func(t *testing.T) {
-		broken_model := `
+	t.Run("returns_error_when_cannot_get_edges_from_exclusion", func(t *testing.T) {
+		brokenModel := `
 			model
 				schema 1.1
 			  type user

 			  type document
 				relations
 				  define viewer: [user2]
 				  define editor: [user]
 				  define admin: viewer but not editor
 		`
-		working_model := `
+		workingModel := `
 			model
 				schema 1.1
 			  type user

 			  type document
 				relations
 				  define viewer: [user]
 				  define editor: [user]
 				  define admin: viewer but not editor
 		`

1837-1901: Fix variable naming conventions in intersection handler test.

-func TestIntersectionHandler(t *testing.T) {
-	t.Run("return_error_when_GetEdgesForIntersection_errors", func(t *testing.T) {
-		broken_model := `
+func TestIntersectionHandler(t *testing.T) {
+	t.Run("return_error_when_GetEdgesForIntersection_errors", func(t *testing.T) {
+		brokenModel := `
 			model
 				schema 1.1
 			  type user

 			  type document
 				relations
 				  define viewer: [user2]
 				  define editor: [user]
 				  define admin: viewer and editor
 		`
-		working_model := `
+		workingModel := `
 			model
 				schema 1.1
 			  type user

 			  type document
 				relations
 				  define viewer: [user]
 				  define editor: [user]
 				  define admin: viewer and editor
 		`

1974-2037: Fix variable naming convention for storeId.

-		storeId, authModel := storagetest.BootstrapFGAStore(t, ds, model, tuples)
+		storeID, authModel := storagetest.BootstrapFGAStore(t, ds, model, tuples)

Also update all usages:

-			StoreID:       storeId,
+			StoreID:       storeID,

2039-2102: Fix storeId variable naming.

-		storeId, authModel := storagetest.BootstrapFGAStore(t, mockDatastore, model, tuples)
+		storeID, authModel := storagetest.BootstrapFGAStore(t, mockDatastore, model, tuples)

And update usage:

-			StoreID:       storeId,
+			StoreID:       storeID,

2106-2169: Apply consistent variable naming in exclusion handler tests.

Similar to the intersection handler tests, fix the variable naming:

-		broken_model := `
+		brokenModel := `
-		working_model := `
+		workingModel := `

2172-2299: Fix remaining variable naming issues.

-		storeId, authModel := storagetest.BootstrapFGAStore(t, ds, model, tuples)
+		storeID, authModel := storagetest.BootstrapFGAStore(t, ds, model, tuples)

Also in the mock test:

-		storeId, authModel := storagetest.BootstrapFGAStore(t, mockDatastore, model, tuples)
+		storeID, authModel := storagetest.BootstrapFGAStore(t, mockDatastore, model, tuples)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db462b6 and c764f20.

📒 Files selected for processing (12)
  • CHANGELOG.md (1 hunks)
  • internal/graph/check.go (3 hunks)
  • internal/graph/check_test.go (0 hunks)
  • internal/graph/interface.go (2 hunks)
  • internal/graph/mock_check_resolver.go (3 hunks)
  • pkg/server/commands/reverseexpand/reverse_expand.go (5 hunks)
  • pkg/server/commands/reverseexpand/reverse_expand_weighted.go (7 hunks)
  • pkg/server/commands/reverseexpand/reverse_expand_weighted_test.go (12 hunks)
  • pkg/typesystem/typesystem.go (2 hunks)
  • pkg/typesystem/typesystem_test.go (1 hunks)
  • pkg/typesystem/weighted_graph.go (1 hunks)
  • pkg/typesystem/weighted_graph_test.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/graph/check_test.go
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
pkg/typesystem/weighted_graph_test.go (2)
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
pkg/typesystem/weighted_graph.go (2)
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
pkg/typesystem/typesystem_test.go (2)

undefined

<retrieved_learning>
Learnt from: adriantam
PR: #2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
</retrieved_learning>

<retrieved_learning>
Learnt from: adriantam
PR: #2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
</retrieved_learning>

pkg/typesystem/typesystem.go (2)
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
pkg/server/commands/reverseexpand/reverse_expand_weighted_test.go (1)
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
pkg/server/commands/reverseexpand/reverse_expand_weighted.go (2)
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
pkg/server/commands/reverseexpand/reverse_expand.go (2)
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:130-134
Timestamp: 2025-06-20T18:39:47.718Z
Learning: In the OpenFGA typesystem weighted graph implementation, when processing edges for intersection operations, the system is designed to gracefully handle disconnected edges by skipping them (returning empty IntersectionEdges{} with nil error) rather than returning explicit errors. This allows list objects to continue operating as a no-op when encountering unexpected edge conditions, supporting graceful degradation of the authorization system.
Learnt from: adriantam
PR: openfga/openfga#2518
File: pkg/typesystem/weighted_graph.go:99-104
Timestamp: 2025-06-20T18:39:39.926Z
Learning: In the OpenFGA typesystem's GetEdgesForIntersection method, when direct edges are disconnected (lowestWeight == 0 && edgeNum != 0), the method should return empty IntersectionEdges{} rather than an error. This allows list objects to skip over the edge and become a no-op, providing graceful handling of disconnected edges instead of failing the entire operation.
🧬 Code Graph Analysis (1)
pkg/typesystem/weighted_graph.go (1)
pkg/tuple/tuple.go (1)
  • SplitObjectRelation (305-314)
🪛 GitHub Actions: Update unreleased section in CHANGELOG
CHANGELOG.md

[error] 1-1: Please run npx keep-a-changelog to fix changelog.

🪛 GitHub Check: lint
pkg/server/commands/reverseexpand/reverse_expand_weighted_test.go

[failure] 1952-1952:
go-require: require must only be used in the goroutine running the test function (testifylint)


[failure] 2193-2193:
ST1003: var storeId should be storeID (staticcheck)


[failure] 2067-2067:
ST1003: var storeId should be storeID (staticcheck)


[failure] 1996-1996:
ST1003: var storeId should be storeID (staticcheck)


[failure] 1850-1850:
ST1003: should not use underscores in Go names; var working_model should be workingModel (staticcheck)


[failure] 1839-1839:
ST1003: should not use underscores in Go names; var broken_model should be brokenModel (staticcheck)


[failure] 1782-1782:
ST1003: should not use underscores in Go names; var working_model should be workingModel (staticcheck)


[failure] 1771-1771:
ST1003: should not use underscores in Go names; var broken_model should be brokenModel (staticcheck)


[failure] 1716-1716:
ST1003: should not use underscores in Go names; var working_model should be workingModel (staticcheck)


[failure] 1705-1705:
ST1003: should not use underscores in Go names; var broken_model should be brokenModel (staticcheck)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: docker-tests
  • GitHub Check: tests
  • GitHub Check: Dry Run GoReleaser
  • GitHub Check: go-bench
  • GitHub Check: Analyze (go)
🔇 Additional comments (26)
pkg/typesystem/weighted_graph.go (1)

176-177: LGTM: Logical correction for TTU edge parent extraction.

This change correctly modifies the source of parent extraction for TTUEdge cases from the node's uniqueLabel to the edge's tupleset relation. This is a more semantically accurate approach for tuple-to-userset edges and aligns with the enhanced intersection and exclusion operator handling mentioned in the PR objectives.

The change ensures that the correct parent object is used when constructing usersets for TTU edges, which is important for proper authorization model evaluation.

CHANGELOG.md (1)

13-15: CHANGELOG format fixed and ready to merge

The changelog entry now adheres to the required format after running npx keep-a-changelog. No further changes are needed.

pkg/typesystem/weighted_graph_test.go (1)

703-703: LGTM: Test assertion correctly aligned with model definition.

The change from "team" to "parent" correctly aligns the test expectation with the model definition where parent: [team] is defined on line 677. This ensures the test validates the actual behavior of the ConstructUserset method.

internal/graph/interface.go (2)

5-9: LGTM: Clean import addition.

The addition of the openfgav1 import is necessary for the new interface method signature and follows Go import conventions.


43-47: LGTM: Well-designed interface extension.

The CheckRewriteResolver interface properly extends CheckResolver by embedding it and adding the CheckRewrite method. The method signature is consistent with the expected usage pattern and provides a clean abstraction for the functionality.

internal/graph/check.go (3)

532-532: LGTM: Consistent method call update.

The call to CheckRewrite (formerly checkRewrite) is correctly updated to use the new exported method name.


1545-1545: LGTM: Consistent method call update.

The call to CheckRewrite in the checkSetOperation method is correctly updated to use the new exported method name.


1569-1592: LGTM: Proper method export implementation.

The method was correctly renamed from checkRewrite to CheckRewrite to make it exported while maintaining the same signature and implementation. This change enables the method to satisfy the new CheckRewriteResolver interface while preserving existing functionality.

pkg/typesystem/typesystem_test.go (1)

7064-7097: Well-structured test coverage for edge retrieval functionality.

The test function provides good coverage of error conditions for the GetEdgesFromWeightedGraph method. The test structure follows established patterns in the codebase with clear subtests for different scenarios:

  1. Nil weighted graph handling
  2. Non-existent node handling

This aligns well with the PR objective to enhance intersection and exclusion handling in reverse expand functionality.

pkg/typesystem/typesystem.go (1)

1703-1730: LGTM! Well-structured helper method for edge retrieval.

The extraction of this logic into a separate helper method improves code reusability and maintainability. The method properly validates the weighted graph existence, node presence, and path connectivity before returning edges.

pkg/server/commands/reverseexpand/reverse_expand.go (5)

70-84: LGTM! Useful method for stack manipulation in intersection/exclusion handling.

The cloneWithStack method provides a clean way to create request copies with different relation stacks, which is essential for the new intersection and exclusion operator handling.


166-166: Good use of interface for improved flexibility.

Changing localCheckResolver from a concrete type to the graph.CheckRewriteResolver interface improves the design by following the Dependency Inversion Principle. This enables different implementations for handling the new intersection and exclusion operators.


267-280: Well-documented shallow clone implementation.

The shallowClone method is appropriately designed with a fresh candidateObjectsMap while sharing other fields. The comment clearly explains the rationale for this design choice.


300-300: Good practice: Context-based dependency injection.

Wrapping the context with the relationship tuple reader ensures the datastore is available throughout the execution flow without passing it as a parameter everywhere.


441-441: Correct parameter addition for weighted graph traversal.

The addition of sourceUserType parameter aligns with the enhanced loopOverEdges functionality that handles intersection and exclusion operators in the weighted graph.

pkg/server/commands/reverseexpand/reverse_expand_weighted_test.go (5)

341-1614: Comprehensive test coverage for intersection and exclusion operators.

The new test cases provide excellent coverage for the newly added intersection and exclusion handling functionality. The tests appropriately validate both optimized and unoptimized execution paths, ensuring the weighted graph optimization maintains correctness.


1615-1701: Well-designed concurrent test execution pattern.

The test execution properly runs both optimized and unoptimized queries concurrently, with clean channel management and error handling. The ConsumerLoop pattern effectively collects results from both execution paths.


2302-2359: Well-structured stack utility tests.

The tests for cloneStack and createStackCloneAndStackWithTopItem are comprehensive, testing both success and error cases with appropriate assertions.


26-1701: LGTM! Comprehensive test coverage for weighted graph functionality.

The test structure effectively validates both optimized and unoptimized execution paths across a wide range of scenarios including unions, intersections, exclusions, and edge cases.


2302-2359: LGTM! Well-structured utility function tests.

The stack cloning tests are thorough and cover both success and error cases appropriately.

internal/graph/mock_check_resolver.go (1)

1-187: Auto-generated mock looks correct.

The generated mock properly implements the new CheckRewriteResolver interface with all required methods including the new CheckRewrite method.

pkg/server/commands/reverseexpand/reverse_expand_weighted.go (5)

59-71: Well-implemented stack manipulation utility.

The createStackCloneAndStackWithTopItem function properly handles stack cloning with appropriate error handling for empty stacks.


271-299: Correct implementation of operator node handling.

The modified logic properly detects intersection and exclusion operators and delegates to their respective handlers while maintaining compatibility with existing edge types.


333-390: Clean refactoring for code reuse.

The extraction of queryForTuplesWithItems enables code reuse in the intersection and exclusion handlers while maintaining the existing behavior and error handling.


585-710: Sophisticated intersection handler with proper edge case handling.

The implementation correctly:

  • Handles disconnected edges gracefully (returning early when no connections exist)
  • Uses a two-phase approach with concurrent execution
  • Validates candidates using CheckRewrite
  • Properly manages goroutines and channels

This aligns with the retrieved learnings about graceful handling of disconnected edges in the weighted graph.


718-823: Well-implemented exclusion handler with correct semantics.

The exclusion handler properly:

  • Handles the case where no excluded edge exists
  • Implements correct exclusion logic (candidates pass when check returns false)
  • Maintains consistency with the intersection handler's patterns
  • Uses proper concurrent execution and error handling

Copy link
Contributor

@justincoh justincoh left a comment

Choose a reason for hiding this comment

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

Is there a good way to break up the new handlers into smaller pieces? This PR is pretty high-complexity and those two functions are doing a number of different things.

@Vic-Dev Vic-Dev requested a review from justincoh July 14, 2025 14:37
@Vic-Dev Vic-Dev requested a review from justincoh July 14, 2025 19:28
Copy link
Contributor

@justincoh justincoh left a comment

Choose a reason for hiding this comment

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

Consider my comment non-blocking. If we want to address it we can always do it in a quick follow up PR so we can get this one into main.

@Vic-Dev Vic-Dev merged commit 8e38786 into main Jul 16, 2025
21 of 22 checks passed
@Vic-Dev Vic-Dev deleted the feat/add-list-objects-intersection-exclusion branch July 16, 2025 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants