Skip to content

feat(gateway): add update commands - #1900

Merged
aidandaly24 merged 9 commits into
refactorfrom
feat/gateway-update-cud
Aug 13, 2026
Merged

feat(gateway): add update commands#1900
aidandaly24 merged 9 commits into
refactorfrom
feat/gateway-update-cud

Conversation

@aidandaly24

@aidandaly24 aidandaly24 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add Gateway, Target, connector-backed Target, and Rule update commands
  • support explicit set, clear, and no-op validation semantics
  • GET current Gateway and Target state, merge requested changes, and send the complete replacement requests required by the service
  • keep Rule updates patch-shaped
  • preserve complete API access through JSON sources plus focused MCP endpoint and Connector shortcuts
  • keep Connector shortcut construction handler-owned and Connector classification local to each layer

IAM scope

  • this PR does not create, mutate, or reconcile IAM roles or permission policies
  • --role-arn updates only the Gateway service configuration
  • execution-role permissions remain customer-managed until the shared IAM management plane lands

Stack

Testing

  • focused Gateway validation, flag mapping, request merge, connector classification, and fixture replay tests
  • fixture-backed Gateway, Target, Connector, and Rule updates through the real router and Core
  • full build, lint, formatting, typecheck, cross-platform unit-test, compiled-binary smoke-test, and AgentCore E2E CI coverage

@github-actions github-actions Bot added the size/xl PR size: XL label Aug 4, 2026
@github-actions github-actions Bot added agentcore-harness-reviewing AgentCore Harness review in progress size/xl PR size: XL and removed size/xl PR size: XL agentcore-harness-reviewing AgentCore Harness review in progress labels Aug 4, 2026
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 44d48b6 to a7eafd8 Compare August 4, 2026 14:08
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 4, 2026
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from a7eafd8 to 1fd0230 Compare August 4, 2026 14:22
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 4, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 4, 2026
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 1fd0230 to c8c57a0 Compare August 4, 2026 16:59
@codecov-commenter

codecov-commenter commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.71102% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.81%. Comparing base (531d068) to head (7bca271).

Files with missing lines Patch % Lines
src/handlers/gateway/connector/update/index.tsx 92.15% 12 Missing ⚠️
src/handlers/gateway/rule/update/index.tsx 97.36% 2 Missing ⚠️
src/handlers/gateway/target/update/index.tsx 99.15% 1 Missing ⚠️
src/handlers/gateway/update/index.tsx 99.43% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #1900      +/-   ##
============================================
+ Coverage     96.78%   96.81%   +0.03%     
============================================
  Files           326      330       +4     
  Lines         18061    18758     +697     
============================================
+ Hits          17480    18161     +681     
- Misses          581      597      +16     

☔ View full report in Codecov by Harness.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from c8c57a0 to 7929436 Compare August 4, 2026 17:57
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 7929436 to 4bc43ed Compare August 4, 2026 18:21
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 4bc43ed to d7e30f7 Compare August 4, 2026 18:32
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch 3 times, most recently from 6774fc3 to 87ae909 Compare August 10, 2026 15:32
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 87ae909 to 6629f3f Compare August 10, 2026 15:47
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 6629f3f to 3b72c3d Compare August 10, 2026 19:14
Base automatically changed from feat/gateway-create-cud to refactor August 10, 2026 19:36
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 3b72c3d to f247150 Compare August 10, 2026 19:36
@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from f247150 to 09c53af Compare August 10, 2026 23:34
Comment thread src/core/gateway.test.ts Outdated
}

describe("GatewayClient updateGateway", () => {
test("preserves required and omitted fields while replacing selected configuration", async () => {

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.

what coverage or guarantees do we get from these tests that we don't get from the top level handler tests that use fixtures? I'm wondering if testing on the client is focusing too heavily on implementation details, rather than behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That’s fair. The fixture-backed flow already covers the normal GET-to-Update path, so the simple preservation tests are redundant. I’ll trim this file to the clear, merge, shorthand, and classification behaviors the fixture flow does not cover.

Comment thread src/core/gateway.tsx Outdated
): Promise<UpdateGatewayResponse> {
const control = this.clients.control(toClientConfig(options));
const current = await control.send(new GetGatewayCommand({ gatewayIdentifier: patch.id }));
const name = GatewayClient.required(current.name, patch.id, "name");

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.

would a get gateway response ever return a response without these fields? If we did get a response without these, should that be classified as a service error instead of client?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. name and authorizerType are modeled as required, and roleArn is required by Create and Update. If GET omits them, that is a service-response issue rather than invalid user input. I changed this guard, and the equivalent required Target configuration guard, to report source: service.

Comment thread src/core/gateway.tsx Outdated
const control = this.clients.control(toClientConfig(options));
const current = await control.send(new GetGatewayCommand({ gatewayIdentifier: patch.id }));
const name = GatewayClient.required(current.name, patch.id, "name");
const roleArn = GatewayClient.required(current.roleArn, patch.id, "role ARN");

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.

would it be simpler to deep merge current and patch then let the API reject? I'm wondering if this could get difficult to maintain as the fields change/grow.

Something like

const current = await.control.send(new GetGatewayCommand({ gatewayIdentifier: patch.id }));
const request = deepReplace(current, patch);
return control.send(new UpdateGatewayCommand(request));

where deepReplace walks the object tree calling the existing replace method.

The downside is we lose the custom error messages, but I wonder if we could rely on the service validation for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I considered this, but deep merging would treat every nested object as a patch. Our JSON flags are full replacements, so it could silently preserve fields the customer intended to remove. Policy Engine is the one intentional partial merge because we expose Policy Engine as separate --policy-engine-arn and --policy-engine-mode flags. An explicit mapper keeps those preserve, replace, and clear semantics unambiguous.

Comment thread src/core/gateway.tsx
return this.clients.control(toClientConfig(options)).send(new UpdateGatewayRuleCommand(input));
}

private async updateTarget(

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.

similar question here. I wonder if we can avoid maintaining all this client side validation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I kept the checks that enforce CLI-only semantics: connector update must remain connector-backed, and --endpoint only applies to an existing MCP server Target. The service cannot infer those command and shorthand contracts. Missing required targetConfiguration from GET now reports a service error instead of a user validation error.

z.string().optional(),
),
flag(
"private-endpoint",

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.

one day I would love to generate these from the api spec, but that day is not today lol.

throw new InputValidationError("required option '--target-id <target-id>' not specified");
}

for (const [name, value, clear] of [

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.

i like this pattern for handling mutually exclusive. Do you see a natural way to generalize it a bit to re-use across these few handlers that use it in this PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Implemented as validateSetClearConflicts in the shared handler utilities and reused it across Gateway, Target, Connector, and Rule updates. Resource-specific validation remains in each handler.


export type CreateGatewayRuleInput = CreateGatewayRuleRequest;

export type GatewayUpdatePatch = {

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.

is there a strong motivation to define our own types here, instead of derive it from the SDK types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We need a distinct patch type because it includes CLI-only semantics such as clear sentinels and endpoint shorthand. However, fields that map directly to the service should derive their types from UpdateGatewayRequest and UpdateGatewayTargetRequest. I’ll update those while keeping only the CLI-specific additions explicit.

@aidandaly24
aidandaly24 force-pushed the feat/gateway-update-cud branch from 09c53af to dd11915 Compare August 11, 2026 19:11
Hweinstock
Hweinstock previously approved these changes Aug 11, 2026
Comment thread src/handlers/utils.tsx
return JSON.parse(raw) as T;
} catch (error) {
throw new InputValidationError(
`Invalid JSON for option '--${name}': ${error instanceof Error ? error.message : String(error)}`,

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.

OOS here, but I feel like we should rename this to avoid it becoming a dumping ground of a bunch of functionality? Looks mostly parsing related? maybe src/handlers/parsing.tsx?

@aidandaly24 aidandaly24 Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed that utils.tsx is broad and could become a dumping ground. I kept the file rename out of this PR since this change only adds one handler-validation helper. Splitting the existing parsing utilities can be a focused follow-up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I also think that the parsing stuff should be moved into a class. There is only one function here that is not parsing.

Comment thread src/handlers/utils.tsx Outdated
return parsed as T[];
}

export function validateSetClearConflicts(

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: my understanding is that the existing logic holds for any mutually exclusive input (not just set and clear). Does it make sense to name it something to capture that? Ex. assertMutuallyExclusiveInputs or something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated this to assertMutuallyExclusiveInputs. It now accepts both input names and values, and is used for ordinary pairs such as --endpoint/--target-configuration and --connector/--connector-configuration, as well as set/clear pairs.

@Hweinstock Hweinstock 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.

LGTM!

@aidandaly24
aidandaly24 merged commit 0e33398 into refactor Aug 13, 2026
14 checks passed

@jariy17 jariy17 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.

nit: These are nits so make follow up prs for this.

Comment thread src/core/gateway.tsx
const control = this.clients.control(toClientConfig(options));
const current = await control.send(new GetGatewayCommand({ gatewayIdentifier: patch.id }));
const resource = `Gateway "${patch.id}"`;
const name = GatewayClient.required(current.name, resource, "name");

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.

Please check the smithy model, I'm pretty sure the name is required in the response. AWS SDK v3's response parameters are all | undefined for some reason.

Comment thread src/core/gateway.tsx
let policyEngineConfiguration = current.policyEngineConfiguration;
if (patch.policyEngineConfiguration === null) {
policyEngineConfiguration = undefined;
} else if (patch.policyEngineConfiguration !== undefined) {

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.

Lets choose one null or undefined.

Comment thread src/core/gateway.tsx
return control.send(new UpdateGatewayTargetCommand(request));
}

private static replace<T>(

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.

Is there a way we can use this.replace instead of GatewayClient.replace?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants