Skip to content

[FEATURE] schema_driven_xml: pass scalar payload siblings through the output envelope (pagination response tokens are dropped) #117

Description

@jeffreyaven

Problem

schema_driven_xml_v0.1.0 emits only {"<listProperty>": [rows]}. Any scalar siblings of the row carrier in the response payload - notably pagination tokens (<nextToken>, <NextMarker>, <Marker>, S3's <NextContinuationToken>) - are dropped.

The pagination machinery extracts the response token via JSONPath against the response rawBody (extractNextPageTokenFromBody -> basicResponse.ExtractElement, which reads r.rawBody). For ops using this transform, rawBody IS the transformed document (getOverridenResponse unmarshals the transformer's out-stream), so a config.pagination.responseToken of e.g. $.nextToken can never resolve, and traversal silently ends after page one.

Net effect: config.pagination works for untransformed responses (rest-json / aws-json AWS protocols) but cannot work for query / ec2 / rest-xml, which all route through this walker.

Proposed fix

In pkg/stream_transform/schema_driven_xml.go, pass scalar (string-typed) payload siblings through to the output envelope alongside the row list. Sketch (validated locally against the AWS provider; all existing walker tests pass):

func (t *schemaDrivenXMLTransformer) write(rows []interface{}, payload map[string]interface{}) error {
	out := map[string]interface{}{t.listProperty: rows}
	// Pass scalar payload siblings (pagination tokens - <nextToken>,
	// <NextMarker>, ... - plus inert metadata like <requestId>) through to
	// the output envelope: the pagination machinery extracts the response
	// token from the transformed document, so dropping them here would end
	// traversal after page one.
	for k, v := range payload {
		if k == t.listProperty {
			continue
		}
		if s, ok := v.(string); ok {
			out[k] = s
		}
	}
	b, err := json.Marshal(out)
	if err != nil {
		return err
	}
	_, writeErr := t.outStream.Write(b)
	return writeErr
}

with Transform() passing payload in, and nil at the two empty-result call sites (return t.write(make([]interface{}, 0), nil)).

Only top-level strings are passed through, so row projection, objectKey descent and column inference are unaffected (the schema_override still only declares the list property; extra raw keys are invisible to DDL).

Notes from local validation:

  • Existing walker tests keep passing, but the test helper runWalker unmarshals the envelope as map[string][]map[string]interface{} and needs loosening to map[string]interface{}, since envelopes may now carry scalar siblings (e.g. requestId; also <Stacks/> empty self-closing lists surface as an empty-string sibling - harmless).
  • Suggested regression test: an ec2-archetype list body containing <nextToken>tok-page-2</nextToken>, asserting env["nextToken"] == "tok-page-2" and that line_items is unchanged.

Downstream

The stackql AWS provider generator already emits config.pagination.{requestToken,responseToken} blocks for rest-json / aws-json list ops (~2,650 methods, verified live: DynamoDB ListTables with Limit=1 traverses all pages, re-injecting ExclusiveStartTableName per page). Emission for query / ec2 / rest-xml is gated off pending this fix - once released, the provider only needs the protocol gate lifted in _pagination_breadcrumbs (response token key = $. + the output member's XML wire name, which this passthrough preserves verbatim).

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions