diff --git a/ballerina-tests/http-advanced-tests/Ballerina.toml b/ballerina-tests/http-advanced-tests/Ballerina.toml index 43d8aa8f86..d30b81a7be 100644 --- a/ballerina-tests/http-advanced-tests/Ballerina.toml +++ b/ballerina-tests/http-advanced-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_advanced_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-advanced-tests/Dependencies.toml b/ballerina-tests/http-advanced-tests/Dependencies.toml index 5949cb4946..c05fe4a76a 100644 --- a/ballerina-tests/http-advanced-tests/Dependencies.toml +++ b/ballerina-tests/http-advanced-tests/Dependencies.toml @@ -72,7 +72,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -105,7 +105,7 @@ modules = [ [[package]] org = "ballerina" name = "http_advanced_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "file"}, @@ -125,7 +125,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-client-tests/Ballerina.toml b/ballerina-tests/http-client-tests/Ballerina.toml index 923da6498d..e9e28a2301 100644 --- a/ballerina-tests/http-client-tests/Ballerina.toml +++ b/ballerina-tests/http-client-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_client_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-client-tests/Dependencies.toml b/ballerina-tests/http-client-tests/Dependencies.toml index 2cdffe5129..d5562e4b98 100644 --- a/ballerina-tests/http-client-tests/Dependencies.toml +++ b/ballerina-tests/http-client-tests/Dependencies.toml @@ -69,7 +69,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -102,7 +102,7 @@ modules = [ [[package]] org = "ballerina" name = "http_client_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "http"}, @@ -121,7 +121,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-client-tests/tests/sc_res_binding_tests.bal b/ballerina-tests/http-client-tests/tests/sc_res_binding_tests.bal index 5cff4ba297..0cda84e845 100644 --- a/ballerina-tests/http-client-tests/tests/sc_res_binding_tests.bal +++ b/ballerina-tests/http-client-tests/tests/sc_res_binding_tests.bal @@ -102,12 +102,24 @@ type AlbumNotFound record {| Headers headers; |}; +type AlbumNotFoundDefault record {| + *http:DefaultStatusCodeResponse; + ErrorMessage body; + Headers headers; +|}; + type AlbumFound record {| *http:Ok; Album body; Headers headers; |}; +type AlbumFoundDefault record {| + *http:DefaultStatusCodeResponse; + Album body; + Headers headers; +|}; + type AlbumFoundMock1 record {| *http:Ok; Album|MockAlbum body; @@ -251,28 +263,75 @@ function testGetSuccessStatusCodeResponse() returns error? { test:assertEquals(albumFound.headers.req\-id, 1, "Invalid req-id header"); test:assertEquals(albumFound.mediaType, "application/json", "Invalid media type"); - AlbumFound|AlbumNotFound res4 = check albumClient->/albums/'1; - if res4 is AlbumFound { - test:assertEquals(res4.body, expectedAlbum, "Invalid album returned"); - test:assertEquals(res4.headers.user\-id, "user-1", "Invalid user-id header"); - test:assertEquals(res4.headers.req\-id, 1, "Invalid req-id header"); - test:assertEquals(res4.mediaType, "application/json", "Invalid media type"); + AlbumFound|AlbumNotFound res1 = check albumClient->/albums/'1; + if res1 is AlbumFound { + test:assertEquals(res1.body, expectedAlbum, "Invalid album returned"); + test:assertEquals(res1.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res1.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res1.mediaType, "application/json", "Invalid media type"); } else { test:assertFail("Invalid response type"); } - AlbumNotFound|error res8 = albumClient->/albums/'1; - if res8 is error { - test:assertTrue(res8 is http:StatusCodeResponseBindingError); - test:assertEquals(res8.message(), "incompatible AlbumNotFound found for response with 200", + AlbumNotFound|error res2 = albumClient->/albums/'1; + if res2 is error { + test:assertTrue(res2 is http:StatusCodeResponseBindingError); + test:assertEquals(res2.message(), "incompatible type: AlbumNotFound found for the response with status code: 200", "Invalid error message"); - error? cause = res8.cause(); + error? cause = res2.cause(); if cause is error { test:assertEquals(cause.message(), "no 'anydata' type found in the target type", "Invalid cause error message"); } } else { test:assertFail("Invalid response type"); } + + AlbumFoundDefault albumFoundDefault = check albumClient->get("/albums/1"); + test:assertEquals(albumFoundDefault.body, expectedAlbum, "Invalid album returned"); + test:assertEquals(albumFoundDefault.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(albumFoundDefault.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(albumFoundDefault.mediaType, "application/json", "Invalid media type"); + test:assertEquals(albumFoundDefault.status.code, 200, "Invalid status code"); + + AlbumFoundDefault|error res3 = albumClient->get("/albums/1"); + if res3 is AlbumFoundDefault { + test:assertEquals(res3.body, expectedAlbum, "Invalid album returned"); + test:assertEquals(res3.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res3.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res3.mediaType, "application/json", "Invalid media type"); + test:assertEquals(res3.status.code, 200, "Invalid status code"); + } else { + test:assertFail("Invalid response type"); + } + + AlbumNotFound|AlbumFoundDefault res4 = check albumClient->/albums/'1; + if res4 is AlbumFoundDefault { + test:assertEquals(res4.body, expectedAlbum, "Invalid album returned"); + test:assertEquals(res4.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res4.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res4.mediaType, "application/json", "Invalid media type"); + test:assertEquals(res4.status.code, 200, "Invalid status code"); + } else { + test:assertFail("Invalid response type"); + } + + http:DefaultStatusCodeResponse res5 = check albumClient->get("/albums/1"); + test:assertEquals(res5?.body, expectedAlbum, "Invalid album returned"); + test:assertEquals(res5.headers["user-id"], "user-1", "Invalid user-id header"); + test:assertEquals(res5.headers["req-id"], "1", "Invalid req-id header"); + test:assertEquals(res5.mediaType, "application/json", "Invalid media type"); + test:assertEquals(res5.status.code, 200, "Invalid status code"); + + http:DefaultStatusCodeResponse|AlbumFound|AlbumFoundDefault res6 = check albumClient->get("/albums/1"); + if res6 is AlbumFound { + test:assertEquals(res6?.body, expectedAlbum, "Invalid album returned"); + test:assertEquals(res6?.headers["user-id"], "user-1", "Invalid user-id header"); + test:assertEquals(res6?.headers["req-id"], 1, "Invalid req-id header"); + test:assertEquals(res6.mediaType, "application/json", "Invalid media type"); + test:assertEquals(res6.status.code, 200, "Invalid status code"); + } else { + test:assertFail("Invalid response type"); + } } @test:Config {} @@ -284,15 +343,15 @@ function testGetFailureStatusCodeResponse() returns error? { test:assertEquals(albumNotFound.headers.req\-id, 1, "Invalid req-id header"); test:assertEquals(albumNotFound.mediaType, "application/json", "Invalid media type"); - AlbumFound|error res6 = albumClient->get("/albums/4"); - if res6 is error { - test:assertTrue(res6 is http:ClientRequestError); - test:assertTrue(res6 is http:StatusCodeResponseBindingError); - test:assertEquals(res6.message(), "incompatible AlbumFound found for response with 404", "Invalid error message"); - test:assertEquals(res6.detail()["statusCode"], 404, "Invalid status code"); - test:assertEquals(res6.detail()["body"], expectedErrorMessage, "Invalid error message"); - if res6.detail()["headers"] is map { - map headers = check res6.detail()["headers"].ensureType(); + AlbumFound|error res1 = albumClient->get("/albums/4"); + if res1 is error { + test:assertTrue(res1 is http:ClientRequestError); + test:assertTrue(res1 is http:StatusCodeResponseBindingError); + test:assertEquals(res1.message(), "incompatible type: AlbumFound found for the response with status code: 404", "Invalid error message"); + test:assertEquals(res1.detail()["statusCode"], 404, "Invalid status code"); + test:assertEquals(res1.detail()["body"], expectedErrorMessage, "Invalid error message"); + if res1.detail()["headers"] is map { + map headers = check res1.detail()["headers"].ensureType(); test:assertEquals(headers.get("user-id")[0], "user-1", "Invalid user-id header"); test:assertEquals(headers.get("req-id")[0], "1", "Invalid req-id header"); } else { @@ -301,54 +360,82 @@ function testGetFailureStatusCodeResponse() returns error? { } else { test:assertFail("Invalid response type"); } + + AlbumNotFoundDefault albumNotFoundDefault = check albumClient->get("/albums/4"); + test:assertEquals(albumNotFoundDefault.body, expectedErrorMessage, "Invalid error message"); + test:assertEquals(albumNotFoundDefault.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(albumNotFoundDefault.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(albumNotFoundDefault.mediaType, "application/json", "Invalid media type"); + test:assertEquals(albumNotFoundDefault.status.code, 404, "Invalid status code"); + + AlbumFound|http:DefaultStatusCodeResponse|error res2 = albumClient->get("/albums/4"); + if res2 is http:DefaultStatusCodeResponse { + test:assertEquals(res2?.body, expectedErrorMessage, "Invalid error message"); + test:assertEquals(res2.headers["user-id"], "user-1", "Invalid user-id header"); + test:assertEquals(res2.headers["req-id"], "1", "Invalid req-id header"); + test:assertEquals(res2.mediaType, "application/json", "Invalid media type"); + test:assertEquals(res2.status.code, 404, "Invalid status code"); + } else { + test:assertFail("Invalid response type"); + } + + AlbumFound|AlbumNotFound|http:DefaultStatusCodeResponse res3 = check albumClient->get("/albums/4"); + if res3 is AlbumNotFound { + test:assertEquals(res3.body, expectedErrorMessage, "Invalid error message"); + test:assertEquals(res3.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res3.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res3.mediaType, "application/json", "Invalid media type"); + } else { + test:assertFail("Invalid response type"); + } } @test:Config {} function testUnionPayloadBindingWithStatusCodeResponse() returns error? { - AlbumFound|AlbumNotFound|AlbumFoundMock1 res7 = check albumClient->/albums/'1; - if res7 is AlbumFound { - test:assertEquals(res7.body, albums.get("1"), "Invalid album returned"); - test:assertEquals(res7.headers.user\-id, "user-1", "Invalid user-id header"); - test:assertEquals(res7.headers.req\-id, 1, "Invalid req-id header"); - test:assertEquals(res7.mediaType, "application/json", "Invalid media type"); + AlbumFound|AlbumNotFound|AlbumFoundMock1 res1 = check albumClient->/albums/'1; + if res1 is AlbumFound { + test:assertEquals(res1.body, albums.get("1"), "Invalid album returned"); + test:assertEquals(res1.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res1.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res1.mediaType, "application/json", "Invalid media type"); } else { test:assertFail("Invalid response type"); } - AlbumFoundMock1|AlbumFound|AlbumNotFound res8 = check albumClient->get("/albums/1"); - if res8 is AlbumFoundMock1 { - test:assertEquals(res8.body, albums.get("1"), "Invalid album returned"); - test:assertEquals(res8.headers.user\-id, "user-1", "Invalid user-id header"); - test:assertEquals(res8.headers.req\-id, 1, "Invalid req-id header"); - test:assertEquals(res8.mediaType, "application/json", "Invalid media type"); + AlbumFoundMock1|AlbumFound|AlbumNotFound res2 = check albumClient->get("/albums/1"); + if res2 is AlbumFoundMock1 { + test:assertEquals(res2.body, albums.get("1"), "Invalid album returned"); + test:assertEquals(res2.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res2.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res2.mediaType, "application/json", "Invalid media type"); } else { test:assertFail("Invalid response type"); } - AlbumFoundMock2|AlbumFound|AlbumFoundMock1|AlbumNotFound res9 = check albumClient->/albums/'1; - if res9 is AlbumFoundMock2 { - test:assertEquals(res9.body, albums.get("1"), "Invalid album returned"); - test:assertEquals(res9.headers.user\-id, "user-1", "Invalid user-id header"); - test:assertEquals(res9.headers.req\-id, 1, "Invalid req-id header"); - test:assertEquals(res9.mediaType, "application/json", "Invalid media type"); + AlbumFoundMock2|AlbumFound|AlbumFoundMock1|AlbumNotFound res3 = check albumClient->/albums/'1; + if res3 is AlbumFoundMock2 { + test:assertEquals(res3.body, albums.get("1"), "Invalid album returned"); + test:assertEquals(res3.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res3.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res3.mediaType, "application/json", "Invalid media type"); } else { test:assertFail("Invalid response type"); } - AlbumFoundMock3|AlbumFound|AlbumFoundMock1|AlbumFoundMock2|AlbumNotFound res10 = check albumClient->get("/albums/1"); - if res10 is AlbumFoundMock3 { - test:assertEquals(res10.body, {...albums.get("1"), "type": "mock"}, "Invalid album returned"); - test:assertEquals(res10.headers.user\-id, "user-1", "Invalid user-id header"); - test:assertEquals(res10.headers.req\-id, 1, "Invalid req-id header"); - test:assertEquals(res10.mediaType, "application/json", "Invalid media type"); + AlbumFoundMock3|AlbumFound|AlbumFoundMock1|AlbumFoundMock2|AlbumNotFound res4 = check albumClient->get("/albums/1"); + if res4 is AlbumFoundMock3 { + test:assertEquals(res4.body, {...albums.get("1"), "type": "mock"}, "Invalid album returned"); + test:assertEquals(res4.headers.user\-id, "user-1", "Invalid user-id header"); + test:assertEquals(res4.headers.req\-id, 1, "Invalid req-id header"); + test:assertEquals(res4.mediaType, "application/json", "Invalid media type"); } else { test:assertFail("Invalid response type"); } - AlbumFoundInvalid|AlbumFound|AlbumNotFound|error res11 = albumClient->/albums/'1; - if res11 is error { - test:assertTrue(res11 is http:PayloadBindingError); - test:assertTrue(res11.message().includes("Payload binding failed: 'map' value cannot be" + + AlbumFoundInvalid|AlbumFound|AlbumNotFound|error res5 = albumClient->/albums/'1; + if res5 is error { + test:assertTrue(res5 is http:PayloadBindingError); + test:assertTrue(res5.message().includes("Payload binding failed: 'map' value cannot be" + " converted to 'http_client_tests:AlbumInvalid"), "Invalid error message"); } else { test:assertFail("Invalid response type"); @@ -392,8 +479,16 @@ function testStatusCodeBindingWithDifferentHeaders() returns error? { record {|*http:Ok; AdditionalMissingHeaders headers;|}|error res6 = albumClient->/albums/'1; if res6 is error { - test:assertTrue(res6 is http:HeaderNotFoundError); - test:assertEquals(res6.message(), "no header value found for 'x-content-type'", "Invalid error message"); + test:assertTrue(res6 is http:HeaderBindingError); + test:assertTrue(res6 is http:StatusCodeResponseBindingError); + test:assertEquals(res6.message(), "header binding failed"); + error? cause = res6.cause(); + if cause is error { + test:assertTrue(cause is http:HeaderNotFoundError); + test:assertEquals(cause.message(), "no header value found for 'x-content-type'", "Invalid cause error message"); + } else { + test:assertFail("Invalid cause error type"); + } } else { test:assertFail("Invalid response type"); } diff --git a/ballerina-tests/http-client-tests/tests/sc_res_binding_with_all_status_codes_tests.bal b/ballerina-tests/http-client-tests/tests/sc_res_binding_with_all_status_codes_tests.bal index d393775dcf..030b37ff51 100644 --- a/ballerina-tests/http-client-tests/tests/sc_res_binding_with_all_status_codes_tests.bal +++ b/ballerina-tests/http-client-tests/tests/sc_res_binding_with_all_status_codes_tests.bal @@ -213,7 +213,7 @@ function getStatusCodeResponse(int code) returns http:StatusCodeResponse { return {body: {msg: "Network authentication required error"}, mediaType: "application/org+json", headers: {"x-error": "Network authentication required"}}; } _ => { - return {body: {msg: "Bad request with unknown status code"}, mediaType: "application/org+json", headers: {"x-error": "Unknown status code"}}; + return {body: {msg: string`Default status code response with code: ${code}`}, mediaType: "application/org+json", headers: {"x-error": "Default status code"}, status: new(code)}; } } } @@ -467,10 +467,6 @@ function testSCResBindingWith5XXStatusCodes() returns error? { http:NetworkAuthenticationRequired networkAuthenticationRequiredResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 511); test:assertTrue(networkAuthenticationRequiredResponse is http:NetworkAuthenticationRequired, "Response type mismatched"); testStatusCodeResponse(networkAuthenticationRequiredResponse, 511, "Network authentication required", "Network authentication required error"); - - http:BadRequest badRequestWithUnknownStatusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 999); - test:assertTrue(badRequestWithUnknownStatusCodeResponse is http:BadRequest, "Response type mismatched"); - testStatusCodeResponse(badRequestWithUnknownStatusCodeResponse, 400, "Unknown status code", "Bad request with unknown status code"); } @test:Config {} @@ -498,6 +494,34 @@ function testSCResBindingWithCommonType() returns error? { statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 504); test:assertTrue(statusCodeResponse is http:GatewayTimeout, "Response type mismatched"); testStatusCodeResponse(statusCodeResponse, 504, "Gateway timeout", "Gateway timeout error"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 600); + test:assertTrue(statusCodeResponse is http:DefaultStatusCodeResponse, "Response type mismatched"); + testStatusCodeResponse(statusCodeResponse, 600, "Default status code", "Default status code response with code: 600"); +} + +@test:Config {} +function testSCResBindingWithDefaultType() returns error? { + http:DefaultStatusCodeResponse statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 103); + testStatusCodeResponse(statusCodeResponse, 103, "Early hints", "Early hints response"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 201); + testStatusCodeResponse(statusCodeResponse, 201, "Created", "Created response"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 204); + testStatusCodeResponse(statusCodeResponse, 204, "No content"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 304); + testStatusCodeResponse(statusCodeResponse, 304, "Not modified", "Not modified response"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 405); + testStatusCodeResponse(statusCodeResponse, 405, "Method not allowed", "Method not allowed error"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 504); + testStatusCodeResponse(statusCodeResponse, 504, "Gateway timeout", "Gateway timeout error"); + + statusCodeResponse = check statusCodeBindingClient1->/api/status\-code\-response(code = 600); + testStatusCodeResponse(statusCodeResponse, 600, "Default status code", "Default status code response with code: 600"); } function testStatusCodeResponse(http:StatusCodeResponse statusCodeResponse, int statusCode, string header, string? body = ()) { diff --git a/ballerina-tests/http-dispatching-tests/Ballerina.toml b/ballerina-tests/http-dispatching-tests/Ballerina.toml index 65192c1646..72d8bd01bb 100644 --- a/ballerina-tests/http-dispatching-tests/Ballerina.toml +++ b/ballerina-tests/http-dispatching-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_dispatching_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-dispatching-tests/Dependencies.toml b/ballerina-tests/http-dispatching-tests/Dependencies.toml index 82ecfd5eab..ba8f1775c4 100644 --- a/ballerina-tests/http-dispatching-tests/Dependencies.toml +++ b/ballerina-tests/http-dispatching-tests/Dependencies.toml @@ -69,7 +69,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -102,7 +102,7 @@ modules = [ [[package]] org = "ballerina" name = "http_dispatching_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "http"}, @@ -124,7 +124,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-interceptor-tests/Ballerina.toml b/ballerina-tests/http-interceptor-tests/Ballerina.toml index 5957cabd40..35a3d18626 100644 --- a/ballerina-tests/http-interceptor-tests/Ballerina.toml +++ b/ballerina-tests/http-interceptor-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_interceptor_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-interceptor-tests/Dependencies.toml b/ballerina-tests/http-interceptor-tests/Dependencies.toml index 4eb94ee92c..10a5f703b0 100644 --- a/ballerina-tests/http-interceptor-tests/Dependencies.toml +++ b/ballerina-tests/http-interceptor-tests/Dependencies.toml @@ -66,7 +66,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -99,7 +99,7 @@ modules = [ [[package]] org = "ballerina" name = "http_interceptor_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "http"}, {org = "ballerina", name = "http_test_common"}, @@ -115,7 +115,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-misc-tests/Ballerina.toml b/ballerina-tests/http-misc-tests/Ballerina.toml index f41831ecf6..207cc0e9d2 100644 --- a/ballerina-tests/http-misc-tests/Ballerina.toml +++ b/ballerina-tests/http-misc-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_misc_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-misc-tests/Dependencies.toml b/ballerina-tests/http-misc-tests/Dependencies.toml index 9418edcff7..22cd357ec5 100644 --- a/ballerina-tests/http-misc-tests/Dependencies.toml +++ b/ballerina-tests/http-misc-tests/Dependencies.toml @@ -66,7 +66,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -99,7 +99,7 @@ modules = [ [[package]] org = "ballerina" name = "http_misc_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "http"}, {org = "ballerina", name = "http_test_common"}, @@ -118,7 +118,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-misc-tests/tests/http_status_code_test.bal b/ballerina-tests/http-misc-tests/tests/http_status_code_test.bal index 3d24413af8..e722991a88 100644 --- a/ballerina-tests/http-misc-tests/tests/http_status_code_test.bal +++ b/ballerina-tests/http-misc-tests/tests/http_status_code_test.bal @@ -197,6 +197,16 @@ service /differentStatusCodes on httpStatusCodeListenerEP { } return {body: "Authorization Required"}; } + + resource function get default/[int statusCode]() returns http:DefaultStatusCodeResponse { + if statusCode == 204 { + return {status: new (204)}; + } + return { + body: "Default Response", + status: new (statusCode) + }; + } } //Test ballerina ok() function with entity body @@ -782,3 +792,50 @@ function testNetworkAuthenticationRequired() { test:assertFail(msg = "Found unexpected output type: " + response.message()); } } + +@test:Config {} +function testDefaultStatusCodeResponse() { + http:Response|error response = httpStatusCodeClient->get("/differentStatusCodes/default/204"); + if response is http:Response { + test:assertEquals(response.statusCode, 204, msg = "Found unexpected output"); + test:assertEquals(response.reasonPhrase, "No Content", msg = "Found unexpected output"); + } else { + test:assertFail(msg = "Found unexpected output type: " + response.message()); + } + + response = httpStatusCodeClient->get("/differentStatusCodes/default/201"); + if response is http:Response { + test:assertEquals(response.statusCode, 201, msg = "Found unexpected output"); + test:assertEquals(response.reasonPhrase, "Created", msg = "Found unexpected output"); + common:assertTextPayload(response.getTextPayload(), "Default Response"); + } else { + test:assertFail(msg = "Found unexpected output type: " + response.message()); + } + + response = httpStatusCodeClient->get("/differentStatusCodes/default/404"); + if response is http:Response { + test:assertEquals(response.statusCode, 404, msg = "Found unexpected output"); + test:assertEquals(response.reasonPhrase, "Not Found", msg = "Found unexpected output"); + common:assertTextPayload(response.getTextPayload(), "Default Response"); + } else { + test:assertFail(msg = "Found unexpected output type: " + response.message()); + } + + response = httpStatusCodeClient->get("/differentStatusCodes/default/500"); + if response is http:Response { + test:assertEquals(response.statusCode, 500, msg = "Found unexpected output"); + test:assertEquals(response.reasonPhrase, "Internal Server Error", msg = "Found unexpected output"); + common:assertTextPayload(response.getTextPayload(), "Default Response"); + } else { + test:assertFail(msg = "Found unexpected output type: " + response.message()); + } + + response = httpStatusCodeClient->get("/differentStatusCodes/default/600"); + if response is http:Response { + test:assertEquals(response.statusCode, 600, msg = "Found unexpected output"); + test:assertEquals(response.reasonPhrase, "Unknown Status (600)", msg = "Found unexpected output"); + common:assertTextPayload(response.getTextPayload(), "Default Response"); + } else { + test:assertFail(msg = "Found unexpected output type: " + response.message()); + } +} diff --git a/ballerina-tests/http-resiliency-tests/Ballerina.toml b/ballerina-tests/http-resiliency-tests/Ballerina.toml index 611c3212a0..e9e2e9fd5d 100644 --- a/ballerina-tests/http-resiliency-tests/Ballerina.toml +++ b/ballerina-tests/http-resiliency-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_resiliency_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-resiliency-tests/Dependencies.toml b/ballerina-tests/http-resiliency-tests/Dependencies.toml index 1902f5ad43..e0f7cb7f48 100644 --- a/ballerina-tests/http-resiliency-tests/Dependencies.toml +++ b/ballerina-tests/http-resiliency-tests/Dependencies.toml @@ -66,7 +66,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -99,7 +99,7 @@ modules = [ [[package]] org = "ballerina" name = "http_resiliency_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "http"}, {org = "ballerina", name = "http_test_common"}, @@ -116,7 +116,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-security-tests/Ballerina.toml b/ballerina-tests/http-security-tests/Ballerina.toml index 4d53113c57..f465ccf431 100644 --- a/ballerina-tests/http-security-tests/Ballerina.toml +++ b/ballerina-tests/http-security-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_security_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-security-tests/Dependencies.toml b/ballerina-tests/http-security-tests/Dependencies.toml index d030995890..7b439cd343 100644 --- a/ballerina-tests/http-security-tests/Dependencies.toml +++ b/ballerina-tests/http-security-tests/Dependencies.toml @@ -69,7 +69,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -102,7 +102,7 @@ modules = [ [[package]] org = "ballerina" name = "http_security_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "http"}, @@ -120,7 +120,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-service-tests/Ballerina.toml b/ballerina-tests/http-service-tests/Ballerina.toml index 2409b43e15..0d65071be0 100644 --- a/ballerina-tests/http-service-tests/Ballerina.toml +++ b/ballerina-tests/http-service-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http_service_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http-service-tests/Dependencies.toml b/ballerina-tests/http-service-tests/Dependencies.toml index 9c983fffac..201aa6b1c7 100644 --- a/ballerina-tests/http-service-tests/Dependencies.toml +++ b/ballerina-tests/http-service-tests/Dependencies.toml @@ -69,7 +69,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -102,7 +102,7 @@ modules = [ [[package]] org = "ballerina" name = "http_service_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "file"}, {org = "ballerina", name = "http"}, @@ -121,7 +121,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina-tests/http-test-common/Ballerina.toml b/ballerina-tests/http-test-common/Ballerina.toml index 58f1cb03ab..ecdb05e263 100644 --- a/ballerina-tests/http-test-common/Ballerina.toml +++ b/ballerina-tests/http-test-common/Ballerina.toml @@ -1,4 +1,4 @@ [package] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" diff --git a/ballerina-tests/http-test-common/Dependencies.toml b/ballerina-tests/http-test-common/Dependencies.toml index fcb61bbb2e..dff80b5dc7 100644 --- a/ballerina-tests/http-test-common/Dependencies.toml +++ b/ballerina-tests/http-test-common/Dependencies.toml @@ -10,7 +10,7 @@ distribution-version = "2201.9.0" [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "lang.string"}, {org = "ballerina", name = "mime"}, diff --git a/ballerina-tests/http2-tests/Ballerina.toml b/ballerina-tests/http2-tests/Ballerina.toml index 229840e571..79f8a8b379 100644 --- a/ballerina-tests/http2-tests/Ballerina.toml +++ b/ballerina-tests/http2-tests/Ballerina.toml @@ -1,17 +1,17 @@ [package] org = "ballerina" name = "http2_tests" -version = "2.11.1" +version = "2.11.2" [[dependency]] org = "ballerina" name = "http_test_common" repository = "local" -version = "2.11.1" +version = "2.11.2" [platform.java17] graalvmCompatible = true [[platform.java17.dependency]] scope = "testOnly" -path = "../../test-utils/build/libs/http-test-utils-2.11.1.jar" +path = "../../test-utils/build/libs/http-test-utils-2.11.2-SNAPSHOT.jar" diff --git a/ballerina-tests/http2-tests/Dependencies.toml b/ballerina-tests/http2-tests/Dependencies.toml index eb76a56238..19fad42422 100644 --- a/ballerina-tests/http2-tests/Dependencies.toml +++ b/ballerina-tests/http2-tests/Dependencies.toml @@ -69,7 +69,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "auth"}, @@ -102,7 +102,7 @@ modules = [ [[package]] org = "ballerina" name = "http2_tests" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "file"}, {org = "ballerina", name = "http"}, @@ -121,7 +121,7 @@ modules = [ [[package]] org = "ballerina" name = "http_test_common" -version = "2.11.1" +version = "2.11.2" scope = "testOnly" dependencies = [ {org = "ballerina", name = "lang.string"}, diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 8d74670a18..c31f962c74 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" authors = ["Ballerina"] keywords = ["http", "network", "service", "listener", "client"] repository = "https://github.com/ballerina-platform/module-ballerina-http" @@ -16,8 +16,8 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "http-native" -version = "2.11.1" -path = "../native/build/libs/http-native-2.11.1.jar" +version = "2.11.2" +path = "../native/build/libs/http-native-2.11.2-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index b05228e941..3cbad54670 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "http-compiler-plugin" class = "io.ballerina.stdlib.http.compiler.HttpCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/http-compiler-plugin-2.11.1.jar" +path = "../compiler-plugin/build/libs/http-compiler-plugin-2.11.2-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 27ce1b5477..4def31d650 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -50,7 +50,7 @@ modules = [ [[package]] org = "ballerina" name = "crypto" -version = "2.7.0" +version = "2.7.1" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "time"} @@ -76,7 +76,7 @@ modules = [ [[package]] org = "ballerina" name = "http" -version = "2.11.1" +version = "2.11.2" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, diff --git a/ballerina/http_client_endpoint.bal b/ballerina/http_client_endpoint.bal index 00ca488b48..7d5d5954b7 100644 --- a/ballerina/http_client_endpoint.bal +++ b/ballerina/http_client_endpoint.bal @@ -677,17 +677,45 @@ isolated function createResponseError(int statusCode, string reasonPhrase, map headers, anydata body = ()) returns ClientError { - if generalError { - return error StatusCodeResponseBindingError(reasonPhrase, statusCode = statusCode, headers = headers, body = body); - } +isolated function createStatusCodeResponseBindingError(int statusCode, string reasonPhrase, map headers, + anydata body = ()) returns ClientError { if 100 <= statusCode && statusCode <= 399 { - return error StatusCodeBindingSuccessError(reasonPhrase, statusCode = statusCode, headers = headers, body = body); + return error StatusCodeResponseBindingError(reasonPhrase, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = false); } else if 400 <= statusCode && statusCode <= 499 { - return error StatusCodeBindingClientRequestError(reasonPhrase, statusCode = statusCode, headers = headers, body = body); + return error StatusCodeBindingClientRequestError(reasonPhrase, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = false); } else { - return error StatusCodeBindingRemoteServerError(reasonPhrase, statusCode = statusCode, headers = headers, body = body); + return error StatusCodeBindingRemoteServerError(reasonPhrase, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = false); + } +} + +enum DataBindingErrorType { + HEADER, MEDIA_TYPE, PAYLOAD, GENERIC +} + +isolated function createStatusCodeResponseDataBindingError(DataBindingErrorType errorType, boolean fromDefaultStatusCodeMapping, + int statusCode, string reasonPhrase, map headers, anydata body = (), error? cause = ()) returns ClientError { + match (errorType) { + HEADER => { + if cause is HeaderValidationClientError { + return error HeaderValidationStatusCodeClientError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } + return error HeaderBindingStatusCodeClientError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } + MEDIA_TYPE => { + if cause is MediaTypeValidationClientError { + return error MediaTypeValidationStatusCodeClientError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } + return error MediaTypeBindingStatusCodeClientError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } + PAYLOAD => { + if cause is PayloadValidationClientError { + return error PayloadValidationStatusCodeClientError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } + return error PayloadBindingStatusCodeClientError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } + _ => { + return error StatusCodeResponseBindingError(reasonPhrase, cause, statusCode = statusCode, headers = headers, body = body, fromDefaultStatusCodeMapping = fromDefaultStatusCodeMapping); + } } } diff --git a/ballerina/http_errors.bal b/ballerina/http_errors.bal index 435508e692..9377c1e175 100644 --- a/ballerina/http_errors.bal +++ b/ballerina/http_errors.bal @@ -27,6 +27,14 @@ public type Detail record { anydata body; }; +# Represents the details of an HTTP status code binding client error. +# +# + fromDefaultStatusCodeMapping - Indicates whether the error orginates from default status code response mapping +public type StatusCodeBindingErrorDetail record { + *Detail; + boolean fromDefaultStatusCodeMapping; +}; + # Represents the details of the `LoadBalanceActionError`. # # + httpActionErr - Array of errors occurred at each endpoint @@ -308,10 +316,25 @@ public type ResourceDispatchingServerError httpscerr:InternalServerErrorError & type InternalResourceDispatchingServerError ResourceDispatchingServerError & InternalError; # Represents the client status code binding error -public type StatusCodeResponseBindingError distinct ClientError & error; +public type StatusCodeResponseBindingError distinct ClientError & error; + +# Represents the status code binding error that occurred due to 4XX status code response binding +public type StatusCodeBindingClientRequestError distinct StatusCodeResponseBindingError & ClientRequestError; + +# Represents the status code binding error that occurred due to 5XX status code response binding +public type StatusCodeBindingRemoteServerError distinct StatusCodeResponseBindingError & RemoteServerError; + +type MediaTypeBindingStatusCodeClientError distinct MediaTypeBindingClientError & StatusCodeResponseBindingError; + +type MediaTypeValidationStatusCodeClientError distinct MediaTypeValidationClientError & StatusCodeResponseBindingError; + +type PayloadBindingStatusCodeClientError distinct PayloadBindingClientError & StatusCodeResponseBindingError; + +type PayloadValidationStatusCodeClientError distinct PayloadValidationClientError & StatusCodeResponseBindingError; -type StatusCodeBindingClientRequestError distinct StatusCodeResponseBindingError & ClientRequestError; +type HeaderBindingStatusCodeClientError distinct HeaderBindingClientError & StatusCodeResponseBindingError; -type StatusCodeBindingRemoteServerError distinct StatusCodeResponseBindingError & RemoteServerError; +type HeaderValidationStatusCodeClientError distinct HeaderValidationClientError & StatusCodeResponseBindingError; -type StatusCodeBindingSuccessError distinct StatusCodeResponseBindingError; +# Represents the client status code response data binding error +public type StatusCodeResponseDataBindingError MediaTypeBindingStatusCodeClientError|PayloadBindingStatusCodeClientError|HeaderBindingStatusCodeClientError; diff --git a/ballerina/http_response.bal b/ballerina/http_response.bal index 751f56a5b9..fbc0d64fd0 100644 --- a/ballerina/http_response.bal +++ b/ballerina/http_response.bal @@ -541,12 +541,12 @@ public class Response { } isolated function buildStatusCodeResponse(typedesc? payloadType, typedesc statusCodeResType, - boolean requireValidation, Status status, map headers, string? mediaType) - returns StatusCodeResponse|ClientError { + boolean requireValidation, Status status, map headers, string? mediaType, + boolean fromDefaultStatusCodeMapping) returns StatusCodeResponse|ClientError { if payloadType !is () { anydata|ClientError payload = self.performDataBinding(payloadType, requireValidation); if payload is ClientError { - return payload; + return self.getStatusCodeResponseDataBindingError(payload.message(), fromDefaultStatusCodeMapping, PAYLOAD, payload); } return externBuildStatusCodeResponse(statusCodeResType, status, headers, payload, mediaType); } else { @@ -562,18 +562,34 @@ public class Response { return payload; } - isolated function getStatusCodeResponseBindingError(string reasonPhrase, boolean generalError) returns ClientError { + isolated function getStatusCodeResponseBindingError(string reasonPhrase) returns ClientError { + map headers = getHeaders(self); + anydata|error payload = getPayload(self); + int statusCode = self.statusCode; + if payload is error { + if payload is NoContentError { + return createStatusCodeResponseBindingError(statusCode, reasonPhrase, headers); + } + return error PayloadBindingClientError("http:StatusCodeBindingError creation failed: " + statusCode.toString() + + " response payload extraction failed", payload); + } else { + return createStatusCodeResponseBindingError(statusCode, reasonPhrase, headers, payload); + } + } + + isolated function getStatusCodeResponseDataBindingError(string reasonPhrase, boolean fromDefaultStatusCodeMapping, + DataBindingErrorType errorType, error? cause) returns ClientError { map headers = getHeaders(self); anydata|error payload = getPayload(self); int statusCode = self.statusCode; if payload is error { if payload is NoContentError { - return createStatusCodeResponseBindingError(generalError, statusCode, reasonPhrase, headers); + return createStatusCodeResponseDataBindingError(errorType, fromDefaultStatusCodeMapping, statusCode, reasonPhrase, headers, cause = cause); } return error PayloadBindingClientError("http:StatusCodeBindingError creation failed: " + statusCode.toString() + " response payload extraction failed", payload); } else { - return createStatusCodeResponseBindingError(generalError, statusCode, reasonPhrase, headers, payload); + return createStatusCodeResponseDataBindingError(errorType, fromDefaultStatusCodeMapping, statusCode, reasonPhrase, headers, payload, cause); } } } diff --git a/ballerina/http_status_code_types.bal b/ballerina/http_status_code_types.bal index 17b17e4bd0..c76250ced3 100644 --- a/ballerina/http_status_code_types.bal +++ b/ballerina/http_status_code_types.bal @@ -25,7 +25,7 @@ public type StatusCodeResponse Continue|SwitchingProtocols|Processing|EarlyHints Locked|FailedDependency|TooEarly|PreconditionRequired|UnavailableDueToLegalReasons|UpgradeRequired|TooManyRequests| RequestHeaderFieldsTooLarge|InternalServerError|NotImplemented|BadGateway|ServiceUnavailable|GatewayTimeout| HttpVersionNotSupported|VariantAlsoNegotiates|InsufficientStorage|LoopDetected|NotExtended| - NetworkAuthenticationRequired; + NetworkAuthenticationRequired|DefaultStatusCodeResponse; # Defines the possible success status code response record types. type SuccessStatusCodeResponse Ok|Created|Accepted|NonAuthoritativeInformation|NoContent|ResetContent| @@ -50,6 +50,16 @@ public type CommonResponse record {| |}; // Status code class declarations + +# The default status code class. +public readonly distinct class DefaultStatus { + *Status; + + public isolated function init(int code) { + self.code = code; + } +} + # Represents the status code of `STATUS_CONTINUE`. # # + code - The response status code @@ -602,6 +612,15 @@ final StatusNotExtended STATUS_NOT_EXTENDED_OBJ = new; final StatusNetworkAuthenticationRequired STATUS_NETWORK_AUTHENTICATION_REQUIRED_OBJ = new; // Status code record types + +# The default status code response record. +# +# + status - The response status code object +public type DefaultStatusCodeResponse record {| + *CommonResponse; + readonly DefaultStatus status; +|}; + # The status code response record of `Continue`. # # + status - The response status code obj diff --git a/changelog.md b/changelog.md index 453cfbbb98..73dd811a5e 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,12 @@ This file contains all the notable changes done to the Ballerina HTTP package th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- [Introduce default status code response record](https://github.com/ballerina-platform/ballerina-library/issues/6491) + ## [2.11.1] - 2024-05-29 ### Fixed diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpErrorType.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpErrorType.java index 658fd61222..6329698474 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpErrorType.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpErrorType.java @@ -88,7 +88,6 @@ public enum HttpErrorType { HEADER_NOT_FOUND_CLIENT_ERROR("HeaderNotFoundClientError"), HEADER_BINDING_CLIENT_ERROR("HeaderBindingClientError"), HEADER_VALIDATION_CLIENT_ERROR("HeaderValidationClientError"), - MEDIA_TYPE_BINDING_CLIENT_ERROR("MediaTypeBindingClientError"), MEDIA_TYPE_VALIDATION_CLIENT_ERROR("MediaTypeValidationClientError"); private final String errorName; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/ValueCreatorUtils.java b/native/src/main/java/io/ballerina/stdlib/http/api/ValueCreatorUtils.java index c706322f66..26a2f91059 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/ValueCreatorUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/ValueCreatorUtils.java @@ -105,6 +105,10 @@ public static Object createStatusCodeObject(String statusCodeObjName) { return createObjectValue(ModuleUtils.getHttpPackage(), statusCodeObjName); } + public static Object createDefaultStatusCodeObject(long statusCode) { + return createObjectValue(ModuleUtils.getHttpPackage(), "DefaultStatus", statusCode); + } + /** * Method that creates a runtime object value using the given package id and object type name. * diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternResponseProcessor.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternResponseProcessor.java index 0b72c05fa6..a1182d98d5 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternResponseProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternResponseProcessor.java @@ -46,7 +46,6 @@ import io.ballerina.stdlib.constraint.Constraints; import io.ballerina.stdlib.http.api.HttpErrorType; import io.ballerina.stdlib.http.api.HttpUtil; -import io.ballerina.stdlib.http.api.ValueCreatorUtils; import io.netty.handler.codec.http.HttpHeaders; import java.util.HashMap; @@ -64,13 +63,13 @@ import static io.ballerina.stdlib.http.api.HttpConstants.STATUS_CODE_RESPONSE_STATUS_CODE_FIELD; import static io.ballerina.stdlib.http.api.HttpConstants.STATUS_CODE_RESPONSE_STATUS_FIELD; import static io.ballerina.stdlib.http.api.HttpErrorType.CLIENT_ERROR; -import static io.ballerina.stdlib.http.api.HttpErrorType.HEADER_BINDING_CLIENT_ERROR; import static io.ballerina.stdlib.http.api.HttpErrorType.HEADER_NOT_FOUND_CLIENT_ERROR; import static io.ballerina.stdlib.http.api.HttpErrorType.HEADER_VALIDATION_CLIENT_ERROR; -import static io.ballerina.stdlib.http.api.HttpErrorType.MEDIA_TYPE_BINDING_CLIENT_ERROR; import static io.ballerina.stdlib.http.api.HttpErrorType.MEDIA_TYPE_VALIDATION_CLIENT_ERROR; -import static io.ballerina.stdlib.http.api.HttpErrorType.PAYLOAD_BINDING_CLIENT_ERROR; +import static io.ballerina.stdlib.http.api.HttpErrorType.STATUS_CODE_RESPONSE_BINDING_ERROR; import static io.ballerina.stdlib.http.api.HttpUtil.createHttpError; +import static io.ballerina.stdlib.http.api.ValueCreatorUtils.createDefaultStatusCodeObject; +import static io.ballerina.stdlib.http.api.ValueCreatorUtils.createStatusCodeObject; /** * Extern response processor to process the response and generate the response with the given target type. @@ -84,16 +83,23 @@ public final class ExternResponseProcessor { private static final String HEADER_BINDING_FAILED = "header binding failed"; private static final String UNSUPPORTED_HEADERS_TYPE = "unsupported headers type: %s"; private static final String UNSUPPORTED_STATUS_CODE = "unsupported status code: %d"; - private static final String INCOMPATIBLE_TYPE_FOUND_FOR_RESPONSE = "incompatible %s found for response with %d"; + private static final String INCOMPATIBLE_TYPE_FOUND_FOR_RESPONSE = "incompatible type: %s found for the response" + + " with status code: %d"; private static final String MEDIA_TYPE_BINDING_FAILED = "media-type binding failed"; private static final String APPLICATION_RES_ERROR_CREATION_FAILED = "http:ApplicationResponseError creation failed"; private static final String STATUS_CODE_RES_CREATION_FAILED = "http:StatusCodeResponse creation failed"; private static final String GET_STATUS_CODE_RESPONSE_BINDING_ERROR = "getStatusCodeResponseBindingError"; + private static final String GET_STATUS_CODE_RESPONSE_DATA_BINDING_ERROR = "getStatusCodeResponseDataBindingError"; private static final String BUILD_STATUS_CODE_RESPONSE = "buildStatusCodeResponse"; private static final Map STATUS_CODE_OBJS = new HashMap<>(); + private static final String DEFAULT_STATUS = "DefaultStatus"; + private static final String DEFAULT = "default"; + private static final String HEADER = "HEADER"; + private static final String MEDIA_TYPE = "MEDIA_TYPE"; + private static final String GENERIC = "GENERIC"; static { STATUS_CODE_OBJS.put("100", "StatusContinue"); @@ -187,16 +193,28 @@ public static Object buildStatusCodeResponse(BTypedesc statusCodeResponseTypeDes private static Object getResponseWithType(BObject response, Type targetType, boolean requireValidation, Environment env) { long responseStatusCode = getStatusCode(response); - Optional statusCodeResponseType = getStatusCodeResponseType(targetType, + + // Find the most specific status code record type + Optional statusCodeResponseType = getSpecificStatusCodeResponseType(targetType, Long.toString(responseStatusCode)); + if (statusCodeResponseType.isEmpty()) { + // Find the default status code record type + statusCodeResponseType = getDefaultStatusCodeResponseType(targetType); + } + if (statusCodeResponseType.isPresent() && TypeUtils.getImpliedType(statusCodeResponseType.get()) instanceof RecordType statusCodeRecordType) { - return generateStatusCodeResponseType(response, requireValidation, env, statusCodeRecordType, - responseStatusCode); + try { + return generateStatusCodeResponseType(response, requireValidation, env, statusCodeRecordType, + responseStatusCode); + } catch (StatusCodeBindingException exp) { + return getStatusCodeResponseDataBindingError(env, response, exp.getMessage(), exp.getBError(), + isDefaultStatusCodeResponseType(statusCodeRecordType), exp.getErrorType()); + } } String reasonPhrase = String.format(INCOMPATIBLE_TYPE_FOUND_FOR_RESPONSE, targetType.getName(), responseStatusCode); - return getStatusCodeResponseBindingError(env, response, reasonPhrase, false); + return getStatusCodeResponseBindingError(env, response, reasonPhrase); } private static long getStatusCode(BObject response) { @@ -204,16 +222,22 @@ private static long getStatusCode(BObject response) { } private static Object generateStatusCodeResponseType(BObject response, boolean requireValidation, Environment env, - RecordType statusCodeRecordType, long responseStatusCode) { + RecordType statusCodeRecordType, long responseStatusCode) + throws StatusCodeBindingException { String statusCodeObjName = STATUS_CODE_OBJS.get(Long.toString(responseStatusCode)); if (Objects.isNull(statusCodeObjName)) { - return getStatusCodeResponseBindingError(env, response, String.format(UNSUPPORTED_STATUS_CODE, - responseStatusCode), true); + if (isDefaultStatusCodeResponseType(statusCodeRecordType)) { + statusCodeObjName = DEFAULT_STATUS; + } else { + throw new StatusCodeBindingException(GENERIC, String.format(UNSUPPORTED_STATUS_CODE, + responseStatusCode)); + } } - Object status = ValueCreatorUtils.createStatusCodeObject(statusCodeObjName); + Object status = statusCodeObjName.equals(DEFAULT_STATUS) ? createDefaultStatusCodeObject(responseStatusCode) : + createStatusCodeObject(statusCodeObjName); - Object headers = getHeaders(env, response, requireValidation, statusCodeRecordType); + Object headers = getHeaders(response, requireValidation, statusCodeRecordType); if (headers instanceof BError) { return headers; } @@ -221,9 +245,6 @@ private static Object generateStatusCodeResponseType(BObject response, boolean r Object mediaType = null; if (statusCodeRecordType.getFields().containsKey(STATUS_CODE_RESPONSE_MEDIA_TYPE_FIELD)) { mediaType = getMediaType(response, requireValidation, statusCodeRecordType); - if (mediaType instanceof BError) { - return mediaType; - } } Type payloadType = null; @@ -238,7 +259,7 @@ private static Object generateStatusCodeResponseType(BObject response, boolean r private static Object[] getParamFeedForStatusCodeBinding(boolean requireValidation, RecordType statusCodeType, Type payloadType, Object status, Object headers, Object mediaType) { - Object[] paramFeed = new Object[12]; + Object[] paramFeed = new Object[14]; paramFeed[0] = Objects.isNull(payloadType) ? null : ValueCreator.createTypedescValue(payloadType); paramFeed[1] = true; paramFeed[2] = ValueCreator.createTypedescValue(statusCodeType); @@ -251,29 +272,34 @@ private static Object[] getParamFeedForStatusCodeBinding(boolean requireValidati paramFeed[9] = true; paramFeed[10] = mediaType; paramFeed[11] = true; + paramFeed[12] = isDefaultStatusCodeResponseType(statusCodeType); + paramFeed[13] = true; return paramFeed; } - private static Object getHeaders(Environment env, BObject response, boolean requireValidation, - RecordType statusCodeRecordType) { + private static Object getHeaders(BObject response, boolean requireValidation, RecordType statusCodeRecordType) { Type headersType = statusCodeRecordType.getFields().get(STATUS_CODE_RESPONSE_HEADERS_FIELD).getFieldType(); - return getHeadersMap(env, response, headersType, requireValidation); + return getHeadersMap(response, headersType, requireValidation); } - private static Object getMediaType(BObject response, boolean requireValidation, RecordType statusCodeRecordType) { + private static Object getMediaType(BObject response, boolean requireValidation, RecordType statusCodeRecordType) + throws StatusCodeBindingException { Type mediaTypeType = statusCodeRecordType.getFields().get(STATUS_CODE_RESPONSE_MEDIA_TYPE_FIELD).getFieldType(); return getMediaType(response, mediaTypeType, requireValidation); } - private static Object getMediaType(BObject response, Type mediaTypeType, boolean requireValidation) { + private static Object getMediaType(BObject response, Type mediaTypeType, boolean requireValidation) + throws StatusCodeBindingException { String contentType = getContentType(response); + if (Objects.isNull(contentType)) { + return null; + } try { - Object convertedValue = ValueUtils.convert(Objects.nonNull(contentType) ? - StringUtils.fromString(contentType) : null, mediaTypeType); + Object convertedValue = ValueUtils.convert(StringUtils.fromString(contentType), mediaTypeType); return validateConstraints(requireValidation, convertedValue, mediaTypeType, - MEDIA_TYPE_VALIDATION_CLIENT_ERROR, MEDIA_TYPE_BINDING_FAILED); + MEDIA_TYPE_VALIDATION_CLIENT_ERROR, MEDIA_TYPE_BINDING_FAILED, MEDIA_TYPE); } catch (BError conversionError) { - return createHttpError(MEDIA_TYPE_BINDING_FAILED, MEDIA_TYPE_BINDING_CLIENT_ERROR, conversionError); + throw new StatusCodeBindingException(MEDIA_TYPE, MEDIA_TYPE_BINDING_FAILED, conversionError); } } @@ -282,8 +308,8 @@ private static String getContentType(BObject response) { return httpHeaders.get("Content-Type"); } - private static Object getHeadersMap(Environment env, BObject response, Type headersType, - boolean requireValidation) { + private static Object getHeadersMap(BObject response, Type headersType, boolean requireValidation) + throws StatusCodeBindingException { HttpHeaders httpHeaders = (HttpHeaders) response.getNativeData(HTTP_HEADERS); Type headersImpliedType = TypeUtils.getImpliedType(headersType); if (headersImpliedType.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { @@ -297,41 +323,46 @@ private static Object getHeadersMap(Environment env, BObject response, Type head } else if (headersImpliedType.getTag() == TypeTags.RECORD_TYPE_TAG) { headerMap = createHeaderRecord(httpHeaders, (RecordType) headersImpliedType); } else { - return getStatusCodeResponseBindingError(env, response, String.format(UNSUPPORTED_HEADERS_TYPE, - headersImpliedType.getName()), true); + throw new StatusCodeBindingException(HEADER, String.format(UNSUPPORTED_HEADERS_TYPE, + headersImpliedType.getName())); } - if (headerMap instanceof BError) { - return headerMap; + if (headerMap instanceof BError error) { + throw new StatusCodeBindingException(HEADER, HEADER_BINDING_FAILED, error); } try { Object convertedHeaderMap = ValueUtils.convert(headerMap, headersType); return validateConstraints(requireValidation, convertedHeaderMap, headersType, - HEADER_VALIDATION_CLIENT_ERROR, HEADER_BINDING_FAILED); + HEADER_VALIDATION_CLIENT_ERROR, HEADER_BINDING_FAILED, HEADER); } catch (BError conversionError) { - return createHttpError(HEADER_BINDING_FAILED, HEADER_BINDING_CLIENT_ERROR, conversionError); + throw new StatusCodeBindingException(HEADER, HEADER_BINDING_FAILED, conversionError); } } - private static Optional getStatusCodeResponseType(Type targetType, String statusCode) { + private static Optional getSpecificStatusCodeResponseType(Type targetType, String statusCode) { if (isStatusCodeResponseType(targetType)) { - if (getStatusCode(targetType).equals(statusCode)) { + String statusCodeFromType = getStatusCode(targetType); + if (statusCodeFromType.equals(statusCode)) { return Optional.of(targetType); } } else if (targetType instanceof UnionType unionType) { return unionType.getMemberTypes().stream() - .map(member -> getStatusCodeResponseType(member, statusCode)) + .map(member -> getSpecificStatusCodeResponseType(member, statusCode)) .filter(Optional::isPresent) .flatMap(Optional::stream) .findFirst(); } else if (targetType instanceof ReferenceType && (!targetType.equals(TypeUtils.getImpliedType(targetType)))) { - return getStatusCodeResponseType(TypeUtils.getImpliedType(targetType), statusCode); + return getSpecificStatusCodeResponseType(TypeUtils.getImpliedType(targetType), statusCode); } return Optional.empty(); } + private static Optional getDefaultStatusCodeResponseType(Type targetType) { + return getSpecificStatusCodeResponseType(targetType, DEFAULT); + } + private static boolean isStatusCodeResponseType(Type targetType) { return targetType instanceof ReferenceType referenceType && TypeUtils.getImpliedType(referenceType) instanceof RecordType recordType && @@ -340,22 +371,29 @@ private static boolean isStatusCodeResponseType(Type targetType) { } private static String getStatusCode(Type targetType) { - return ((ObjectType) ((RecordType) TypeUtils.getImpliedType(targetType)).getFields(). - get(STATUS_CODE_RESPONSE_STATUS_FIELD).getFieldType()).getFields(). - get(STATUS_CODE_RESPONSE_STATUS_CODE_FIELD).getFieldType().getEmptyValue().toString(); + ObjectType statusCodeType = (ObjectType) ((RecordType) TypeUtils.getImpliedType(targetType)).getFields(). + get(STATUS_CODE_RESPONSE_STATUS_FIELD).getFieldType(); + if (statusCodeType.getName().equals(DEFAULT_STATUS)) { + return DEFAULT; + } + return statusCodeType.getFields().get(STATUS_CODE_RESPONSE_STATUS_CODE_FIELD).getFieldType(). + getEmptyValue().toString(); + } + + private static boolean isDefaultStatusCodeResponseType(RecordType statusCodeRecordType) { + return getStatusCode(statusCodeRecordType).equals(DEFAULT); } - private static Object createHeaderMap(HttpHeaders httpHeaders, Type elementType) { + private static Object createHeaderMap(HttpHeaders headers, Type elementType) throws StatusCodeBindingException { BMap headerMap = ValueCreator.createMapValue(); - Set headerNames = httpHeaders.names(); + Set headerNames = headers.names(); for (String headerName : headerNames) { - List headerValues = getHeader(httpHeaders, headerName); + List headerValues = getHeader(headers, headerName); try { Object convertedValue = convertHeaderValues(headerValues, elementType); headerMap.put(StringUtils.fromString(headerName), convertedValue); } catch (BError ex) { - return createHttpError(String.format(HEADER_BINDING_FAILED_ERROR_MSG, headerName), - HEADER_BINDING_CLIENT_ERROR, ex); + throw new StatusCodeBindingException(HEADER, HEADER_BINDING_FAILED, ex); } } return headerMap; @@ -377,16 +415,17 @@ private static Object createHeaderRecord(HttpHeaders httpHeaders, RecordType hea continue; } // Return Header Not Found Error - return createHttpError(String.format(NO_HEADER_VALUE_ERROR_MSG, headerName), + BError cause = createHttpError(String.format(NO_HEADER_VALUE_ERROR_MSG, headerName), HEADER_NOT_FOUND_CLIENT_ERROR); + throw new StatusCodeBindingException(HEADER, HEADER_BINDING_FAILED, cause); } try { Object convertedValue = convertHeaderValues(headerValues, headerFieldType); headerMap.put(StringUtils.fromString(headerName), convertedValue); } catch (BError ex) { - return createHttpError(String.format(HEADER_BINDING_FAILED_ERROR_MSG, headerName), - HEADER_BINDING_CLIENT_ERROR, ex); + throw new StatusCodeBindingException(HEADER, String.format(HEADER_BINDING_FAILED_ERROR_MSG, + headerName), ex); } } return headerMap; @@ -464,46 +503,60 @@ private static List getHeader(HttpHeaders httpHeaders, String headerName } private static Object validateConstraints(boolean requireValidation, Object convertedValue, Type type, - HttpErrorType errorType, String errorMsg) { + HttpErrorType errorType, String errorMsg, String paramType) + throws StatusCodeBindingException { if (requireValidation) { Object result = Constraints.validate(convertedValue, ValueCreator.createTypedescValue(type)); if (result instanceof BError bError) { String message = errorMsg + ": " + HttpUtil.getPrintableErrorMsg(bError); - return createHttpError(message, errorType); + BError cause = createHttpError(message, errorType, bError); + throw new StatusCodeBindingException(paramType, message, cause); } } return convertedValue; } - private static Object getStatusCodeResponseBindingError(Environment env, BObject response, String reasonPhrase, - boolean generalError) { + private static Object getStatusCodeResponseBindingError(Environment env, BObject response, String reasonPhrase) { Future balFuture = env.markAsync(); - Callback returnCallback = new Callback() { - @Override - public void notifySuccess(Object result) { - balFuture.complete(result); - } + Callback returnCallback = getReturnCallback(balFuture, APPLICATION_RES_ERROR_CREATION_FAILED); + Object[] paramFeed = new Object[2]; + paramFeed[0] = StringUtils.fromString(reasonPhrase); + paramFeed[1] = true; + env.getRuntime().invokeMethodAsyncSequentially(response, GET_STATUS_CODE_RESPONSE_BINDING_ERROR, null, + ModuleUtils.getNotifySuccessMetaData(), returnCallback, null, PredefinedTypes.TYPE_ERROR, paramFeed); + return null; + } - @Override - public void notifyFailure(BError bError) { - BError error = createHttpError(APPLICATION_RES_ERROR_CREATION_FAILED, PAYLOAD_BINDING_CLIENT_ERROR, - bError); - balFuture.complete(error); - } - }; - Object[] paramFeed = new Object[4]; + private static Object getStatusCodeResponseDataBindingError(Environment env, BObject response, String reasonPhrase, + BError cause, boolean isDefaultStatusCodeResponse, + String errorType) { + Future balFuture = env.markAsync(); + Callback returnCallback = getReturnCallback(balFuture, APPLICATION_RES_ERROR_CREATION_FAILED); + Object[] paramFeed = new Object[8]; paramFeed[0] = StringUtils.fromString(reasonPhrase); paramFeed[1] = true; - paramFeed[2] = generalError; + paramFeed[2] = isDefaultStatusCodeResponse; paramFeed[3] = true; - env.getRuntime().invokeMethodAsyncSequentially(response, GET_STATUS_CODE_RESPONSE_BINDING_ERROR, null, + paramFeed[4] = StringUtils.fromString(errorType); + paramFeed[5] = true; + paramFeed[6] = cause; + paramFeed[7] = true; + env.getRuntime().invokeMethodAsyncSequentially(response, GET_STATUS_CODE_RESPONSE_DATA_BINDING_ERROR, null, ModuleUtils.getNotifySuccessMetaData(), returnCallback, null, PredefinedTypes.TYPE_ERROR, paramFeed); return null; } private static Object getStatusCodeResponse(Environment env, BObject response, Object[] paramFeed) { Future balFuture = env.markAsync(); - Callback returnCallback = new Callback() { + Callback returnCallback = getReturnCallback(balFuture, STATUS_CODE_RES_CREATION_FAILED); + env.getRuntime().invokeMethodAsyncSequentially(response, BUILD_STATUS_CODE_RESPONSE, null, + ModuleUtils.getNotifySuccessMetaData(), returnCallback, null, PredefinedTypes.TYPE_ANY, + paramFeed); + return null; + } + + private static Callback getReturnCallback(Future balFuture, String errorMessage) { + return new Callback() { @Override public void notifySuccess(Object result) { balFuture.complete(result); @@ -511,13 +564,9 @@ public void notifySuccess(Object result) { @Override public void notifyFailure(BError bError) { - BError error = createHttpError(STATUS_CODE_RES_CREATION_FAILED, CLIENT_ERROR, bError); + BError error = createHttpError(errorMessage, STATUS_CODE_RESPONSE_BINDING_ERROR, bError); balFuture.complete(error); } }; - env.getRuntime().invokeMethodAsyncSequentially(response, BUILD_STATUS_CODE_RESPONSE, null, - ModuleUtils.getNotifySuccessMetaData(), returnCallback, null, PredefinedTypes.TYPE_ANY, - paramFeed); - return null; } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/StatusCodeBindingException.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/StatusCodeBindingException.java new file mode 100644 index 0000000000..e3b3f4a418 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/StatusCodeBindingException.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.stdlib.http.api.nativeimpl; + +import io.ballerina.runtime.api.values.BError; + +/** + * Status code binding exception to indicate that the status code data-binding has failed. + * + * @since 2.11.1 + */ +public class StatusCodeBindingException extends RuntimeException { + + private final String errorType; + private final BError cause; + + public StatusCodeBindingException(String errorType, String message) { + super(message); + this.errorType = errorType; + this.cause = null; + } + + public StatusCodeBindingException(String errorType, String message, BError cause) { + super(message); + this.errorType = errorType; + this.cause = cause; + } + + public String getErrorType() { + return errorType; + } + + public BError getBError() { + return cause; + } +}