Skip to content

Commit acd6589

Browse files
committed
add support for raw responses beginning with name of single field in response struct
1 parent 78b36e2 commit acd6589

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

src/aws.zig

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -709,8 +709,10 @@ pub fn Request(comptime request_action: anytype) type {
709709

710710
// Extract the first json key
711711
const key = firstJsonKey(data);
712-
const found_normal_json_response = std.mem.eql(u8, key, action.action_name ++ "Response") or
713-
std.mem.eql(u8, key, action.action_name ++ "Result");
712+
const found_normal_json_response =
713+
std.mem.eql(u8, key, action.action_name ++ "Response") or
714+
std.mem.eql(u8, key, action.action_name ++ "Result") or
715+
isOtherNormalResponse(response_types.NormalResponse, key);
714716
var raw_response_parsed = false;
715717
var stream = json.TokenStream.init(data);
716718
const parsed_response_ptr = blk: {
@@ -734,6 +736,7 @@ pub fn Request(comptime request_action: anytype) type {
734736
log.debug("Appears server has provided a raw response", .{});
735737
raw_response_parsed = true;
736738
const ptr = try options.client.allocator.create(response_types.NormalResponse);
739+
errdefer options.client.allocator.destroy(ptr);
737740
@field(ptr.*, std.meta.fields(action.Response)[0].name) =
738741
json.parse(response_types.RawResponse, &stream, parser_options) catch |e| {
739742
log.err(
@@ -761,6 +764,14 @@ pub fn Request(comptime request_action: anytype) type {
761764
};
762765
}
763766

767+
fn isOtherNormalResponse(comptime T: type, first_key: []const u8) bool {
768+
const fields = std.meta.fields(T);
769+
if (fields.len != 1) return false;
770+
const first_field = fields[0];
771+
if (!@hasDecl(T, "fieldNameFor")) return false;
772+
const expected_key = T.fieldNameFor(undefined, first_field.name);
773+
return std.mem.eql(u8, first_key, expected_key);
774+
}
764775
fn coerceFromString(comptime T: type, val: []const u8) anyerror!T {
765776
if (@typeInfo(T) == .optional) return try coerceFromString(@typeInfo(T).optional.child, val);
766777
// TODO: This is terrible...fix it
@@ -2270,3 +2281,59 @@ test "rest_xml_with_input: S3 put object" {
22702281
try std.testing.expectEqualStrings("AES256", result.response.server_side_encryption.?);
22712282
try std.testing.expectEqualStrings("37b51d194a7513e45b56f6524f2d51f2", result.response.e_tag.?);
22722283
}
2284+
test "raw ECR timestamps" {
2285+
// This is a way to test the json parsing. Ultimately the more robust tests
2286+
// should be preferred, but in this case we were tracking down an issue
2287+
// for which the root cause was the incorrect type being passed to the parse
2288+
// routine
2289+
const allocator = std.testing.allocator;
2290+
const ecr = (Services(.{.ecr}){}).ecr;
2291+
const options = json.ParseOptions{
2292+
.allocator = allocator,
2293+
.allow_camel_case_conversion = true, // new option
2294+
.allow_snake_case_conversion = true, // new option
2295+
.allow_unknown_fields = true, // new option. Cannot yet handle non-struct fields though
2296+
.allow_missing_fields = false, // new option. Cannot yet handle non-struct fields though
2297+
};
2298+
var stream = json.TokenStream.init(
2299+
\\{"authorizationData":[{"authorizationToken":"***","expiresAt":1.7385984915E9,"proxyEndpoint":"https://146325435496.dkr.ecr.us-west-2.amazonaws.com"}]}
2300+
);
2301+
const ptr = try json.parse(ecr.get_authorization_token.Response, &stream, options);
2302+
defer json.parseFree(ecr.get_authorization_token.Response, ptr, options);
2303+
}
2304+
test "json_1_1: ECR timestamps" {
2305+
// See: https://github.com/elerch/aws-sdk-for-zig/issues/5
2306+
// const old = std.testing.log_level;
2307+
// defer std.testing.log_level = old;
2308+
// std.testing.log_level = .debug;
2309+
const allocator = std.testing.allocator;
2310+
var test_harness = TestSetup.init(.{
2311+
.allocator = allocator,
2312+
.server_response =
2313+
\\{"authorizationData":[{"authorizationToken":"***","expiresAt":1.7385984915E9,"proxyEndpoint":"https://146325435496.dkr.ecr.us-west-2.amazonaws.com"}]}
2314+
// \\{"authorizationData":[{"authorizationToken":"***","expiresAt":1.738598491557E9,"proxyEndpoint":"https://146325435496.dkr.ecr.us-west-2.amazonaws.com"}]}
2315+
,
2316+
.server_response_headers = &.{
2317+
.{ .name = "Content-Type", .value = "application/json" },
2318+
.{ .name = "x-amzn-RequestId", .value = "QBI72OUIN8U9M9AG6PCSADJL4JVV4KQNSO5AEMVJF66Q9ASUAAJG" },
2319+
},
2320+
});
2321+
defer test_harness.deinit();
2322+
const options = try test_harness.start();
2323+
const ecr = (Services(.{.ecr}){}).ecr;
2324+
std.log.debug("Typeof response {}", .{@TypeOf(ecr.get_authorization_token.Response{})});
2325+
const call = try test_harness.client.call(ecr.get_authorization_token.Request{}, options);
2326+
defer call.deinit();
2327+
test_harness.stop();
2328+
// Request expectations
2329+
try std.testing.expectEqual(std.http.Method.POST, test_harness.request_options.request_method);
2330+
try std.testing.expectEqualStrings("/", test_harness.request_options.request_target);
2331+
try test_harness.request_options.expectHeader("X-Amz-Target", "AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken");
2332+
// Response expectations
2333+
try std.testing.expectEqualStrings("QBI72OUIN8U9M9AG6PCSADJL4JVV4KQNSO5AEMVJF66Q9ASUAAJG", call.response_metadata.request_id);
2334+
try std.testing.expectEqual(@as(usize, 1), call.response.authorization_data.?.len);
2335+
try std.testing.expectEqualStrings("***", call.response.authorization_data.?[0].authorization_token.?);
2336+
try std.testing.expectEqualStrings("https://146325435496.dkr.ecr.us-west-2.amazonaws.com", call.response.authorization_data.?[0].proxy_endpoint.?);
2337+
// try std.testing.expectEqual(@as(i64, 1.73859841557E9), call.response.authorization_data.?[0].expires_at.?);
2338+
try std.testing.expectEqual(@as(f128, 1.7385984915E9), call.response.authorization_data.?[0].expires_at.?);
2339+
}

src/json.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,7 @@ fn isMapPattern(comptime T: type) bool {
18951895
}
18961896

18971897
pub fn parse(comptime T: type, tokens: *TokenStream, options: ParseOptions) !T {
1898+
// std.log.debug("parsing {s} into type {s}", .{ tokens.slice, @typeName(T) });
18981899
const token = (try tokens.next()) orelse return error.UnexpectedEndOfJson;
18991900
return parseInternal(T, token, tokens, options);
19001901
}

0 commit comments

Comments
 (0)