Skip to content

feat(policy): DSPX-2541 implement GetEntitleableAttributesByFqns#3629

Closed
alkalescent wants to merge 1 commit into
DSPX-2541-attribute-read-api-handlersfrom
DSPX-2541-entitleable-attributes-api
Closed

feat(policy): DSPX-2541 implement GetEntitleableAttributesByFqns#3629
alkalescent wants to merge 1 commit into
DSPX-2541-attribute-read-api-handlersfrom
DSPX-2541-entitleable-attributes-api

Conversation

@alkalescent

@alkalescent alkalescent commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Proposed Changes

  • Implement GetEntitleableAttributesByFqns, the server-side decisioning read API defined in feat(policy): DSPX-2541 add narrow attribute read APIs #3627 (replaces the Unimplemented stub from feat(policy): DSPX-2541 implement GetKeyMappingsByFqns #3628). Per requested attribute value FQN it returns the attribute rule, the value id, the definition's ordered value FQNs (for hierarchy rule logic), and the value-level subject mappings.
  • Add a lean getSubjectMappingsByValueFqns SQL query (sqlc) so subject mappings are fetched by FQN in a single roundtrip, instead of the full ListAllSubjectMappings load the entitlement path uses today. The handler runs two selective queries (attribute FQN lookup + subject-mapping-by-FQN) and groups results into an FQN-keyed map.

Stacked on #3628 (base DSPX-2541-attribute-read-api-handlers). This delivers the API only; migrating the v1/v2 decisioning + PDP consumers onto it and adding namespace-scoped fetch is a separate PR (it depends on the tenancy model, still under discussion).

Checklist

  • I have added or updated unit tests
  • I have added or updated integration tests (if appropriate)
  • I have added or updated documentation

Testing Instructions

  • go test ./service/integration/ -run TestAttributesSuite/Test_GetEntitleableAttributesByFqns — passes against Postgres (verifies rule, value id, ordered definition FQNs, and subject mappings grouped by FQN).
  • golangci-lint run ./policy/db/... ./policy/attributes/... ./integration/... — 0 issues.

Related

Implement the server-side decisioning read API. Per requested attribute value FQN it returns the attribute rule, value id, the definition's ordered value FQNs (for hierarchy rule logic), and the value-level subject mappings. Adds a lean getSubjectMappingsByValueFqns SQL query so subject mappings are fetched by FQN in a single roundtrip instead of the full-policy load. Adds an integration test.

Signed-off-by: Krish Suchak <suchak.krish@gmail.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements the GetEntitleableAttributesByFqns API endpoint, enabling efficient retrieval of attribute-related metadata and subject mappings. By introducing a targeted SQL query, the implementation avoids unnecessary data loading, streamlining the entitlement resolution process for downstream consumers.

Highlights

  • API Implementation: Implemented the GetEntitleableAttributesByFqns server-side read API, replacing the previous stub with functional logic to retrieve attribute rules, value IDs, hierarchy FQNs, and subject mappings.
  • Database Optimization: Added a new getSubjectMappingsByValueFqns SQL query to fetch subject mappings in a single roundtrip, improving performance over the previous full-load approach.
  • Testing: Added comprehensive integration tests in AttributesSuite to verify the new API's response structure, including rule validation and correct grouping of subject mappings by FQN.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


The query runs with speed and grace, To find the FQN in its place. With mappings grouped and rules in sight, The policy engine shines so bright.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added comp:db DB component comp:policy Policy Configuration ( attributes, subject mappings, resource mappings, kas registry) labels Jun 17, 2026
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

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.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5c4caf6f-9316-48d7-8c11-28313f986c76

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch DSPX-2541-entitleable-attributes-api

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements the GetEntitleableAttributesByFqns endpoint to retrieve attribute rules, value identities, ordered value FQNs, and subject mappings for entitlement resolution. It includes a new database query, service and DB client implementations, and integration tests. The feedback focuses on performance optimizations, such as casting actions to jsonb in SQL to avoid redundant JSON marshaling in Go, adding an early return for empty FQN inputs, and using slog.Any for cleaner logging of slices.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +235 to +239
SELECT
sm.id,
fqns.fqn AS value_fqn,
sa.standard_actions,
sa.custom_actions,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

By explicitly casting sa.standard_actions and sa.custom_actions to jsonb in the SQL query, sqlc will generate []byte fields in the Go struct instead of interface{}. This avoids the need to marshal them back to JSON in Go, improving performance and type safety.

SELECT
    sm.id,
    fqns.fqn AS value_fqn,
    sa.standard_actions::jsonb AS standard_actions,
    sa.custom_actions::jsonb AS custom_actions,

Comment on lines +381 to +385
SELECT
sm.id,
fqns.fqn AS value_fqn,
sa.standard_actions,
sa.custom_actions,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Update the generated query constant to match the SQL cast to jsonb.

Suggested change
SELECT
sm.id,
fqns.fqn AS value_fqn,
sa.standard_actions,
sa.custom_actions,
SELECT
sm.id,
fqns.fqn AS value_fqn,
sa.standard_actions::jsonb AS standard_actions,
sa.custom_actions::jsonb AS custom_actions,

Comment on lines +412 to +420
type getSubjectMappingsByValueFqnsRow struct {
ID string `json:"id"`
ValueFqn string `json:"value_fqn"`
StandardActions interface{} `json:"standard_actions"`
CustomActions interface{} `json:"custom_actions"`
Metadata []byte `json:"metadata"`
SubjectConditionSet []byte `json:"subject_condition_set"`
AttributeValue []byte `json:"attribute_value"`
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Update the generated struct fields to []byte to match the jsonb cast in the SQL query.

Suggested change
type getSubjectMappingsByValueFqnsRow struct {
ID string `json:"id"`
ValueFqn string `json:"value_fqn"`
StandardActions interface{} `json:"standard_actions"`
CustomActions interface{} `json:"custom_actions"`
Metadata []byte `json:"metadata"`
SubjectConditionSet []byte `json:"subject_condition_set"`
AttributeValue []byte `json:"attribute_value"`
}
type getSubjectMappingsByValueFqnsRow struct {
ID string `json:"id"`
ValueFqn string `json:"value_fqn"`
StandardActions []byte `json:"standard_actions"`
CustomActions []byte `json:"custom_actions"`
Metadata []byte `json:"metadata"`
SubjectConditionSet []byte `json:"subject_condition_set"`
AttributeValue []byte `json:"attribute_value"`
}

Comment on lines +308 to +319
stdActionsBytes, err := json.Marshal(row.StandardActions)
if err != nil {
return nil, fmt.Errorf("failed to marshal standard actions: %w", err)
}
customActionsBytes, err := json.Marshal(row.CustomActions)
if err != nil {
return nil, fmt.Errorf("failed to marshal custom actions: %w", err)
}
actions := []*policy.Action{}
if err := unmarshalAllActionsProto(stdActionsBytes, customActionsBytes, &actions); err != nil {
return nil, err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Since row.StandardActions and row.CustomActions are now []byte (thanks to the jsonb cast in SQL), we can completely eliminate the redundant json.Marshal calls. This significantly improves performance by avoiding reflection and extra allocations.

Suggested change
stdActionsBytes, err := json.Marshal(row.StandardActions)
if err != nil {
return nil, fmt.Errorf("failed to marshal standard actions: %w", err)
}
customActionsBytes, err := json.Marshal(row.CustomActions)
if err != nil {
return nil, fmt.Errorf("failed to marshal custom actions: %w", err)
}
actions := []*policy.Action{}
if err := unmarshalAllActionsProto(stdActionsBytes, customActionsBytes, &actions); err != nil {
return nil, err
}
actions := []*policy.Action{}
if err := unmarshalAllActionsProto(row.StandardActions, row.CustomActions, &actions); err != nil {
return nil, err
}

Comment on lines +247 to +251
fqns := r.GetFqns()
normalized := make([]string, len(fqns))
for i, fqn := range fqns {
normalized[i] = strings.ToLower(fqn)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If the requested slice of FQNs is empty, we can return early and avoid executing any database queries.

    fqns := r.GetFqns()
    if len(fqns) == 0 {
        return make(map[string]*attributes.GetEntitleableAttributesByFqnsResponse_EntitleableAttribute), nil
    }
    normalized := make([]string, len(fqns))
    for i, fqn := range fqns {
        normalized[i] = strings.ToLower(fqn)
    }


entitleable, err := s.dbClient.GetEntitleableAttributesByFqns(ctx, req.Msg)
if err != nil {
return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextGetRetrievalFailed, slog.String("fqns", fmt.Sprintf("%v", req.Msg.GetFqns())))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Use slog.Any instead of slog.String with fmt.Sprintf to log the slice of FQNs. This is cleaner, more idiomatic, and avoids string formatting overhead.

Suggested change
return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextGetRetrievalFailed, slog.String("fqns", fmt.Sprintf("%v", req.Msg.GetFqns())))
return nil, db.StatusifyError(ctx, s.logger, err, db.ErrTextGetRetrievalFailed, slog.Any("fqns", req.Msg.GetFqns()))

@github-actions

Copy link
Copy Markdown
Contributor
Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 190.852842ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 99.453289ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 420.896499ms
Throughput 237.59 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.70608276s
Average Latency 435.297379ms
Throughput 114.40 requests/second

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • examples
  • otdfctl
  • sdk
  • service
  • lib/fixtures
  • tests-bdd

See the workflow run for details.

@alkalescent

Copy link
Copy Markdown
Contributor Author

Superseded by the collapsed per-API PRs #3632 (GetKeyMappingsByFqns) and #3633 (GetEntitleableAttributesByFqns). Restructured so each new RPC ships with its proto, generated code, and handler in one PR, matching repo convention (e.g. 4c3d53d) and removing the need for the Unimplemented-handler embed.

@alkalescent alkalescent deleted the DSPX-2541-entitleable-attributes-api branch June 17, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:db DB component comp:policy Policy Configuration ( attributes, subject mappings, resource mappings, kas registry) size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant