Skip to content

feat: add policy client and tests#427

Open
nborges-aws wants to merge 1 commit intomainfrom
policyClient
Open

feat: add policy client and tests#427
nborges-aws wants to merge 1 commit intomainfrom
policyClient

Conversation

@nborges-aws
Copy link
Copy Markdown
Contributor

Issue #, if available: Issue #392

Description of changes:

  • Add PolicyEngineClient with__getattr__passthrough
  • Control plane allowlisted methods (14):
    • Policy engine CRUD: create_policy_engine, get_policy_engine, list_policy_engines, update_policy_engine, delete_policy_engine
    • Policy CRUD: create_policy, get_policy, list_policies, update_policy, delete_policy
    • Policy generation: start_policy_generation, get_policy_generation, list_policy_generations, list_policy_generation_assets
  • Add *_and_wait polling methods using shared wait_until/wait_until_deleted utilities:
    • create_policy_engine_and_wait, update_policy_engine_and_wait, delete_policy_engine_and_wait
    • create_policy_and_wait, delete_policy_and_wait
  • Add higher-level orchestration methods:
    • generate_policy_asset_and_wait — starts NL→Cedar generation, polls until complete, optionally fetches generated assets
    • generate_and_create_policy — end-to-end flow: generate asset + create policy from it in one call
    • create_policy_from_generation_asset — creates a policy from a specific generation asset
    • create_or_get_policy_engine / create_or_get_policy — idempotent creates (handles ConflictException by finding existing resource)
  • All *_and_wait methods accept WaitConfig for configurable polling behavior and apply convert_kwargs for snake_case support

Test plan:

  • Integration tests (10 total):
    • Full CRUD lifecycle: create engine → get (camelCase + snake_case) → update → create policy → get policy → list engines → list policies → delete policy → delete engine
    • Validates passthrough, snake_case conversion, *_and_wait polling, and cleanup
  • Unit tests (30 total, all passing):
    • Init: region, region fallback, boto3 client creation, integration_source
    • Passthrough: CP forwarding, snake_case conversion, AttributeError on unknown method, allowlist contents
    • Generate policy: immediate success, fetch assets, polling, failure, timeout
    • Create from generation asset: definition shape, optional params
    • Wait methods: immediate active, polling, failed status, timeout (engine + policy)
    • Idempotent creates: new resource, conflict → find existing, not found → reraise, non-conflict error → reraise

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions
Copy link
Copy Markdown
Contributor

✅ No Breaking Changes Detected

No public API breaking changes found in this PR.

Copy link
Copy Markdown
Contributor

@Hweinstock Hweinstock left a comment

Choose a reason for hiding this comment

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

Nice! I like the client. Few questions on the integ tests, but otherwise looks good.

)
except Exception as e:
print(f"Failed to delete policy {policy_id}: {e}")
# Wait for policy deletes to complete before deleting engines
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.

should we poll here instead of waiting?

policyEngineId=self.engine_ids[0],
description={"optionalValue": "updated by integ test"},
)
assert updated["status"] == "ACTIVE"
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.

should we verify that the policy actually updated?

cls.policy_ids = []
sts = boto3.client("sts", region_name=cls.region)
account_id = sts.get_caller_identity()["Account"]
cls.gateway_resource_arn = f"arn:aws:bedrock-agentcore:{cls.region}:{account_id}:gateway/*"
Copy link
Copy Markdown
Contributor

@Hweinstock Hweinstock Apr 23, 2026

Choose a reason for hiding this comment

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

is this used anywhere?

logger.info("Policy engine '%s' already exists, looking up...", name)
engine_id = self._find_policy_engine_by_name(name)
if not engine_id:
raise
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.

nit: if a gateway exists, but the lookup fails, the user sees a conflict exception here. Not sure how this could happen, but we might want a better error to avoid confusing customers.



@pytest.mark.integration
class TestPolicyEngineClient:
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.

should we also test create_or_get methods?

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.

2 participants