switch sqs query test (json) with sts query test (xml) and fix response parsing
AWS-Zig Build / build-zig-0.11.0-amd64-host (push) Successful in 5m22s Details

This commit is contained in:
Emil Lerch 2024-02-29 20:41:03 -08:00
parent 55298f7575
commit 6df02b1074
Signed by: lobo
GPG Key ID: A7B62D657EF764F8
2 changed files with 105 additions and 15 deletions

View File

@ -457,6 +457,30 @@ pub fn Request(comptime request_action: anytype) type {
}
}
fn findResult(element: *xml_shaper.Element, options: xml_shaper.ParseOptions) *xml_shaper.Element {
_ = options;
// We're looking for a very specific pattern here. We want only two direct
// children. The first one must end with "Result", and the second should
// be our ResponseMetadata node
var children = element.elements();
var found_metadata = false;
var result_child: ?*xml_shaper.Element = null;
var inx: usize = 0;
while (children.next()) |child| : (inx += 1) {
if (std.mem.eql(u8, child.tag, "ResponseMetadata")) {
found_metadata = true;
continue;
}
if (std.mem.endsWith(u8, child.tag, "Result")) {
result_child = child;
continue;
}
if (inx > 1) return element;
return element; // It should only be those two
}
return result_child orelse element;
}
fn xmlReturn(request: awshttp.HttpRequest, options: Options, result: awshttp.HttpResult) !FullResponseType {
// Server shape be all like:
//
@ -481,7 +505,7 @@ pub fn Request(comptime request_action: anytype) type {
// }
//
// Big thing is that requestid, which we'll need to fetch "manually"
const xml_options = xml_shaper.ParseOptions{ .allocator = options.client.allocator };
const xml_options = xml_shaper.ParseOptions{ .allocator = options.client.allocator, .elementToParse = findResult };
var body: []const u8 = result.body;
var free_body = false;
if (result.body.len < 20) {
@ -1584,24 +1608,31 @@ test "query_no_input: sts getCallerIdentity comptime" {
try std.testing.expectEqualStrings("123456789012", call.response.account.?);
try std.testing.expectEqualStrings("8f0d54da-1230-40f7-b4ac-95015c4b84cd", call.response_metadata.request_id);
}
test "query_with_input: sqs listQueues runtime" {
if (true) return error.SkipZigTest; // sqs switched from query to json recently
test "query_with_input: sts getAccessKeyInfo runtime" {
// sqs switched from query to json in aws sdk for go v2 commit f5a08768ef820ff5efd62a49ba50c61c9ca5dbcb
const allocator = std.testing.allocator;
var test_harness = TestSetup.init(allocator, .{
.allocator = allocator,
.server_response =
\\{"ListQueuesResponse":{"ListQueuesResult":{"NextExclusiveStartQueueName":null,"NextToken":null,"queueUrls":null},"ResponseMetadata":{"RequestId":"a85e390b-b866-590e-8cae-645f2bbe59c5"}}}
\\<GetAccessKeyInfoResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
\\ <GetAccessKeyInfoResult>
\\ <Account>123456789012</Account>
\\ </GetAccessKeyInfoResult>
\\ <ResponseMetadata>
\\ <RequestId>ec85bf29-1ef0-459a-930e-6446dd14a286</RequestId>
\\ </ResponseMetadata>
\\</GetAccessKeyInfoResponse>
,
.server_response_headers = @constCast(&[_][2][]const u8{
.{ "Content-Type", "application/json" },
.{ "x-amzn-RequestId", "a85e390b-b866-590e-8cae-645f2bbe59c5" },
.{ "Content-Type", "text/xml" },
.{ "x-amzn-RequestId", "ec85bf29-1ef0-459a-930e-6446dd14a286" },
}),
});
defer test_harness.deinit();
const options = try test_harness.start();
const sqs = (Services(.{.sqs}){}).sqs;
const call = try test_harness.client.call(sqs.list_queues.Request{
.queue_name_prefix = "s",
const sts = (Services(.{.sts}){}).sts;
const call = try test_harness.client.call(sts.get_access_key_info.Request{
.access_key_id = "ASIAYAM4POHXJNKTYFUN",
}, options);
defer call.deinit();
test_harness.stop();
@ -1609,12 +1640,12 @@ test "query_with_input: sqs listQueues runtime" {
try std.testing.expectEqual(std.http.Method.POST, test_harness.request_options.request_method);
try std.testing.expectEqualStrings("/", test_harness.request_options.request_target);
try std.testing.expectEqualStrings(
\\Action=ListQueues&Version=2012-11-05&QueueNamePrefix=s
\\Action=GetAccessKeyInfo&Version=2011-06-15&AccessKeyId=ASIAYAM4POHXJNKTYFUN
, test_harness.request_options.request_body);
// Response expectations
// TODO: We can get a lot better with this under test
try std.testing.expect(call.response.queue_urls == null);
try std.testing.expectEqualStrings("a85e390b-b866-590e-8cae-645f2bbe59c5", call.response_metadata.request_id);
try std.testing.expect(call.response.account != null);
try std.testing.expectEqualStrings("123456789012", call.response.account.?);
try std.testing.expectEqualStrings("ec85bf29-1ef0-459a-930e-6446dd14a286", call.response_metadata.request_id);
}
test "json_1_0_query_with_input: dynamodb listTables runtime" {
const allocator = std.testing.allocator;

View File

@ -4,6 +4,8 @@ const date = @import("date.zig");
const log = std.log.scoped(.xml_shaper);
pub const Element = xml.Element;
pub fn Parsed(comptime T: type) type {
return struct {
// Forcing an arean allocator isn't my favorite choice here, but
@ -70,6 +72,8 @@ fn deinitObject(allocator: std.mem.Allocator, obj: anytype) void {
pub const ParseOptions = struct {
allocator: ?std.mem.Allocator = null,
match_predicate_ptr: ?*const fn (a: []const u8, b: []const u8, options: xml.PredicateOptions) anyerror!bool = null,
/// defines a function to use to locate an element other than the root of the document for parsing
elementToParse: ?*const fn (element: *Element, options: ParseOptions) *Element = null,
};
pub fn parse(comptime T: type, source: []const u8, options: ParseOptions) !Parsed(T) {
@ -86,7 +90,8 @@ pub fn parse(comptime T: type, source: []const u8, options: ParseOptions) !Parse
.match_predicate_ptr = options.match_predicate_ptr,
};
return Parsed(T).init(arena_allocator, try parseInternal(T, parsed.root, opts), parsed);
const root = if (options.elementToParse) |e| e(parsed.root, opts) else parsed.root;
return Parsed(T).init(arena_allocator, try parseInternal(T, root, opts), parsed);
}
fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions) !T {
@ -244,6 +249,7 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
// Zig compiler bug circa 0.9.0. Using "and !found_value"
// in the if statement above will trigger assertion failure
if (!found_value) {
log.debug("Child element not found, but field optional. Setting {s}=null", .{field.name});
// @compileLog("Optional: Field name ", field.name, ", type ", field.type);
@field(r, field.name) = null;
fields_set = fields_set + 1;
@ -625,12 +631,65 @@ test "can parse something serious" {
\\</DescribeRegionsResponse>
;
// const ServerResponse = struct { DescribeRegionsResponse: describe_regions.Response, };
const parsed_data = try parse(describe_regions.Response, data, .{ .allocator = allocator });
const parsed_data = try parse(describe_regions.Response, data, .{ .allocator = allocator, .elementToParse = findResult });
defer parsed_data.deinit();
try testing.expect(parsed_data.parsed_value.regions != null);
try testing.expectEqualStrings("eu-north-1", parsed_data.parsed_value.regions.?[0].region_name.?);
try testing.expectEqualStrings("ec2.eu-north-1.amazonaws.com", parsed_data.parsed_value.regions.?[0].endpoint.?);
}
const StsGetAccesskeyInfoResponse: type = struct {
account: ?[]const u8 = null,
pub fn fieldNameFor(_: @This(), comptime field_name: []const u8) []const u8 {
const mappings = .{
.account = "Account",
};
return @field(mappings, field_name);
}
};
fn findResult(element: *xml.Element, options: ParseOptions) *xml.Element {
_ = options;
// We're looking for a very specific pattern here. We want only two direct
// children. The first one must end with "Result", and the second should
// be our ResponseMetadata node
var children = element.elements();
var found_metadata = false;
var result_child: ?*xml.Element = null;
var inx: usize = 0;
while (children.next()) |child| : (inx += 1) {
if (std.mem.eql(u8, child.tag, "ResponseMetadata")) {
found_metadata = true;
continue;
}
if (std.mem.endsWith(u8, child.tag, "Result")) {
result_child = child;
continue;
}
if (inx > 1) return element;
return element; // It should only be those two
}
return result_child orelse element;
}
test "can parse a result within a response" {
log.debug("", .{});
const allocator = std.testing.allocator;
const data =
\\<GetAccessKeyInfoResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
\\ <GetAccessKeyInfoResult>
\\ <Account>123456789012</Account>
\\ </GetAccessKeyInfoResult>
\\ <ResponseMetadata>
\\ <RequestId>ec85bf29-1ef0-459a-930e-6446dd14a286</RequestId>
\\ </ResponseMetadata>
\\</GetAccessKeyInfoResponse>
;
const parsed_data = try parse(StsGetAccesskeyInfoResponse, data, .{ .allocator = allocator, .elementToParse = findResult });
defer parsed_data.deinit();
// Response expectations
try std.testing.expect(parsed_data.parsed_value.account != null);
try std.testing.expectEqualStrings("123456789012", parsed_data.parsed_value.account.?);
}
test "compiler assertion failure 2" {
// std.testing.log_level = .debug;