Skip to content

feat(policy): support inline obligation triggers on attribute value create#3432

Merged
jakedoublev merged 14 commits intomainfrom
feat/DSPX-3160-inline-attribute-value-obligation-triggers
May 5, 2026
Merged

feat(policy): support inline obligation triggers on attribute value create#3432
jakedoublev merged 14 commits intomainfrom
feat/DSPX-3160-inline-attribute-value-obligation-triggers

Conversation

@jakedoublev
Copy link
Copy Markdown
Contributor

@jakedoublev jakedoublev commented May 4, 2026

DSPX-3160

Summary

This updates obligation trigger creation so the trigger’s source namespace comes from the attribute value side, and the action must resolve/validate in that same namespace. That allows a trigger under one namespace to point at an obligation value in another namespace, including when triggers are created inline during CreateAttributeValue.

Changes

  • allow CreateObligationTrigger to validate and resolve against the trigger source namespace from the attribute value
  • allow trigger creation to target obligation values in a different namespace
  • update ListObligationTriggers namespace filtering to use the trigger source namespace
  • CreateAttributeValueRequest now accepts obligation_triggers with inline trigger metadata support
  • add a top-level namespace to ObligationTrigger responses and populate it from the trigger source namespace
  • add integration coverage

Summary by CodeRabbit

  • New Features

    • Added ability to associate obligation triggers with attribute values during creation
    • Enabled cross-namespace obligation trigger support
    • Obligation triggers now include namespace information in responses
  • Documentation

    • Updated API specifications across multiple services to reflect obligation trigger enhancements and namespace field additions

…reate

Signed-off-by: jakedoublev <jake.vanvorhis@virtru.com>
@jakedoublev jakedoublev requested review from a team as code owners May 4, 2026 22:53
@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 introduces support for inline obligation triggers when creating attribute values. By allowing these triggers to be defined at the time of creation, the system ensures that the necessary associations are established atomically, improving both the developer experience and the reliability of policy enforcement configurations.

Highlights

  • API Enhancement: Added optional obligation_triggers to the CreateAttributeValue RPC, allowing for the association of obligation values during attribute value creation.
  • Transactional Integrity: Implemented transactional creation of obligation triggers within the same RPC call to ensure data consistency.
  • Validation and Testing: Added comprehensive validation for the new request fields and included integration tests to verify successful creation and rollback behavior on failure.

🧠 New Feature in Public Preview: You can now enable Memory 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.

Ignored Files
  • Ignored by pattern: docs/openapi/**/* (4)
    • docs/openapi/authorization/authorization.openapi.yaml
    • docs/openapi/policy/attributes/attributes.openapi.yaml
    • docs/openapi/policy/objects.openapi.yaml
    • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
  • Ignored by pattern: protocol/**/* (3)
    • protocol/go/authorization/authorization.pb.go
    • protocol/go/policy/attributes/attributes.pb.go
    • protocol/go/policy/objects.pb.go
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 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 counter productive. 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.


A value created with care, Obligations now linked in the air. Atomic and swift, A transactional gift, To keep all our policies fair.

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) docs Documentation labels May 4, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

📝 Walkthrough

Walkthrough

This PR introduces inline obligation-trigger creation during attribute-value creation via a new AttributeValueObligationTriggerRequest message. It shifts trigger namespace derivation from the obligation value to the attribute value, enabling cross-namespace obligation triggers. The ObligationTrigger proto and OpenAPI schemas now include a namespace field derived from the trigger's attribute value, with updated database queries and integration tests.

Changes

Obligation Trigger Cross-Namespace Support

Layer / File(s) Summary
Data Shape / Proto Definitions
service/policy/attributes/attributes.proto, service/policy/objects.proto
New AttributeValueObligationTriggerRequest message with required obligation_value and action plus optional context and metadata. CreateAttributeValueRequest adds obligation_triggers field. ObligationTrigger gains namespace field derived from attribute value and action.
API Documentation & Schemas
docs/grpc/index.html, docs/openapi/policy/attributes/attributes.openapi.yaml, docs/openapi/policy/objects.openapi.yaml, docs/openapi/policy/actions/actions.openapi.yaml, docs/openapi/policy/obligations/obligations.openapi.yaml, docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml, docs/openapi/policy/registeredresources/registered_resources.openapi.yaml, docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml, docs/openapi/policy/unsafe/unsafe.openapi.yaml
OpenAPI specs and gRPC documentation updated with new common.IdFqnIdentifier, AttributeValueObligationTriggerRequest, and namespace field on ObligationTrigger. Namespace documentation clarifies trigger derivation from attribute value and action.
Database Query Layer
service/policy/db/queries/attribute_values.sql, service/policy/db/queries/obligations.sql, service/policy/db/attribute_values.sql.go, service/policy/db/obligations.sql.go
SQL queries updated to join attribute_definitions and attribute_namespaces (as trigger_ns) to derive trigger namespace instead of using obligation-value namespace. Trigger JSON payloads now include namespace { id, name, fqn } derived from attribute value. createObligationTrigger removed namespace equality constraint between obligation value and attribute value. listObligationTriggers filters by trigger namespace instead of obligation namespace.
Core Implementation
service/policy/db/attribute_values.go, service/policy/db/obligations.go
CreateAttributeValue iterates over obligation_triggers, calling CreateObligationTrigger for each after upserting attribute value FQN. CreateObligationTrigger derives triggerNamespaceID from attributeValue, resolves actionID within that namespace, and validates action belongs to trigger namespace (not obligation value namespace). Helper getAttributeValueNamespaceID added; resolveObligationTriggerActionID updated to accept explicit actionNamespaceID.
Protocol Validator
service/policy/attributes/attributes_test.go
New TestCreateAttributeValue_WithObligationTriggers_Request validates CreateAttributeValueRequest.ObligationTriggers with valid obligation value/action pairs and asserts validation errors for missing/invalid obligation value and action fields.
Integration Tests
service/integration/attribute_values_test.go, service/integration/obligation_triggers_test.go
Three new attribute-values tests verify inline trigger creation (triggers persisted with correct mappings), cross-namespace obligation values (trigger namespace matches target obligation namespace), and non-existent FQN failures. Three new obligation-triggers tests validate cross-namespace obligation-value creation, action-name resolution using source-namespace action, and trigger filtering by source-namespace. Helper assertObligationValueHasSingleTrigger validates trigger structure and namespace derivation. Helpers getActionByNameInNamespace and createCrossNamespaceObligationValue support cross-namespace tests.

Sequence Diagram

sequenceDiagram
    participant Client
    participant AttrValueSvc as AttributeValue Service
    participant TriggerSvc as ObligationTrigger Service
    participant DB as Database
    
    Client->>AttrValueSvc: CreateAttributeValue(name, obligations=[])
    Note over Client,AttrValueSvc: obligations contain obligation_triggers
    
    AttrValueSvc->>DB: Upsert AttributeValue FQN
    DB-->>AttrValueSvc: Created ID
    
    loop For each obligation_trigger
        AttrValueSvc->>TriggerSvc: CreateObligationTrigger(attr_value_id, obligation_value, action)
        Note over TriggerSvc: Derive trigger_namespace from attribute_value
        TriggerSvc->>DB: Resolve action in trigger_namespace
        DB-->>TriggerSvc: Action ID
        TriggerSvc->>DB: Validate action namespace == trigger_namespace
        TriggerSvc->>DB: Insert trigger with derived namespace
        DB-->>TriggerSvc: Trigger created
        TriggerSvc-->>AttrValueSvc: Trigger result
    end
    
    AttrValueSvc->>DB: GetAttributeValue(ID)
    Note over DB: Join attribute_definitions and attribute_namespaces<br/>Enrich triggers with namespace from attribute_value
    DB-->>AttrValueSvc: AttributeValue with triggers [namespace fields]
    
    AttrValueSvc-->>Client: Created AttributeValue with obligations
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

  • opentdf/platform#3353: Updates obligation-trigger retrieval and filtering logic to work with cross-namespace triggers and action hydration.
  • opentdf/platform#3206: Modifies obligation-trigger creation and namespace-resolution logic in the same core files.
  • opentdf/platform#3318: Alters obligation-trigger persistence and payload handling in obligations.go and obligations.sql.go.

Suggested Labels

size/m

Suggested Reviewers

  • c-r33d
  • elizabethhealy
  • alkalescent

Poem

🐰 A trigger now hops through namespaces free,
Not bound to one obligation, but all it may see,
Attribute values bloom with inline delights,
Cross-namespace journeys in obligation's flights,
The namespace derived where the action takes flight! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the primary change: enabling inline obligation triggers to be specified and created together with attribute values.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/DSPX-3160-inline-attribute-value-obligation-triggers

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.

@github-actions github-actions Bot added the size/s label May 4, 2026
Copy link
Copy Markdown
Contributor

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

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 introduces the capability to create obligation triggers inline during the creation of an attribute value. The changes include updates to the Protobuf definitions, documentation, and database logic to handle these triggers within a transaction, along with new integration and unit tests. Feedback suggests adding a metadata field to the trigger request for consistency and optimizing the database loop used for trigger creation to improve performance.

Comment thread service/policy/attributes/attributes.proto
Comment thread service/policy/db/attribute_values.go
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 410.944255ms
Throughput 243.34 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.141154564s
Average Latency 429.538178ms
Throughput 115.90 requests/second

Copy link
Copy Markdown

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/grpc/index.html`:
- Around line 8391-8397: Update the documentation for the obligation_triggers
field (AttributeValueObligationTriggerRequest) to state its transactional
semantics: explicitly note that creation of obligation_triggers is atomic with
the attribute value creation (all-or-nothing), so either the attribute value and
all provided obligation_triggers are created together or none are created;
ensure the wording appears alongside the existing description for
obligation_triggers so clients don’t assume partial success.

In `@docs/openapi/authorization/authorization.openapi.yaml`:
- Around line 96-98: The documented numeric bounds for
google.protobuf.Timestamp.seconds are incorrect; update the description for
Timestamp.seconds to use the canonical epoch-second min and max values: set the
minimum to -62135596800 and the maximum to 253402300799 (corresponding to
0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z respectively) wherever the old
±315576000000 bounds appear (e.g., in the Timestamp.seconds description entries
for google.protobuf.Timestamp).
- Around line 112-116: Update the two duplicated descriptions that refer to
createdAt.nanos to use "timestamp" terminology instead of "duration": locate the
description text for the field named createdAt.nanos and replace phrases like
"duration" and "the nanosecond portion of the duration" with wording that
clarifies this is the nanosecond component of a timestamp (e.g., "the nanosecond
portion of the timestamp, not an alternative to seconds"), ensure the note about
negative seconds and valid range (0 to 999,999,999) remains intact, and apply
the same change to both occurrences of createdAt.nanos so both descriptions
match.

In `@service/policy/db/attribute_values.go`:
- Around line 61-71: The loop creating obligation triggers currently sets
AttributeValue to &common.IdFqnIdentifier{Id: createdID} and omits the computed
FQN returned by upsertAttributeValueFqn; update the code to capture the FQN from
the upsertAttributeValueFqn result (the []upsertAttributeValueFqnRow returned
earlier) and pass it into CreateObligationTrigger by populating
AttributeValue.Fqn (i.e., use &common.IdFqnIdentifier{Id: createdID, Fqn:
<capturedFqn>}) so CreateObligationTrigger (and its SQL using
pgtypeText(r.GetAttributeValue().GetFqn())) receives the FQN fallback value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: db35657f-4386-45c2-9555-3ea6fa049f8b

📥 Commits

Reviewing files that changed from the base of the PR and between cffcb73 and 4957e57.

⛔ Files ignored due to path filters (3)
  • protocol/go/authorization/authorization.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/attributes/attributes.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/objects.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (9)
  • docs/grpc/index.html
  • docs/openapi/authorization/authorization.openapi.yaml
  • docs/openapi/policy/attributes/attributes.openapi.yaml
  • docs/openapi/policy/objects.openapi.yaml
  • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
  • service/integration/attribute_values_test.go
  • service/policy/attributes/attributes.proto
  • service/policy/attributes/attributes_test.go
  • service/policy/db/attribute_values.go
💤 Files with no reviewable changes (2)
  • docs/openapi/policy/objects.openapi.yaml
  • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml

Comment thread docs/grpc/index.html
Comment thread docs/openapi/authorization/authorization.openapi.yaml
Comment thread docs/openapi/authorization/authorization.openapi.yaml
Comment thread service/policy/db/attribute_values.go
Signed-off-by: jakedoublev <jake.vanvorhis@virtru.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 439.971377ms
Throughput 227.29 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.109270075s
Average Latency 429.23221ms
Throughput 115.98 requests/second

@jakedoublev jakedoublev marked this pull request as draft May 4, 2026 23:10
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

…ation

Signed-off-by: jakedoublev <jake.vanvorhis@virtru.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 421.460978ms
Throughput 237.27 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 44.260184691s
Average Latency 440.658174ms
Throughput 112.97 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 454.824739ms
Throughput 219.86 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 42.256946658s
Average Latency 419.831876ms
Throughput 118.32 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 444.588885ms
Throughput 224.93 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 44.441488419s
Average Latency 442.421916ms
Throughput 112.51 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Signed-off-by: jakedoublev <jake.vanvorhis@virtru.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 397.241678ms
Throughput 251.74 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 42.505285964s
Average Latency 423.690906ms
Throughput 117.63 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 464.317236ms
Throughput 215.37 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 45.089297817s
Average Latency 449.141153ms
Throughput 110.89 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 412.13259ms
Throughput 242.64 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.725782757s
Average Latency 435.763521ms
Throughput 114.35 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

X-Test Failure Report

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 352.271903ms
Throughput 283.87 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 33.664172918s
Average Latency 335.170221ms
Throughput 148.53 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 449.721311ms
Throughput 222.36 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 46.386373534s
Average Latency 462.190409ms
Throughput 107.79 requests/second

…ous SQL joins

Signed-off-by: jakedoublev <jake.vanvorhis@virtru.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 433.619722ms
Throughput 230.62 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.918050477s
Average Latency 437.779101ms
Throughput 113.85 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Signed-off-by: jakedoublev <jake.vanvorhis@virtru.com>
@jakedoublev jakedoublev marked this pull request as ready for review May 5, 2026 19:34
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

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

Benchmark authorization.v2.GetMultiResourceDecision Results:

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

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 408.418466ms
Throughput 244.85 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 43.15726623s
Average Latency 429.168015ms
Throughput 115.86 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • tests-bdd

See the workflow run for details.

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
service/policy/db/obligations.go (1)

775-792: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Use the direct namespace lookup instead of hydrating two full resources.

getAttributeValueNamespaceID does a GetAttributeValue and then a GetAttribute just to recover one UUID. On the new inline-trigger path, that turns multi-trigger creates into N× extra DB round-trips even though getAttributeValueNamespaceIDs already returns ad.namespace_id directly. Query that helper here, or add a single-row variant, so CreateObligationTrigger stays cheap on bulk inline trigger creation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/policy/db/obligations.go` around lines 775 - 792,
getAttributeValueNamespaceID currently hydrates two full resources via
GetAttributeValue and GetAttribute to return a single namespace id, causing
extra DB round-trips; change it to use the existing helper that returns
ad.namespace_id directly (getAttributeValueNamespaceIDs) or add a new single-row
helper (e.g., GetAttributeValueNamespaceIDDirect) and call that from
getAttributeValueNamespaceID so CreateObligationTrigger no longer does N× extra
queries—replace the GetAttributeValue/GetAttribute calls with the direct
namespace-id query and return its id (preserve the same error wrapping behavior
using db.WrapIfKnownInvalidQueryErr).
service/policy/db/queries/attribute_values.sql (1)

6-61: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope trigger aggregation by attribute value too.

obligation_triggers_agg still collapses rows on ot.obligation_value_id alone, so getAttributeValue reuses the same trigger list for every attribute value that references that obligation value. With the new namespace payload, this will return other attribute values' source namespaces/triggers in the wrong GetAttributeValue response. Carry ot.attribute_value_id through the trigger/value aggregations and join on it in attribute_obligations so the nested trigger set stays scoped to the requested attribute value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/policy/db/queries/attribute_values.sql` around lines 6 - 61, The
trigger aggregation is currently grouped only by ot.obligation_value_id so
triggers get shared across different attribute values; update
obligation_triggers_agg to SELECT and GROUP BY ot.attribute_value_id as well
(keep ot.obligation_value_id), propagate that attribute_value_id into
obligation_values_agg (include it in the SELECT and GROUP BY alongside
ov.obligation_definition_id), and then adjust the downstream join in
attribute_obligations to join on both obligation_definition_id and
attribute_value_id so each attribute value receives only its scoped triggers.
service/policy/db/queries/obligations.sql (1)

731-769: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Reuse trigger_ns_fqns for the output FQN to avoid two sources of truth in this query.

In listObligationTriggers you LEFT JOIN attribute_fqns trigger_ns_fqns (line 765) and filter the result against trigger_ns_fqns.fqn (line 769), but the JSON output at line 734 builds the namespace FQN as CONCAT('https://', trigger_ns.name). The two should be equal in practice, but having one canonical-table value drive filtering and a hardcoded reconstruction drive the response is unnecessary divergence — any future change to FQN normalization (e.g., scheme, casing) would have to be updated in both places to stay consistent. Since the JOIN is already paid for here, prefer the canonical column for output as well.

♻️ Suggested change
             'attribute_value', JSON_BUILD_OBJECT(
                 'id', av.id,
                 'value', av.value,
                 'fqn', COALESCE(av_fqns.fqn, '')
             ),
             'namespace', JSON_BUILD_OBJECT(
                 'id', trigger_ns.id,
                 'name', trigger_ns.name,
-                'fqn', CONCAT('https://', trigger_ns.name)
+                'fqn', COALESCE(trigger_ns_fqns.fqn, CONCAT('https://', trigger_ns.name))
             ),

Note: the other 7 trigger queries use CONCAT(...) deliberately to avoid an extra LEFT JOIN attribute_fqns per row (PR description: "remove extraneous joins"), so this suggestion is scoped to listObligationTriggers only, where the join is already present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/policy/db/queries/obligations.sql` around lines 731 - 769, The JSON
output for the trigger namespace FQN in listObligationTriggers currently
constructs the FQN with CONCAT('https://', trigger_ns.name) inside the
JSON_BUILD_OBJECT; instead use the canonical value returned by the existing LEFT
JOIN (trigger_ns_fqns.fqn) so filtering and output come from the same source.
Update the JSON_BUILD_OBJECT that builds 'fqn' (inside the 'namespace' object)
to use trigger_ns_fqns.fqn (or COALESCE(trigger_ns_fqns.fqn, CONCAT(...)) if you
want a fallback) instead of concatenating trigger_ns.name, leaving the JOIN on
attribute_fqns trigger_ns_fqns and the WHERE condition unchanged.
♻️ Duplicate comments (1)
docs/grpc/index.html (1)

8405-8411: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Re-add transactional semantics for obligation_triggers docs.

Line 8409 describes contents but not all-or-nothing behavior. This can still mislead clients into expecting partial success.

✏️ Suggested wording
-                  <td><p>Optional
-Existing obligation values to trigger for the newly created attribute value. </p></td>
+                  <td><p>Optional
+Existing obligation values to trigger for the newly created attribute value.
+Creation is transactional with attribute value creation; if any trigger fails validation or persistence, the request fails and no records are created. </p></td>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/grpc/index.html` around lines 8405 - 8411, The documentation for the
field obligation_triggers (AttributeValueObligationTriggerRequest) omits that
processing is transactional/all-or-nothing; update the table cell describing
obligation_triggers to explicitly state that the provided
AttributeValueObligationTriggerRequest entries are applied atomically—either all
obligation triggers succeed for the new attribute value or none are applied—and
that on failure the operation is rolled back and an error is returned; mention
this transactional behavior and error semantics in the same description text for
obligation_triggers so clients do not assume partial success.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@service/integration/attribute_values_test.go`:
- Around line 1247-1292: The test currently checks for an error and nil
createdValue but does not assert that no attribute value was persisted; after
the existing s.Require().ErrorIs(err, db.ErrNotFound) add a verification that no
attribute value exists for attrDef.GetId() with Value
"test_value_missing_obligation_fqn" (e.g. call the appropriate read/list API
such as PolicyClient.ListAttributeValues or
PolicyClient.GetAttributeValuesByAttributeId using s.ctx and attrDef.GetId(),
then assert the returned list does not contain that value or is empty) to ensure
the failed CreateAttributeValue (with inline obligation trigger) rolled back
completely.

---

Outside diff comments:
In `@service/policy/db/obligations.go`:
- Around line 775-792: getAttributeValueNamespaceID currently hydrates two full
resources via GetAttributeValue and GetAttribute to return a single namespace
id, causing extra DB round-trips; change it to use the existing helper that
returns ad.namespace_id directly (getAttributeValueNamespaceIDs) or add a new
single-row helper (e.g., GetAttributeValueNamespaceIDDirect) and call that from
getAttributeValueNamespaceID so CreateObligationTrigger no longer does N× extra
queries—replace the GetAttributeValue/GetAttribute calls with the direct
namespace-id query and return its id (preserve the same error wrapping behavior
using db.WrapIfKnownInvalidQueryErr).

In `@service/policy/db/queries/attribute_values.sql`:
- Around line 6-61: The trigger aggregation is currently grouped only by
ot.obligation_value_id so triggers get shared across different attribute values;
update obligation_triggers_agg to SELECT and GROUP BY ot.attribute_value_id as
well (keep ot.obligation_value_id), propagate that attribute_value_id into
obligation_values_agg (include it in the SELECT and GROUP BY alongside
ov.obligation_definition_id), and then adjust the downstream join in
attribute_obligations to join on both obligation_definition_id and
attribute_value_id so each attribute value receives only its scoped triggers.

In `@service/policy/db/queries/obligations.sql`:
- Around line 731-769: The JSON output for the trigger namespace FQN in
listObligationTriggers currently constructs the FQN with CONCAT('https://',
trigger_ns.name) inside the JSON_BUILD_OBJECT; instead use the canonical value
returned by the existing LEFT JOIN (trigger_ns_fqns.fqn) so filtering and output
come from the same source. Update the JSON_BUILD_OBJECT that builds 'fqn'
(inside the 'namespace' object) to use trigger_ns_fqns.fqn (or
COALESCE(trigger_ns_fqns.fqn, CONCAT(...)) if you want a fallback) instead of
concatenating trigger_ns.name, leaving the JOIN on attribute_fqns
trigger_ns_fqns and the WHERE condition unchanged.

---

Duplicate comments:
In `@docs/grpc/index.html`:
- Around line 8405-8411: The documentation for the field obligation_triggers
(AttributeValueObligationTriggerRequest) omits that processing is
transactional/all-or-nothing; update the table cell describing
obligation_triggers to explicitly state that the provided
AttributeValueObligationTriggerRequest entries are applied atomically—either all
obligation triggers succeed for the new attribute value or none are applied—and
that on failure the operation is rolled back and an error is returned; mention
this transactional behavior and error semantics in the same description text for
obligation_triggers so clients do not assume partial success.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a8572483-1b51-4b97-b9e8-650e54696b27

📥 Commits

Reviewing files that changed from the base of the PR and between 4957e57 and e7c08c0.

⛔ Files ignored due to path filters (3)
  • protocol/go/policy/attributes/attributes.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/objects.pb.go is excluded by !**/*.pb.go
  • protocol/go/policy/obligations/obligations.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (21)
  • docs/grpc/index.html
  • docs/openapi/policy/actions/actions.openapi.yaml
  • docs/openapi/policy/attributes/attributes.openapi.yaml
  • docs/openapi/policy/objects.openapi.yaml
  • docs/openapi/policy/obligations/obligations.openapi.yaml
  • docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
  • docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml
  • docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
  • docs/openapi/policy/unsafe/unsafe.openapi.yaml
  • service/integration/attribute_values_test.go
  • service/integration/obligation_triggers_test.go
  • service/policy/attributes/attributes.proto
  • service/policy/attributes/attributes_test.go
  • service/policy/db/attribute_values.go
  • service/policy/db/attribute_values.sql.go
  • service/policy/db/obligations.go
  • service/policy/db/obligations.sql.go
  • service/policy/db/queries/attribute_values.sql
  • service/policy/db/queries/obligations.sql
  • service/policy/objects.proto
  • service/policy/obligations/obligations.proto

Comment thread service/integration/attribute_values_test.go
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@jakedoublev jakedoublev enabled auto-merge May 5, 2026 19:43
@jakedoublev jakedoublev added this pull request to the merge queue May 5, 2026
Merged via the queue into main with commit 876f512 May 5, 2026
41 checks passed
@jakedoublev jakedoublev deleted the feat/DSPX-3160-inline-attribute-value-obligation-triggers branch May 5, 2026 20:12
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) docs Documentation size/s

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants