add support for raw responses beginning with name of single field in response struct
This commit is contained in:
		
							parent
							
								
									96e2b7bbc1
								
							
						
					
					
						commit
						8c68dd6902
					
				
					 2 changed files with 70 additions and 2 deletions
				
			
		
							
								
								
									
										71
									
								
								src/aws.zig
									
										
									
									
									
								
							
							
						
						
									
										71
									
								
								src/aws.zig
									
										
									
									
									
								
							|  | @ -709,8 +709,10 @@ pub fn Request(comptime request_action: anytype) type { | |||
| 
 | ||||
|             // Extract the first json key | ||||
|             const key = firstJsonKey(data); | ||||
|             const found_normal_json_response = std.mem.eql(u8, key, action.action_name ++ "Response") or | ||||
|                 std.mem.eql(u8, key, action.action_name ++ "Result"); | ||||
|             const found_normal_json_response = | ||||
|                 std.mem.eql(u8, key, action.action_name ++ "Response") or | ||||
|                 std.mem.eql(u8, key, action.action_name ++ "Result") or | ||||
|                 isOtherNormalResponse(response_types.NormalResponse, key); | ||||
|             var raw_response_parsed = false; | ||||
|             var stream = json.TokenStream.init(data); | ||||
|             const parsed_response_ptr = blk: { | ||||
|  | @ -734,6 +736,7 @@ pub fn Request(comptime request_action: anytype) type { | |||
|                 log.debug("Appears server has provided a raw response", .{}); | ||||
|                 raw_response_parsed = true; | ||||
|                 const ptr = try options.client.allocator.create(response_types.NormalResponse); | ||||
|                 errdefer options.client.allocator.destroy(ptr); | ||||
|                 @field(ptr.*, std.meta.fields(action.Response)[0].name) = | ||||
|                     json.parse(response_types.RawResponse, &stream, parser_options) catch |e| { | ||||
|                     log.err( | ||||
|  | @ -761,6 +764,14 @@ pub fn Request(comptime request_action: anytype) type { | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| fn isOtherNormalResponse(comptime T: type, first_key: []const u8) bool { | ||||
|     const fields = std.meta.fields(T); | ||||
|     if (fields.len != 1) return false; | ||||
|     const first_field = fields[0]; | ||||
|     if (!@hasDecl(T, "fieldNameFor")) return false; | ||||
|     const expected_key = T.fieldNameFor(undefined, first_field.name); | ||||
|     return std.mem.eql(u8, first_key, expected_key); | ||||
| } | ||||
| fn coerceFromString(comptime T: type, val: []const u8) anyerror!T { | ||||
|     if (@typeInfo(T) == .Optional) return try coerceFromString(@typeInfo(T).Optional.child, val); | ||||
|     // TODO: This is terrible...fix it | ||||
|  | @ -2270,3 +2281,59 @@ test "rest_xml_with_input: S3 put object" { | |||
|     try std.testing.expectEqualStrings("AES256", result.response.server_side_encryption.?); | ||||
|     try std.testing.expectEqualStrings("37b51d194a7513e45b56f6524f2d51f2", result.response.e_tag.?); | ||||
| } | ||||
| test "raw ECR timestamps" { | ||||
|     // This is a way to test the json parsing. Ultimately the more robust tests | ||||
|     // should be preferred, but in this case we were tracking down an issue | ||||
|     // for which the root cause was the incorrect type being passed to the parse | ||||
|     // routine | ||||
|     const allocator = std.testing.allocator; | ||||
|     const ecr = (Services(.{.ecr}){}).ecr; | ||||
|     const options = json.ParseOptions{ | ||||
|         .allocator = allocator, | ||||
|         .allow_camel_case_conversion = true, // new option | ||||
|         .allow_snake_case_conversion = true, // new option | ||||
|         .allow_unknown_fields = true, // new option. Cannot yet handle non-struct fields though | ||||
|         .allow_missing_fields = false, // new option. Cannot yet handle non-struct fields though | ||||
|     }; | ||||
|     var stream = json.TokenStream.init( | ||||
|         \\{"authorizationData":[{"authorizationToken":"***","expiresAt":1.7385984915E9,"proxyEndpoint":"https://146325435496.dkr.ecr.us-west-2.amazonaws.com"}]} | ||||
|     ); | ||||
|     const ptr = try json.parse(ecr.get_authorization_token.Response, &stream, options); | ||||
|     defer json.parseFree(ecr.get_authorization_token.Response, ptr, options); | ||||
| } | ||||
| test "json_1_1: ECR timestamps" { | ||||
|     // See: https://github.com/elerch/aws-sdk-for-zig/issues/5 | ||||
|     // const old = std.testing.log_level; | ||||
|     // defer std.testing.log_level = old; | ||||
|     // std.testing.log_level = .debug; | ||||
|     const allocator = std.testing.allocator; | ||||
|     var test_harness = TestSetup.init(.{ | ||||
|         .allocator = allocator, | ||||
|         .server_response = | ||||
|         \\{"authorizationData":[{"authorizationToken":"***","expiresAt":1.7385984915E9,"proxyEndpoint":"https://146325435496.dkr.ecr.us-west-2.amazonaws.com"}]} | ||||
|         // \\{"authorizationData":[{"authorizationToken":"***","expiresAt":1.738598491557E9,"proxyEndpoint":"https://146325435496.dkr.ecr.us-west-2.amazonaws.com"}]} | ||||
|         , | ||||
|         .server_response_headers = &.{ | ||||
|             .{ .name = "Content-Type", .value = "application/json" }, | ||||
|             .{ .name = "x-amzn-RequestId", .value = "QBI72OUIN8U9M9AG6PCSADJL4JVV4KQNSO5AEMVJF66Q9ASUAAJG" }, | ||||
|         }, | ||||
|     }); | ||||
|     defer test_harness.deinit(); | ||||
|     const options = try test_harness.start(); | ||||
|     const ecr = (Services(.{.ecr}){}).ecr; | ||||
|     std.log.debug("Typeof response {}", .{@TypeOf(ecr.get_authorization_token.Response{})}); | ||||
|     const call = try test_harness.client.call(ecr.get_authorization_token.Request{}, options); | ||||
|     defer call.deinit(); | ||||
|     test_harness.stop(); | ||||
|     // Request expectations | ||||
|     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 test_harness.request_options.expectHeader("X-Amz-Target", "AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken"); | ||||
|     // Response expectations | ||||
|     try std.testing.expectEqualStrings("QBI72OUIN8U9M9AG6PCSADJL4JVV4KQNSO5AEMVJF66Q9ASUAAJG", call.response_metadata.request_id); | ||||
|     try std.testing.expectEqual(@as(usize, 1), call.response.authorization_data.?.len); | ||||
|     try std.testing.expectEqualStrings("***", call.response.authorization_data.?[0].authorization_token.?); | ||||
|     try std.testing.expectEqualStrings("https://146325435496.dkr.ecr.us-west-2.amazonaws.com", call.response.authorization_data.?[0].proxy_endpoint.?); | ||||
|     // try std.testing.expectEqual(@as(i64, 1.73859841557E9), call.response.authorization_data.?[0].expires_at.?); | ||||
|     try std.testing.expectEqual(@as(f128, 1.7385984915E9), call.response.authorization_data.?[0].expires_at.?); | ||||
| } | ||||
|  |  | |||
|  | @ -1895,6 +1895,7 @@ fn isMapPattern(comptime T: type) bool { | |||
| } | ||||
| 
 | ||||
| pub fn parse(comptime T: type, tokens: *TokenStream, options: ParseOptions) !T { | ||||
|     // std.log.debug("parsing {s} into type {s}", .{ tokens.slice, @typeName(T) }); | ||||
|     const token = (try tokens.next()) orelse return error.UnexpectedEndOfJson; | ||||
|     return parseInternal(T, token, tokens, options); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue