Skip to content

feat(aws-apigateway): method/integration responses, mapping templates, MOCK (A2) - #1441

Merged
NitinKumar004 merged 4 commits into
developmentfrom
feat/aws-apigateway-a2
Oct 4, 2026
Merged

NitinKumar004 merged 4 commits into
developmentfrom
feat/aws-apigateway-a2

Conversation

@NitinKumar004

@NitinKumar004 NitinKumar004 commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A2 from the API Gateway REST build-out. MOCK integrations used to return 502 on invoke. They now run end to end: the request template picks the status, an integration response is selected by selectionPattern, its mapping template renders the body, and its parameter mappings set the response headers. HTTP and HTTP_PROXY stay 502 until A3, as the plan scopes them.

Control plane

  • MethodResponse CRUD: PUT/GET/PATCH/DELETE .../methods/{m}/responses/{code}. A duplicate put gives ConflictException, and an unknown code gives NotFoundException: Invalid Response status code specified.
  • IntegrationResponse CRUD: .../integration/responses/{code}. The method must already declare the status code. responseParameters keys must name headers the method response declares, and an invalid selectionPattern is rejected.
  • New Integration fields with their PATCH paths: requestTemplates, requestParameters, passthroughBehavior (validated), contentHandling, cacheNamespace (defaults to the resource id), cacheKeyParameters and credentials. GetIntegration and GetMethod now embed integrationResponses and methodResponses.
  • New Method fields: requestParameters, requestModels and operationName, all patchable.

Data plane (MOCK)

  • The request template is chosen by Content-Type, which defaults to application/json. passthroughBehavior is applied: when NEVER, or WHEN_NO_TEMPLATES with templates defined, an unmatched type gets 415 {"message": "Unsupported Media Type"}.
  • The response template is chosen by Accept, falling back to application/json and then to the first template, and the response Content-Type follows the chosen template. $context.responseOverride.status and .header are honoured.
  • If no integration response matches and none is the default, or a template fails, the invoke returns 500 {"message": "Internal server error"} with x-amzn-ErrorType.
  • Templates get $input (body, json, path, params), $context, $stageVariables and $util (escapeJavaScript, parseJson, urlEncode/urlDecode, base64Encode/base64Decode).

Shared pieces

  • F2 hasn't landed, so this PR includes the VTL subset internal/vtl that the plan describes (§3.3). It covers #set/#if/#elseif/#else/#foreach/#break/#stop/#return, comments, references, literals, ranges, maps, operators, and the String/List/Map method bridge. #foreach is capped at 1000 iterations, every render has a step budget and a deadline, and #macro/#parse/#include/#evaluate/#define are rejected.
  • The SFN JSONPath subset has moved to internal/jsonpath, so ASL and the mapping templates share one implementation. ASL error messages are unchanged.

New state lives on the existing driver structs, so snapshot/restore and the deployment trees carry it without extra code. There is a round-trip test for that.

Limits

Templates are untrusted input, so the engine bounds every way they can grow:

  • A list or map that contains itself prints the way Java does: (this Collection) / (this Map). Encoding one as JSON is an error.
  • Printing, encoding, comparing and JSON parsing stop at 1000 levels of nesting. Directives and expressions stop at 100.
  • Output and any single string are capped at 10 MB, the API Gateway payload quota. A render may create at most 64 MB of strings and list or map entries, so doubling loops fail fast. Hitting any limit gives the 500 InternalServerErrorException response.
  • Templates are capped at 300 KB, the mapping-template quota. Puts and updates over the cap are rejected. Parsing is linear. Parsed templates are cached in an LRU capped at 64 MB, with each entry weighted by its estimated syntax-tree size.
  • Regex methods (split, replaceAll, replaceFirst) find one match at a time and charge each one before the next search. Literal patterns don't use the regex engine. Patterns with ^, \A, \b or \B fall back to doubling batches, and each batch is checked against the budget before it runs. Patterns over 64 KB are rejected.
  • At most GOMAXPROCS renders run at once. Their charges also come from one process-wide pool of 128 MB, so many concurrent requests can't each use a full budget. A render that runs out of budget triggers a GC.
  • $input.path walks check the template deadline every 1024 nodes. The Step Functions path (jsonpath.Eval) is unchanged.
  • $util.urlEncode now matches Java's URLEncoder (~ is encoded, * is left alone). escapeJavaScript leaves 0x7F as is.
  • $input.path and $input.json support [*], .* and ... Indefinite paths return a list, as Jayway does, and the number of matches is capped. Step Functions JSONPath still rejects these forms.

Testing

  • New tests:
    • provider tests for template context, passthrough, selection, response override, the response CRUD lifecycle and snapshot restore;
    • wire tests for invoke, errors, PATCH and redeploy gating;
    • VTL and JSONPath unit tests.
  • The wire tests fail on development.
  • Limit tests cover cycles, deep values, doubling (string, concat, list, map, replace, regex), output flooding, parser depth and size, linear parse time, the cache, and the gateway 500 path. A 60s+ fuzz run over the parser and evaluator, with a heap guard, found nothing.
  • Against serve: a self-referencing list or map returns 200 and prints itself, and the string and list doubling templates return 500 in about 5 ms and 30 ms. serve stays up, and RSS peaks at about 150 MB (47 MB idle).
  • Split repro against serve: 50 parallel requests, each with a 6 MB &a&a… body split by the request template, all return 500 within a second. Peak RSS is about 790 MB. A template that does no allocation peaks at about 740 MB on the same 50 bodies, so nearly all of that is the HTTP layer holding the request bodies, and templates add about 45 MB. Before this change the same run reached 3.5 GB.
  • Memory assertions: splits and regex replaces over 6 MB with millions of matches stop at the budget, and each allocates less than twice the budget (checked with a MemStats delta; skipped under -race, where sync.Pool drops items).
  • go build ./.... Vet and -race tests pass on the touched packages, persist and server/aws (including TestEveryHandlerDeclaresAuthorization). golangci-lint --new-from-rev reports 0 issues. Coverage docs are regenerated.
  • aws CLI against serve:
    • put/get method and integration responses;
    • curl invoke through the path and host forms, checking 200 with mapped header and body, and the 404 selected by 4\d\d;
    • redeploy gating, 415 under NEVER, and 500 with no matching response;
    • the 409, 404 and 400 error cases.
  • Terraform (aws provider) with aws_api_gateway_integration (MOCK, request_templates), aws_api_gateway_method_response and aws_api_gateway_integration_response: apply, plan clean, update (template, header mapping, response model), plan clean, destroy.

Comment thread internal/vtl/eval.go
func toInt(v any) (int, bool) {
switch t := v.(type) {
case int64:
return int(t), true
Comment thread internal/vtl/eval.go
case int64:
return int(t), true
case float64:
return int(t), true
@NitinKumar004
NitinKumar004 marked this pull request as ready for review October 4, 2026 13:32
@NitinKumar004
NitinKumar004 merged commit 770204c into development Oct 4, 2026
22 of 23 checks passed
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