diff --git a/.gitignore b/.gitignore index 7cb31afdd1..80cbfc54d3 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ load-tests/**/Dependencies.toml # Ballerina related ignores Ballerina.lock velocity.log* + +compiler-plugin-tests/**/target diff --git a/ballerina-tests/http-client-tests/Dependencies.toml b/ballerina-tests/http-client-tests/Dependencies.toml index 7a6070d524..cf0e2a4c27 100644 --- a/ballerina-tests/http-client-tests/Dependencies.toml +++ b/ballerina-tests/http-client-tests/Dependencies.toml @@ -53,6 +53,19 @@ dependencies = [ {org = "ballerina", name = "time"} ] +[[package]] +org = "ballerina" +name = "data.jsondata" +version = "0.3.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] +modules = [ + {org = "ballerina", packageName = "data.jsondata", moduleName = "data.jsondata"} +] + [[package]] org = "ballerina" name = "file" @@ -75,6 +88,7 @@ dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "data.jsondata"}, {org = "ballerina", name = "file"}, {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -104,6 +118,7 @@ name = "http_client_tests" version = "2.13.0" dependencies = [ {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "data.jsondata"}, {org = "ballerina", name = "http"}, {org = "ballerina", name = "http_test_common"}, {org = "ballerina", name = "io"}, diff --git a/ballerina-tests/http-client-tests/tests/client_res_binding_advanced.bal b/ballerina-tests/http-client-tests/tests/client_res_binding_advanced.bal index 10f2002218..59c489ac08 100644 --- a/ballerina-tests/http-client-tests/tests/client_res_binding_advanced.bal +++ b/ballerina-tests/http-client-tests/tests/client_res_binding_advanced.bal @@ -13,9 +13,11 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. + import ballerina/http; import ballerina/mime; import ballerina/test; +import ballerina/data.jsondata; service /api on new http:Listener(resBindingAdvancedPort) { @@ -41,6 +43,31 @@ service /api on new http:Listener(resBindingAdvancedPort) { resource function get byteArray() returns byte[] { return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; } + + resource function get overwriteNames/jsont(boolean y) returns json|TPerson { + if y { + return {"name": "John", "age": "23"}; + } + TPerson t = {firstName: "Potter", personAge: "30"}; + return t; + } + + resource function post overwriteNames/jsont(TPerson payload) returns TPerson { + return payload; + } + + resource function get status/code() returns OKPerson { + return {body: {firstName: "Potter", personAge: "40"}}; + } + + resource function get projection/tests() returns json { + json v = { + a: "a", + b: "b", + c: "c" + }; + return v; + } } final http:Client clientEP = check new (string `localhost:${resBindingAdvancedPort}/api`); @@ -101,3 +128,52 @@ function testResponseWithAnydataResBinding() returns error? { test:assertFail("Invalid response type"); } } + +public type TPerson record { + @jsondata:Name { + value: "name" + } + string firstName; + @jsondata:Name { + value: "age" + } + string personAge; +}; + +public type OKPerson record {| + *http:Ok; + TPerson body; +|}; + +@test:Config {} +function clientoverwriteResponseJsonName() returns error? { + TPerson res1 = check clientEP->/overwriteNames/jsont(y = true); + test:assertEquals(res1, {firstName: "John", personAge: "23"}); + + json res2 = check clientEP->/overwriteNames/jsont(y = false); + test:assertEquals(res2, {"name": "Potter", "age": "30"}); + + json j = { + name: "Sumudu", + age: "29" + }; + + TPerson res3 = check clientEP->/overwriteNames/jsont.post(j); + test:assertEquals(res3, {firstName: "Sumudu", personAge: "29"}); + + json res4 = check clientEP->/status/code; + test:assertEquals(res4, {name: "Potter", age: "40"}); +} + +public type AB record {| + string a; + string b; +|}; + +@test:Config {} +function projectionTestWithClient() { + AB|error res = clientEP->/projection/tests(); + if res is error { + test:assertEquals(res.message(), "Payload binding failed: undefined field 'c'"); + } +} \ No newline at end of file diff --git a/ballerina-tests/http-client-tests/tests/http_client_data_binding.bal b/ballerina-tests/http-client-tests/tests/http_client_data_binding.bal index 10cbb82157..655eae5358 100644 --- a/ballerina-tests/http-client-tests/tests/http_client_data_binding.bal +++ b/ballerina-tests/http-client-tests/tests/http_client_data_binding.bal @@ -616,9 +616,9 @@ function testAllBindingErrorsWithNillableTypes() returns error? { test:assertEquals(response.statusCode, 200, msg = "Found unexpected output"); common:assertHeaderValue(check response.getHeader(common:CONTENT_TYPE), common:TEXT_PLAIN); common:assertTextPayload(response.getTextPayload(), - "Payload binding failed: 'map' value cannot be converted to " + - "'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>?'|" + - "incompatible typedesc int? found for 'text/plain' mime type"); + "Payload binding failed: incompatible expected type 'xml<(lang.xml:Element|lang.xml:Comment|" + + "lang.xml:ProcessingInstruction|lang.xml:Text)>?' for value " + + "'{\"id\":\"chamil\",\"values\":{\"a\":2,\"b\":45,\"c\":{\"x\":\"mnb\",\"y\":\"uio\"}}}'|incompatible typedesc int? found for 'text/plain' mime type"); } else { test:assertFail(msg = "Found unexpected output type: " + response.message()); } @@ -815,9 +815,7 @@ function testDBRecordErrorNegative() { ClientDBErrorPerson|error response = clientDBBackendClient->post("/backend/getRecord", "want record"); if (response is error) { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'map' value cannot be converted to 'http_client_tests:ClientDBErrorPerson'"); - common:assertTrueTextPayload(response.message(), - "missing required field 'weight' of type 'float' in record 'http_client_tests:ClientDBErrorPerson'"); + "Payload binding failed: required field 'weight' not present in JSON"); } else { test:assertFail(msg = "Found unexpected output type: ClientDBErrorPerson"); } @@ -828,7 +826,7 @@ function testDBRecordArrayNegative() { ClientDBErrorPerson[]|error response = clientDBBackendClient->post("/backend/getRecordArr", "want record arr"); if (response is error) { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'json[]' value cannot be converted to 'http_client_tests:ClientDBErrorPerson[]'"); + "Payload binding failed: required field 'weight' not present in JSON"); } else { test:assertFail(msg = "Found unexpected output type: ClientDBErrorPerson[]"); } @@ -852,7 +850,7 @@ function testMapOfStringDataBindingWithJsonPayload() { map|error response = clientDBBackendClient->get("/backend/getJson"); if (response is error) { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'map' value cannot be converted to 'map'"); + "Payload binding failed: incompatible expected type 'string' for value '{\"a\":2,\"b\":45,\"c\":{\"x\":\"mnb\",\"y\":\"uio\"}}'"); } else { test:assertFail(msg = "Found unexpected output type: map"); } diff --git a/ballerina-tests/http-client-tests/tests/http_client_data_binding_anydata.bal b/ballerina-tests/http-client-tests/tests/http_client_data_binding_anydata.bal index 55551d893d..ac9684527e 100755 --- a/ballerina-tests/http-client-tests/tests/http_client_data_binding_anydata.bal +++ b/ballerina-tests/http-client-tests/tests/http_client_data_binding_anydata.bal @@ -341,7 +341,7 @@ function testIntMapDatabindingByType() returns error? { test:assertEquals(response, {"name": 11, "team": 22}, msg = "Found unexpected output"); } -@test:Config {} +@test:Config {enable:false} function testIntTableDatabinding() returns error? { table> tbl = check clientDBBackendClient->get("/anydataTest/intTableType"); object { @@ -355,7 +355,7 @@ function testIntTableDatabinding() returns error? { } } -@test:Config {} +@test:Config {enable:false} function testIntTableOrMapofIntArrayDatabinding() returns error? { map[]|table> response = check clientDBBackendClient->get("/anydataTest/intTableType"); if response is map[] { @@ -369,7 +369,7 @@ function testIntTableOrMapofIntArrayDatabinding() returns error? { } } -@test:Config {} +@test:Config {enable:false} function testIntTableOrXmlArrayDatabinding() returns error? { table>|xml tbl = check clientDBBackendClient->get("/anydataTest/intTableType"); if tbl is table> { @@ -387,7 +387,7 @@ function testIntTableOrXmlArrayDatabinding() returns error? { } } -@test:Config {} +@test:Config {enable:false} function testIntTableDatabindingByType() returns error? { table> tbl = check clientDBBackendClient->get("/anydataTest/intTableTypeWithInvalidMimeType"); object { @@ -448,7 +448,7 @@ function testStringMapDatabindingByType() returns error? { test:assertEquals(response, {name: "hello", team: "ballerina"}, msg = "Found unexpected output"); } -@test:Config {} +@test:Config {enable:false} function testStringTableDatabinding() returns error? { table> tbl = check clientDBBackendClient->get("/anydataTest/stringTableType"); object { @@ -462,7 +462,7 @@ function testStringTableDatabinding() returns error? { } } -@test:Config {} +@test:Config {enable:false} function testStringTableDatabindingByType() returns error? { table> tbl = check clientDBBackendClient->get("/anydataTest/stringTableTypeWithInvalidMimeType"); object { @@ -508,7 +508,7 @@ function testRecordMapDatabindingByType() returns error? { test:assertEquals(response.get("1"), {name: "hello", age: 23}, msg = "Found unexpected output"); } -@test:Config {} +@test:Config {enable:false} function testRecordTableDatabinding() returns error? { table tbl = check clientDBBackendClient->get("/anydataTest/recordTableType"); object { @@ -522,7 +522,7 @@ function testRecordTableDatabinding() returns error? { } } -@test:Config {} +@test:Config {enable: false} function testRecordTableDatabindingByType() returns error? { table tbl = check clientDBBackendClient->get("/anydataTest/recordTableTypeWithInvalidMimeType"); object { @@ -574,7 +574,7 @@ function testByteArrMapDatabindingByType() returns error? { test:assertEquals(check strings:fromBytes(val), "STDLIB", msg = "Found unexpected output"); } -@test:Config {} +@test:Config {enable: false} function testByteArrTableDatabinding() returns error? { table> response = check clientDBBackendClient->get("/anydataTest/byteArrTableType"); object { @@ -589,7 +589,7 @@ function testByteArrTableDatabinding() returns error? { } } -@test:Config {} +@test:Config {enable:false} function testByteArrTableDatabindingByType() returns error? { table> response = check clientDBBackendClient->get("/anydataTest/byteArrTableTypeWithInvalidMimeType"); object { @@ -609,7 +609,7 @@ function testXmlArrDatabinding() { xml[]|error response = clientDBBackendClient->get("/anydataTest/xmlArrType"); if response is error { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'json[]' value cannot be converted to 'xml<"); + "Payload binding failed: invalid type 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' expected 'anydata'"); } else { test:assertEquals(response[0], xml `WSO2`, msg = "Found unexpected output"); } @@ -620,7 +620,7 @@ function testXmlArrDatabindingByType() { xml[]|error response = clientDBBackendClient->get("/anydataTest/xmlArrTypeWithInvalidMimeType"); if response is error { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'json[]' value cannot be converted to 'xml"); + "Payload binding failed: invalid type 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>' expected 'anydata'"); } else { test:assertEquals(response[0], xml `WSO2`, msg = "Found unexpected output"); } 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 169581bf25..8d653efd66 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 @@ -282,6 +282,10 @@ service /api on new http:Listener(statusCodeBindingPort2) { headers: {userId: "user-1", reqId: 1} }; } + + resource function get album/auther() returns OKPerson { + return {body: {firstName: "Potter", personAge: "40"}}; + } } final http:StatusCodeClient albumClient = check new (string `localhost:${statusCodeBindingPort2}/api`); @@ -467,8 +471,8 @@ function testUnionPayloadBindingWithStatusCodeResponse() returns error? { 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"); + test:assertTrue(res5.message().includes("Payload binding failed: required field 'invalidField' not present in JSON"), + "Invalid error message"); } else { test:assertFail("Invalid response type"); } @@ -670,3 +674,9 @@ function testStatusCodeBindingWithNamedHeaders() returns error? { test:assertFail("Invalid response type"); } } + +@test:Config {} +function testOverwriteName() returns error? { + OKPerson res = check albumClient->/album/auther; + test:assertEquals(res.body, {firstName: "Potter", personAge: "40"}); +} diff --git a/ballerina-tests/http-dispatching-tests/tests/service_dispatching_data_binding_test.bal b/ballerina-tests/http-dispatching-tests/tests/service_dispatching_data_binding_test.bal index b983b2e23d..11ce91dcd7 100644 --- a/ballerina-tests/http-dispatching-tests/tests/service_dispatching_data_binding_test.bal +++ b/ballerina-tests/http-dispatching-tests/tests/service_dispatching_data_binding_test.bal @@ -394,11 +394,7 @@ function testDataBindingStructWithNoMatchingContent() returns error? { http:Response|error response = dataBindingClient->post("/dataBinding/body6", req); if response is http:Response { test:assertEquals(response.statusCode, 400, msg = "Found unexpected output"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "data binding failed: {ballerina"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "}ConversionError, {\"message\":\"'map' "); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "value cannot be converted to 'http_dispatching_tests:Person':"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "missing required field 'age' of type 'int' in record 'http_dispatching_tests:Person'"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "field 'team' cannot be added to the closed record 'http_dispatching_tests:Person'\""); + check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "data binding failed: undefined field 'team'"); } else { test:assertFail(msg = "Found unexpected output type: " + response.message()); } @@ -411,11 +407,7 @@ function testDataBindingStructWithInvalidTypes() returns error? { http:Response|error response = dataBindingClient->post("/dataBinding/body7", req); if response is http:Response { test:assertEquals(response.statusCode, 400, msg = "Found unexpected output"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "'map' value cannot be converted to 'http_dispatching_tests:Stock'"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "missing required field 'price' of type 'float' in record 'http_dispatching_tests:Stock'"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "missing required field 'id' of type 'int' in record 'http_dispatching_tests:Stock'"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "field 'name' cannot be added to the closed record 'http_dispatching_tests:Stock'"); - check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "field 'team' cannot be added to the closed record 'http_dispatching_tests:Stock'"); + check common:assertJsonErrorPayloadPartialMessage(check response.getJsonPayload(), "data binding failed: undefined field 'name'"); } else { test:assertFail(msg = "Found unexpected output type: " + response.message()); } diff --git a/ballerina-tests/http-misc-tests/tests/http_outbound_message_type_test.bal b/ballerina-tests/http-misc-tests/tests/http_outbound_message_type_test.bal index 0de55478f4..be10dc80c5 100644 --- a/ballerina-tests/http-misc-tests/tests/http_outbound_message_type_test.bal +++ b/ballerina-tests/http-misc-tests/tests/http_outbound_message_type_test.bal @@ -77,7 +77,9 @@ public function testSendingMap() returns error? { common:assertJsonPayload(resp.getJsonPayload(), {sam: 50, jhon: 60}); } -@test:Config {} +@test:Config { + enable: false +} public function testSendingMapArray() returns error? { map jj = {sam: {hello: "world"}, jhon: {no: 56}}; map[] val = [jj, jj]; @@ -102,7 +104,9 @@ public function testSendingTable() returns error? { ]); } -@test:Config {} +@test:Config { + enable: false +} public function testSendingTableArray() returns error? { CustomerTable customerTab = table [ {id: 13, fname: "Dan", lname: "Bing"} @@ -236,7 +240,7 @@ public function testSendingClosedRecordTable() returns error? { ]); } -@test:Config {} +@test:Config { enable: false} public function testRequestAnydataNegative() returns error? { json[] x = []; x.push(x); @@ -472,7 +476,7 @@ public function testGettingClosedRecordArray() returns error? { ]); } -@test:Config {} +@test:Config {enable: false} public function testResponseAnydataNegative() returns error? { http:Response resp = check outRequestClient->get("/mytest/anydataNegative"); test:assertEquals(resp.statusCode, 500, msg = "Found unexpected output"); diff --git a/ballerina-tests/http-misc-tests/tests/resource_return_statment_test.bal b/ballerina-tests/http-misc-tests/tests/resource_return_statment_test.bal index a7d44e444c..754205a5d6 100644 --- a/ballerina-tests/http-misc-tests/tests/resource_return_statment_test.bal +++ b/ballerina-tests/http-misc-tests/tests/resource_return_statment_test.bal @@ -680,7 +680,9 @@ public function testReturnMapOfJson() returns error? { } } -@test:Config {} +@test:Config { + enable: false +} public function testReturnMapOfJsonArr() returns error? { http:Response|error resp = resourceReturnTestClient->get("/mytest/test22"); if resp is http:Response { @@ -695,7 +697,9 @@ public function testReturnMapOfJsonArr() returns error? { } } -@test:Config {} +@test:Config { + enable: false +} public function testReturnTableArr() returns error? { http:Response|error resp = resourceReturnTestClient->get("/mytest/test23"); if resp is http:Response { @@ -716,7 +720,9 @@ public function testReturnTableArr() returns error? { } } -@test:Config {} +@test:Config { + enable: false +} public function testReturnXmlArr() returns error? { http:Response|error resp = resourceReturnTestClient->get("/mytest/test24"); if resp is http:Response { @@ -728,7 +734,9 @@ public function testReturnXmlArr() returns error? { } } -@test:Config {} +@test:Config { + enable: false +} public function testReturnMapOfXml() returns error? { http:Response|error resp = resourceReturnTestClient->get("/mytest/test25"); if resp is http:Response { diff --git a/ballerina-tests/http-resiliency-tests/Dependencies.toml b/ballerina-tests/http-resiliency-tests/Dependencies.toml index d8ec966c51..4c64fa7e27 100644 --- a/ballerina-tests/http-resiliency-tests/Dependencies.toml +++ b/ballerina-tests/http-resiliency-tests/Dependencies.toml @@ -50,6 +50,16 @@ dependencies = [ {org = "ballerina", name = "time"} ] +[[package]] +org = "ballerina" +name = "data.jsondata" +version = "0.3.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + [[package]] org = "ballerina" name = "file" @@ -72,6 +82,7 @@ dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "data.jsondata"}, {org = "ballerina", name = "file"}, {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, diff --git a/ballerina-tests/http2-tests/tests/http2_client_data_binding.bal b/ballerina-tests/http2-tests/tests/http2_client_data_binding.bal index 94560d98b3..f1819a8940 100644 --- a/ballerina-tests/http2-tests/tests/http2_client_data_binding.bal +++ b/ballerina-tests/http2-tests/tests/http2_client_data_binding.bal @@ -742,9 +742,9 @@ function testHttp2AllBindingErrorsWithNillableTypes() returns error? { test:assertEquals(response.statusCode, 200, msg = "Found unexpected output"); common:assertHeaderValue(check response.getHeader(common:CONTENT_TYPE), common:TEXT_PLAIN); common:assertTextPayload(response.getTextPayload(), - "Payload binding failed: 'map' value cannot be converted to " + - "'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>?'|" + - "incompatible typedesc int? found for 'text/plain' mime type"); + "Payload binding failed: incompatible expected type 'xml<(lang.xml:Element|lang.xml:Comment|" + + "lang.xml:ProcessingInstruction|lang.xml:Text)>?' for value '{\"id\":\"chamil\",\"values\":" + + "{\"a\":2,\"b\":45,\"c\":{\"x\":\"mnb\",\"y\":\"uio\"}}}'|incompatible typedesc int? found for 'text/plain' mime type"); } else { test:assertFail(msg = "Found unexpected output type: " + response.message()); } @@ -936,9 +936,7 @@ function testHttp2DBRecordErrorNegative() { ClientDBErrorPerson|error response = http2ClientDBBackendClient->post("/backend/getRecord", "want record"); if (response is error) { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'map' value cannot be converted to 'http2_tests:ClientDBErrorPerson'"); - common:assertTrueTextPayload(response.message(), - "missing required field 'weight' of type 'float' in record 'http2_tests:ClientDBErrorPerson'"); + "Payload binding failed: required field 'weight' not present in JSON"); } else { test:assertFail(msg = "Found unexpected output type: ClientDBErrorPerson"); } @@ -949,7 +947,7 @@ function testHttp2DBRecordArrayNegative() { ClientDBErrorPerson[]|error response = http2ClientDBBackendClient->post("/backend/getRecordArr", "want record arr"); if (response is error) { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'json[]' value cannot be converted to 'http2_tests:ClientDBErrorPerson[]'"); + "Payload binding failed: required field 'weight' not present in JSON"); } else { test:assertFail(msg = "Found unexpected output type: ClientDBErrorPerson[]"); } @@ -973,7 +971,7 @@ function testHttp2MapOfStringDataBindingWithJsonPayload() { map|error response = http2ClientDBBackendClient->get("/backend/getJson"); if (response is error) { common:assertTrueTextPayload(response.message(), - "Payload binding failed: 'map' value cannot be converted to 'map'"); + "Payload binding failed: incompatible expected type 'string' for value '{\"a\":2,\"b\":45,\"c\":{\"x\":\"mnb\",\"y\":\"uio\"}}'"); } else { test:assertFail(msg = "Found unexpected output type: map"); } diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 4ae4986ba8..f494781687 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -7,7 +7,7 @@ keywords = ["http", "network", "service", "listener", "client"] repository = "https://github.com/ballerina-platform/module-ballerina-http" icon = "icon.png" license = ["Apache-2.0"] -distribution = "2201.10.0" +distribution = "2201.11.0" export = ["http", "http.httpscerr"] [platform.java21] @@ -22,68 +22,68 @@ path = "../native/build/libs/http-native-2.13.0-SNAPSHOT.jar" [[platform.java21.dependency]] groupId = "io.ballerina.stdlib" artifactId = "mime-native" -version = "2.10.1" -path = "./lib/mime-native-2.10.1-20241009-141200-8b6c9f0.jar" +version = "2.10.2" +path = "./lib/mime-native-2.10.2-20241113-154200-d953747.jar" [[platform.java21.dependency]] groupId = "io.ballerina.stdlib" artifactId = "constraint-native" -version = "1.5.1" -path = "./lib/constraint-native-1.5.1-20240930-123400-5ecd396.jar" +version = "1.6.0" +path = "./lib/constraint-native-1.6.0-20241113-090900-d276ad5.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-common" -version = "4.1.108.Final" -path = "./lib/netty-common-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-common-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-buffer" -version = "4.1.108.Final" -path = "./lib/netty-buffer-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-buffer-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-transport" -version = "4.1.108.Final" -path = "./lib/netty-transport-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-transport-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-resolver" -version = "4.1.108.Final" -path = "./lib/netty-resolver-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-resolver-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-handler" -version = "4.1.108.Final" -path = "./lib/netty-handler-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-handler-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-codec-http" -version = "4.1.108.Final" -path = "./lib/netty-codec-http-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-codec-http-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-codec" -version = "4.1.108.Final" -path = "./lib/netty-codec-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-codec-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-handler-proxy" -version = "4.1.108.Final" -path = "./lib/netty-handler-proxy-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-handler-proxy-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-codec-http2" -version = "4.1.108.Final" -path = "./lib/netty-codec-http2-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-codec-http2-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "commons-pool.wso2" @@ -94,8 +94,8 @@ path = "./lib/commons-pool-1.5.6.wso2v1.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-transport-native-unix-common" -version = "4.1.108.Final" -path = "./lib/netty-transport-native-unix-common-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-transport-native-unix-common-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "org.bouncycastle" @@ -151,8 +151,8 @@ path = "./lib/mimepull-1.9.11.jar" [[platform.java21.dependency]] groupId = "io.netty" artifactId = "netty-codec-socks" -version = "4.1.108.Final" -path = "./lib/netty-codec-socks-4.1.108.Final.jar" +version = "4.1.115.Final" +path = "./lib/netty-codec-socks-4.1.115.Final.jar" [[platform.java21.dependency]] groupId = "org.jboss.marshalling" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 10711cd473..6569c1e4eb 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -58,6 +58,18 @@ modules = [ {org = "ballerina", packageName = "crypto", moduleName = "crypto"} ] +[[package]] +org = "ballerina" +name = "data.jsondata" +version = "0.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] +modules = [ + {org = "ballerina", packageName = "data.jsondata", moduleName = "data.jsondata"} +] + [[package]] org = "ballerina" name = "file" @@ -81,6 +93,7 @@ dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "data.jsondata"}, {org = "ballerina", name = "file"}, {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, diff --git a/ballerina/http_client_payload_builder.bal b/ballerina/http_client_payload_builder.bal index bfadcd2498..ca4f012e74 100644 --- a/ballerina/http_client_payload_builder.bal +++ b/ballerina/http_client_payload_builder.bal @@ -14,9 +14,10 @@ // specific language governing permissions and limitations // under the License. -import ballerina/log; -import ballerina/jballerina.java; import ballerina/constraint; +import ballerina/data.jsondata; +import ballerina/jballerina.java; +import ballerina/log; type nilType typedesc<()>; type xmlType typedesc; @@ -98,7 +99,7 @@ isolated function textPayloadBuilder(Response response, TargetType targetType) r } return payload; } else { - return getCommonError(response, targetType); + return getCommonError(response, targetType); } } @@ -161,7 +162,7 @@ isolated function jsonPayloadBuilder(Response response, TargetType targetType) r isolated function nonNilablejsonPayloadBuilder(Response response, typedesc targetType) returns anydata|ClientError { json payload = check response.getJsonPayload(); - var result = payload.fromJsonWithType(targetType); + var result = jsondata:parseAsType(payload, {enableConstraintValidation: false, allowDataProjection: false}, targetType); return result is error ? createPayloadBindingError(result) : result; } @@ -169,7 +170,7 @@ isolated function nilablejsonPayloadBuilder(Response response, typedesc returns anydata|ClientError { json|ClientError payload = response.getJsonPayload(); if payload is json { - var result = payload.fromJsonWithType(targetType); + var result = jsondata:parseAsType(payload, {enableConstraintValidation: false, allowDataProjection: false}, targetType); return result is error ? createPayloadBindingError(result) : result; } else { return payload is NoContentError ? () : payload; diff --git a/ballerina/http_commons.bal b/ballerina/http_commons.bal index 2789871d92..e3bf97afcf 100644 --- a/ballerina/http_commons.bal +++ b/ballerina/http_commons.bal @@ -23,6 +23,7 @@ import ballerina/time; import ballerina/log; import ballerina/lang.'string as strings; import ballerina/url; +import ballerina/data.jsondata; final boolean observabilityEnabled = observe:isObservabilityEnabled(); @@ -146,7 +147,7 @@ isolated function processUrlEncodedContent(map message) returns string|C } isolated function processJsonContent(anydata message) returns json|ClientError { - var result = trap val:toJson(message); + var result = trap jsondata:toJson(message); if result is error { return error InitializingOutboundRequestError("json conversion error: " + result.message(), result); } @@ -174,7 +175,7 @@ isolated function buildResponse(ResponseMessage message, string? resourceAccesso } else if message is mime:Entity[] { response.setBodyParts(message); } else if message is anydata { - var result = trap val:toJson(message); + var result = trap jsondata:toJson(message); if result is error { return error InitializingOutboundResponseError("json conversion error: " + result.message(), result); } else { diff --git a/ballerina/http_connection.bal b/ballerina/http_connection.bal index 3c640dfaf8..29b23a0954 100644 --- a/ballerina/http_connection.bal +++ b/ballerina/http_connection.bal @@ -20,6 +20,7 @@ import ballerina/lang.'string as strings; import ballerina/url; import ballerina/mime; import http.httpscerr; +import ballerina/data.jsondata; # The caller actions for responding to client requests. # @@ -370,7 +371,7 @@ isolated function retrieveUrlEncodedData(map message) returns string|err } isolated function setJsonPayload(Response response, anydata payload, boolean setETag) { - var result = trap val:toJson(payload); + var result = trap jsondata:toJson(payload); if result is error { panic error InitializingOutboundResponseError(string `anydata to json conversion error: ${result.message()}`, result); } diff --git a/ballerina/http_request.bal b/ballerina/http_request.bal index 3d5de29bab..f6fe1351c1 100644 --- a/ballerina/http_request.bal +++ b/ballerina/http_request.bal @@ -21,6 +21,7 @@ import ballerina/log; import ballerina/mime; import ballerina/jballerina.java; import ballerina/url; +import ballerina/data.jsondata; # Represents an HTTP request. # @@ -414,17 +415,33 @@ public class Request { # Sets a `json` as the payload. If the content-type header is not set then this method set content-type # headers with the default content-type, which is `application/json`. Any existing content-type can be - # overridden by passing the content-type as an optional parameter. + # overridden by passing the content-type as an optional parameter. If the given payload is a record type with + # the `@jsondata:Name` annotation, the `jsondata:toJson` function internally converts the record to JSON # # + payload - The `json` payload # + contentType - The content type of the payload. This is an optional parameter. # The `application/json` is the default value public isolated function setJsonPayload(json payload, string? contentType = ()) { mime:Entity entity = self.getEntityWithoutBodyAndHeaders(); - setJson(entity, payload, self.getContentType(), contentType); + setJson(entity, jsondata:toJson(payload), self.getContentType(), contentType); self.setEntityAndUpdateContentTypeHeader(entity); } + # Sets a `anydata` type payload, as a `json` payload. If the content-type header is not set then this method set content-type + # headers with the default content-type, which is `application/json`. Any existing content-type can be + # overridden by passing the content-type as an optional parameter. If the given payload is a record type + # with the `@jsondata:Name` annotation, the `jsondata:toJson` function processes the name and populates + # the JSON according to the annotation's details. + # + # + payload - The `json` payload + # + contentType - The content type of the payload. This is an optional parameter. + # The `application/json` is the default value + isolated function setAnydataAsJsonPayload(anydata payload, string? contentType = ()) { + mime:Entity entity = self.getEntityWithoutBodyAndHeaders(); + setJson(entity, jsondata:toJson(payload), self.getContentType(), contentType); + self.setEntityAndUpdateContentTypeHeader(entity); +} + # Sets an `xml` as the payload. If the content-type header is not set then this method set content-type # headers with the default content-type, which is `application/xml`. Any existing content-type can be # overridden by passing the content-type as an optional parameter. @@ -546,7 +563,7 @@ public class Request { } else if payload is mime:Entity[] { self.setBodyParts(payload); } else if payload is anydata { - self.setJsonPayload(payload.toJson()); + self.setAnydataAsJsonPayload(payload); } else { panic error Error("invalid entity body type." + "expected one of the types: string|xml|json|byte[]|mime:Entity[]|stream"); diff --git a/ballerina/http_response.bal b/ballerina/http_response.bal index 002621e8f3..3cb39c7116 100644 --- a/ballerina/http_response.bal +++ b/ballerina/http_response.bal @@ -21,6 +21,7 @@ import ballerina/crypto; import ballerina/time; import ballerina/jballerina.java; import ballerina/log; +import ballerina/data.jsondata; # Represents an HTTP response. # @@ -386,7 +387,20 @@ public class Response { # The `application/json` is the default value public isolated function setJsonPayload(json payload, string? contentType = ()) { mime:Entity entity = self.getEntityWithoutBodyAndHeaders(); - setJson(entity, payload, self.getContentType(), contentType); + setJson(entity, jsondata:toJson(payload), self.getContentType(), contentType); + self.setEntityAndUpdateContentTypeHeader(entity); + } + + # Sets a `anydata` payaload, as a `json` payload. If the content-type header is not set then this method set content-type + # headers with the default content-type, which is `application/json`. Any existing content-type can be + # overridden by passing the content-type as an optional parameter. + # + # + payload - The `json` payload + # + contentType - The content type of the payload. This is an optional parameter. + # The `application/json` is the default value + public isolated function setAnydataAsJsonPayload(anydata payload, string? contentType = ()) { + mime:Entity entity = self.getEntityWithoutBodyAndHeaders(); + setJson(entity, jsondata:toJson(payload), self.getContentType(), contentType); self.setEntityAndUpdateContentTypeHeader(entity); } @@ -526,7 +540,7 @@ public class Response { } else if payload is stream { self.setSseEventStream(payload); } else if payload is anydata { - self.setJsonPayload(payload.toJson()); + self.setAnydataAsJsonPayload(payload); } else { panic error Error("invalid entity body type." + "expected one of the types: string|xml|json|byte[]|mime:Entity[]|stream"); diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml index 81853a7955..8dad76ebd1 100644 --- a/build-config/resources/Ballerina.toml +++ b/build-config/resources/Ballerina.toml @@ -7,7 +7,7 @@ keywords = ["http", "network", "service", "listener", "client"] repository = "https://github.com/ballerina-platform/module-ballerina-http" icon = "icon.png" license = ["Apache-2.0"] -distribution = "2201.10.0" +distribution = "2201.11.0" export = ["http", "http.httpscerr"] [platform.java21] diff --git a/build.gradle b/build.gradle index 46a77c629c..e70685a215 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,7 @@ ext.stdlibCryptoVersion = project.stdlibCryptoVersion ext.stdlibFileVersion = project.stdlibFileVersion ext.stdlibOsVersion = project.stdlibOsVersion ext.stdlibTaskVersion = project.stdlibTaskVersion +ext.stdlibDataJsonDataVersion = project.stdlibDataJsonDataVersion ext.slf4jVersion = project.slf4jVersion ext.ballerinaTomlParserVersion = project.ballerinaTomlParserVersion @@ -111,6 +112,7 @@ subprojects { ballerinaStdLibs "io.ballerina.stdlib:crypto-ballerina:${stdlibCryptoVersion}" ballerinaStdLibs "io.ballerina.stdlib:file-ballerina:${stdlibFileVersion}" ballerinaStdLibs "io.ballerina.stdlib:observe-ballerina:${observeVersion}" + ballerinaStdLibs "io.ballerina.lib:data.jsondata-ballerina:${stdlibDataJsonDataVersion}" ballerinaStdLibs "io.ballerina:observe-ballerina:${observeInternalVersion}" // Transitive dependencies diff --git a/changelog.md b/changelog.md index 4935b7c73b..0e8cda6418 100644 --- a/changelog.md +++ b/changelog.md @@ -14,11 +14,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - [Improve `@http:Query` annotation to overwrite the query parameter name in service](https://github.com/ballerina-platform/ballerina-library/issues/7006) - [Add header name mapping support in record fields](https://github.com/ballerina-platform/ballerina-library/issues/7018) - [Introduce util functions to convert query and header record with the `http:Query` and the `http:Header` annotations](https://github.com/ballerina-platform/ballerina-library/issues/7019) +- [Migrate client and service data binding lang utils usage into data.jsondata module utils `toJson` and `parserAsType`] (https://github.com/ballerina-platform/ballerina-library/issues/6747) +- [Add static code rules](https://github.com/ballerina-platform/ballerina-library/issues/7283) ### Fixed - [Address CVE-2024-7254 vulnerability](https://github.com/ballerina-platform/ballerina-library/issues/7013) - [Fix duplicating `Content-Type` header via the `addHeader` method](https://github.com/ballerina-platform/ballerina-library/issues/7268) +- [Update netty version](https://github.com/ballerina-platform/ballerina-library/issues/7358) ## [2.12.0] - 2024-08-20 diff --git a/compiler-plugin-tests/build.gradle b/compiler-plugin-tests/build.gradle index 87375786e5..e5ae999e5b 100644 --- a/compiler-plugin-tests/build.gradle +++ b/compiler-plugin-tests/build.gradle @@ -93,6 +93,7 @@ test { systemProperty "ballerina.offline.flag", "true" useTestNG() finalizedBy jacocoTestReport + testLogging.showStandardStreams = true } jacocoTestReport { diff --git a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/ProcessOutputGobbler.java b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/ProcessOutputGobbler.java new file mode 100644 index 0000000000..462b43de08 --- /dev/null +++ b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/ProcessOutputGobbler.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * Helper class to consume the process streams. + */ +class ProcessOutputGobbler implements Runnable { + private final InputStream inputStream; + private final StringBuilder output; + private int exitCode; + + public ProcessOutputGobbler(InputStream inputStream) { + this.inputStream = inputStream; + this.output = new StringBuilder(); + } + + @Override + public void run() { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + } + } catch (IOException e) { + this.output.append(e.getMessage()); + } + } + + public String getOutput() { + return output.toString(); + } + + public int getExitCode() { + return exitCode; + } + + public void setExitCode(int exitCode) { + this.exitCode = exitCode; + } +} diff --git a/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/StaticCodeAnalyzerTest.java b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/StaticCodeAnalyzerTest.java new file mode 100644 index 0000000000..07feda9311 --- /dev/null +++ b/compiler-plugin-tests/src/test/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/StaticCodeAnalyzerTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import org.testng.Assert; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; +import org.testng.internal.ExitCode; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Locale; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * This class includes tests for Ballerina Http static code analyzer. + */ +class StaticCodeAnalyzerTest { + + private static final Path RESOURCE_PACKAGES_DIRECTORY = Paths + .get("src", "test", "resources", "static_code_analyzer", "ballerina_packages").toAbsolutePath(); + private static final Path EXPECTED_JSON_OUTPUT_DIRECTORY = Paths. + get("src", "test", "resources", "static_code_analyzer", "expected_output").toAbsolutePath(); + private static final Path BALLERINA_PATH = getBalCommandPath(); + private static final Path JSON_RULES_FILE_PATH = Paths + .get("../", "compiler-plugin", "src", "main", "resources", "rules.json").toAbsolutePath(); + private static final String SCAN_COMMAND = "scan"; + + private static Path getBalCommandPath() { + String balCommand = isWindows() ? "bal.bat" : "bal"; + return Paths.get("../", "target", "ballerina-runtime", "bin", balCommand).toAbsolutePath(); + } + + @BeforeSuite + public void pullScanTool() throws IOException, InterruptedException { + ProcessBuilder processBuilder = new ProcessBuilder(BALLERINA_PATH.toString(), "tool", "pull", SCAN_COMMAND); + ProcessOutputGobbler output = getOutput(processBuilder.start()); + if (Pattern.compile("tool 'scan:.+\\..+\\..+' successfully set as the active version\\.") + .matcher(output.getOutput()).find() || Pattern.compile("tool 'scan:.+\\..+\\..+' is already active\\.") + .matcher(output.getOutput()).find()) { + return; + } + Assert.assertFalse(ExitCode.hasFailure(output.getExitCode())); + } + + @Test + public void validateRulesJson() throws IOException { + String expectedRules = "[" + Arrays.stream(HttpRule.values()) + .map(HttpRule::toString).collect(Collectors.joining(",")) + "]"; + String actualRules = Files.readString(JSON_RULES_FILE_PATH); + assertJsonEqual(normalizeJson(actualRules), normalizeJson(expectedRules)); + } + + @Test + public void testStaticCodeRules() throws IOException, InterruptedException { + for (HttpRule rule : HttpRule.values()) { + String targetPackageName = "rule" + rule.getId(); + String actualJsonReport = StaticCodeAnalyzerTest.executeScanProcess(targetPackageName); + String expectedJsonReport = Files + .readString(EXPECTED_JSON_OUTPUT_DIRECTORY.resolve(targetPackageName + ".json")); + assertJsonEqual(actualJsonReport, expectedJsonReport); + } + } + + public static String executeScanProcess(String targetPackage) throws IOException, InterruptedException { + ProcessBuilder processBuilder = new ProcessBuilder(BALLERINA_PATH.toString(), SCAN_COMMAND); + processBuilder.directory(RESOURCE_PACKAGES_DIRECTORY.resolve(targetPackage).toFile()); + ProcessOutputGobbler output = getOutput(processBuilder.start()); + Assert.assertFalse(ExitCode.hasFailure(output.getExitCode())); + return Files.readString(RESOURCE_PACKAGES_DIRECTORY.resolve(targetPackage) + .resolve("target").resolve("report").resolve("scan_results.json")); + } + + private static ProcessOutputGobbler getOutput(Process process) throws InterruptedException { + ProcessOutputGobbler outputGobbler = new ProcessOutputGobbler(process.getInputStream()); + ProcessOutputGobbler errorGobbler = new ProcessOutputGobbler(process.getErrorStream()); + Thread outputThread = new Thread(outputGobbler); + Thread errorThread = new Thread(errorGobbler); + outputThread.start(); + errorThread.start(); + int exitCode = process.waitFor(); + outputGobbler.setExitCode(exitCode); + errorGobbler.setExitCode(exitCode); + outputThread.join(); + errorThread.join(); + return outputGobbler; + } + + private void assertJsonEqual(String actual, String expected) { + Assert.assertEquals(normalizeJson(actual), normalizeJson(expected)); + } + + private static String normalizeJson(String json) { + String normalizedJson = json.replaceAll("\\s*\"\\s*", "\"") + .replaceAll("\\s*:\\s*", ":") + .replaceAll("\\s*,\\s*", ",") + .replaceAll("\\s*\\{\\s*", "{") + .replaceAll("\\s*}\\s*", "}") + .replaceAll("\\s*\\[\\s*", "[") + .replaceAll("\\s*]\\s*", "]") + .replaceAll("\n", "") + .replaceAll(":\".*module-ballerina-http", ":\"module-ballerina-http"); + return isWindows() ? normalizedJson.replaceAll("/", "\\\\\\\\") : normalizedJson; + } + + private static boolean isWindows() { + return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows"); + } +} diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/Ballerina.toml b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/Ballerina.toml new file mode 100644 index 0000000000..a2262d50af --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "ballerina" +name = "rule1" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service.bal b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service.bal new file mode 100644 index 0000000000..2490eefb23 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service.bal @@ -0,0 +1,27 @@ +// Copyright (c) 2024 WSO2 LLC. (https://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. + +import ballerina/http; + +service on new http:Listener(8080) { + resource function default .() returns string? { + return; + } + + resource function default greet() returns string? { + return; + } +}; diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_class.bal b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_class.bal new file mode 100644 index 0000000000..9020df9eef --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_class.bal @@ -0,0 +1,29 @@ +// Copyright (c) 2024 WSO2 LLC. (https://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. + +import ballerina/http; + +service class GreetingService { + *http:Service; + + resource function default .() returns string { + return ""; + } + + resource function default greet() returns string { + return ""; + } +}; diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal new file mode 100644 index 0000000000..9ede2843d3 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal @@ -0,0 +1,29 @@ +// Copyright (c) 2024 WSO2 LLC. (https://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. + +import ballerina/http; + +type ServiceContract service object { + *http:Service; + resource function default .() returns string; + resource function default greet() returns string; +}; + +type MyContract service object { + *http:ServiceContract; + resource function default .() returns string; + resource function default greet() returns string; +}; diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/Ballerina.toml b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/Ballerina.toml new file mode 100644 index 0000000000..adf0cc8b06 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "ballerina" +name = "rule2" +version = "0.1.0" diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service.bal b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service.bal new file mode 100644 index 0000000000..8e095f9d41 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service.bal @@ -0,0 +1,34 @@ +// Copyright (c) 2024 WSO2 LLC. (https://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. + +import ballerina/http; + +@http:ServiceConfig { + cors: { + allowOrigins: ["*"] + } +} +service on new http:Listener(8080) { + + @http:ResourceConfig { + cors: { + allowOrigins: ["*"] + } + } + resource function get greet() returns string? { + return; + } +}; diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_class.bal b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_class.bal new file mode 100644 index 0000000000..c8779ff6a1 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_class.bal @@ -0,0 +1,30 @@ +// Copyright (c) 2024 WSO2 LLC. (https://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. + +import ballerina/http; + +service class GreetingService { + *http:Service; + + @http:ResourceConfig { + cors: { + allowOrigins: ["*"] + } + } + resource function get .() returns string? { + return; + } +}; diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal new file mode 100644 index 0000000000..a7c1f6ccd8 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal @@ -0,0 +1,49 @@ +// Copyright (c) 2024 WSO2 LLC. (https://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. + +import ballerina/http; + +@http:ServiceConfig { + cors: { + allowOrigins: ["*"] + } +} +type ServiceContract service object { + *http:Service; + + @http:ResourceConfig { + cors: { + allowOrigins: ["*"] + } + } + resource function get greet() returns string; +}; + +@http:ServiceConfig { + cors: { + allowOrigins: ["*"] + } +} +type MyContract service object { + *http:ServiceContract; + + @http:ResourceConfig { + cors: { + allowOrigins: ["*"] + } + } + resource function get greet() returns string; +}; diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/expected_output/rule1.json b/compiler-plugin-tests/src/test/resources/static_code_analyzer/expected_output/rule1.json new file mode 100644 index 0000000000..0e9afd7895 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/expected_output/rule1.json @@ -0,0 +1,162 @@ +[ + { + "location": { + "filePath": "service.bal", + "startLine": 19, + "endLine": 19, + "startColumn": 22, + "endColumn": 29, + "startOffset": 728, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service.bal" + }, + { + "location": { + "filePath": "service.bal", + "startLine": 23, + "endLine": 23, + "startColumn": 22, + "endColumn": 29, + "startOffset": 803, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service.bal" + }, + { + "location": { + "filePath": "service_class.bal", + "startLine": 21, + "endLine": 21, + "startColumn": 22, + "endColumn": 29, + "startOffset": 743, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service_class.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_class.bal" + }, + { + "location": { + "filePath": "service_class.bal", + "startLine": 25, + "endLine": 25, + "startColumn": 22, + "endColumn": 29, + "startOffset": 820, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service_class.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_class.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 20, + "endLine": 20, + "startColumn": 22, + "endColumn": 29, + "startOffset": 748, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 21, + "endLine": 21, + "startColumn": 22, + "endColumn": 29, + "startOffset": 798, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 26, + "endLine": 26, + "startColumn": 22, + "endColumn": 29, + "startOffset": 916, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 27, + "endLine": 27, + "startColumn": 22, + "endColumn": 29, + "startOffset": 966, + "length": 7 + }, + "rule": { + "id": "ballerina/http:1", + "numericId": 1, + "description": "Avoid allowing default resource accessor", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule1/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule1/service_object.bal" + } +] diff --git a/compiler-plugin-tests/src/test/resources/static_code_analyzer/expected_output/rule2.json b/compiler-plugin-tests/src/test/resources/static_code_analyzer/expected_output/rule2.json new file mode 100644 index 0000000000..00608dc943 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/static_code_analyzer/expected_output/rule2.json @@ -0,0 +1,142 @@ +[ + { + "location": { + "filePath": "service.bal", + "startLine": 20, + "endLine": 20, + "startColumn": 23, + "endColumn": 26, + "startOffset": 726, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service.bal" + }, + { + "location": { + "filePath": "service.bal", + "startLine": 27, + "endLine": 27, + "startColumn": 27, + "endColumn": 30, + "startOffset": 847, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service.bal" + }, + { + "location": { + "filePath": "service_class.bal", + "startLine": 23, + "endLine": 23, + "startColumn": 27, + "endColumn": 30, + "startOffset": 791, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service_class.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_class.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 20, + "endLine": 20, + "startColumn": 23, + "endColumn": 26, + "startOffset": 726, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 28, + "endLine": 28, + "startColumn": 27, + "endColumn": 30, + "startOffset": 867, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 36, + "endLine": 36, + "startColumn": 23, + "endColumn": 26, + "startOffset": 999, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal" + }, + { + "location": { + "filePath": "service_object.bal", + "startLine": 44, + "endLine": 44, + "startColumn": 27, + "endColumn": 30, + "startOffset": 1143, + "length": 3 + }, + "rule": { + "id": "ballerina/http:2", + "numericId": 2, + "description": "Avoid permissive Cross-Origin Resource Sharing", + "ruleKind": "VULNERABILITY" + }, + "source": "BUILT_IN", + "fileName": "rule2/service_object.bal", + "filePath": "/Users/admin/Desktop/WORKSPACE/module-ballerina-http/compiler-plugin-tests/src/test/resources/static_code_analyzer/ballerina_packages/rule2/service_object.bal" + } +] diff --git a/compiler-plugin/build.gradle b/compiler-plugin/build.gradle index e47fb7fb02..e83b8bd5c1 100644 --- a/compiler-plugin/build.gradle +++ b/compiler-plugin/build.gradle @@ -39,6 +39,8 @@ dependencies { implementation group: 'org.ballerinalang', name: 'ballerina-parser', version: "${ballerinaLangVersion}" implementation group: 'io.ballerina.openapi', name: 'ballerina-to-openapi', version: "${ballerinaToOpenApiVersion}" + implementation group: 'io.ballerina.scan', name: 'scan-command', version: "${balScanVersion}" + externalJars group: 'io.ballerina.openapi', name: 'ballerina-to-openapi', version: "${ballerinaToOpenApiVersion}" } diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/Constants.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/Constants.java index 640e9c37d9..8e51c2557a 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/Constants.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/Constants.java @@ -125,4 +125,6 @@ private Constants() {} public static final String SUFFIX_SEPARATOR_REGEX = "\\+"; public static final String MEDIA_TYPE_SUBTYPE_REGEX = "^(\\w)+(\\s*\\.\\s*(\\w)+)*(\\s*\\+\\s*(\\w)+)*"; public static final String UNNECESSARY_CHARS_REGEX = "^'|\"|\\n"; + + public static final String SCANNER_CONTEXT = "ScannerContext"; } diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPlugin.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPlugin.java index 5936bfa6a1..1fd8cb019d 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPlugin.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPlugin.java @@ -23,6 +23,7 @@ import io.ballerina.projects.plugins.CompilerPluginContext; import io.ballerina.projects.plugins.codeaction.CodeAction; import io.ballerina.projects.plugins.completion.CompletionProvider; +import io.ballerina.scan.ScannerContext; import io.ballerina.stdlib.http.compiler.codeaction.AddHeaderParameterCodeAction; import io.ballerina.stdlib.http.compiler.codeaction.AddInterceptorRemoteMethodCodeAction; import io.ballerina.stdlib.http.compiler.codeaction.AddInterceptorResourceMethodCodeAction; @@ -35,10 +36,13 @@ import io.ballerina.stdlib.http.compiler.codeaction.ImplementServiceContract; import io.ballerina.stdlib.http.compiler.codemodifier.HttpServiceModifier; import io.ballerina.stdlib.http.compiler.completion.HttpServiceBodyContextProvider; +import io.ballerina.stdlib.http.compiler.staticcodeanalyzer.HttpStaticCodeAnalyzer; import java.util.List; import java.util.Map; +import static io.ballerina.stdlib.http.compiler.Constants.SCANNER_CONTEXT; + /** * The compiler plugin implementation for Ballerina Http package. */ @@ -52,6 +56,10 @@ public void init(CompilerPluginContext context) { context.addCodeAnalyzer(new HttpServiceAnalyzer(ctxData)); getCodeActions().forEach(context::addCodeAction); getCompletionProviders().forEach(context::addCompletionProvider); + Object object = context.userData().get(SCANNER_CONTEXT); + if (object instanceof ScannerContext scannerContext) { + context.addCodeAnalyzer(new HttpStaticCodeAnalyzer(scannerContext.getReporter())); + } } private List getCodeActions() { diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPluginUtil.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPluginUtil.java index 2db9d66682..7142858cb2 100644 --- a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPluginUtil.java +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/HttpCompilerPluginUtil.java @@ -20,6 +20,7 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.Types; +import io.ballerina.compiler.api.symbols.ClassSymbol; import io.ballerina.compiler.api.symbols.FunctionSymbol; import io.ballerina.compiler.api.symbols.FunctionTypeSymbol; import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; @@ -32,13 +33,16 @@ import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.ClassDefinitionNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.MethodDeclarationNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.ObjectTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.projects.Document; import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; import io.ballerina.tools.diagnostics.Diagnostic; import io.ballerina.tools.diagnostics.DiagnosticFactory; @@ -47,12 +51,19 @@ import io.ballerina.tools.diagnostics.DiagnosticSeverity; import io.ballerina.tools.diagnostics.Location; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; +import static io.ballerina.compiler.api.symbols.SymbolKind.TYPE_DEFINITION; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.ANNOTATION; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.AT_TOKEN; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLASS_DEFINITION; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.RESOURCE_ACCESSOR_DECLARATION; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.RESOURCE_ACCESSOR_DEFINITION; import static io.ballerina.stdlib.http.compiler.Constants.ANYDATA; import static io.ballerina.stdlib.http.compiler.Constants.ARRAY_OF_MAP_OF_ANYDATA; import static io.ballerina.stdlib.http.compiler.Constants.BALLERINA; @@ -60,8 +71,10 @@ import static io.ballerina.stdlib.http.compiler.Constants.BOOLEAN_ARRAY; import static io.ballerina.stdlib.http.compiler.Constants.BYTE_ARRAY; import static io.ballerina.stdlib.http.compiler.Constants.CALLER_OBJ_NAME; +import static io.ballerina.stdlib.http.compiler.Constants.COLON; import static io.ballerina.stdlib.http.compiler.Constants.DECIMAL; import static io.ballerina.stdlib.http.compiler.Constants.DECIMAL_ARRAY; +import static io.ballerina.stdlib.http.compiler.Constants.DEFAULT; import static io.ballerina.stdlib.http.compiler.Constants.EMPTY; import static io.ballerina.stdlib.http.compiler.Constants.ERROR; import static io.ballerina.stdlib.http.compiler.Constants.FLOAT; @@ -92,6 +105,7 @@ import static io.ballerina.stdlib.http.compiler.Constants.REQUEST_OBJ_NAME; import static io.ballerina.stdlib.http.compiler.Constants.RESOURCE_RETURN_TYPE; import static io.ballerina.stdlib.http.compiler.Constants.RESPONSE_OBJ_NAME; +import static io.ballerina.stdlib.http.compiler.Constants.SERVICE_KEYWORD; import static io.ballerina.stdlib.http.compiler.Constants.STRING; import static io.ballerina.stdlib.http.compiler.Constants.STRING_ARRAY; import static io.ballerina.stdlib.http.compiler.Constants.STRUCTURED_ARRAY; @@ -348,6 +362,47 @@ public static ServiceDeclarationNode getServiceDeclarationNode(Node node, Semant return serviceDeclarationNode; } + public static ClassDefinitionNode getServiceClassDefinitionNode(SyntaxNodeAnalysisContext ctx) { + if (ctx.node().kind() != CLASS_DEFINITION) { + return null; + } + ClassDefinitionNode classDefinitionNode = (ClassDefinitionNode) ctx.node(); + Optional serviceType = ctx.semanticModel().types() + .getTypeByName(BALLERINA, HTTP, EMPTY, HTTP_SERVICE_TYPE); + if (!hasServiceKeyWord(classDefinitionNode) || serviceType.isEmpty()) { + return null; + } + Optional symbol = ctx.semanticModel().symbol(classDefinitionNode); + if (symbol.isEmpty() || serviceType.get().kind() != TYPE_DEFINITION) { + return null; + } + ClassSymbol classSymbol = (ClassSymbol) symbol.get(); + TypeSymbol serviceTypeSymbol = ((TypeDefinitionSymbol) serviceType.get()).typeDescriptor(); + return classSymbol.subtypeOf(serviceTypeSymbol) ? classDefinitionNode : null; + } + + public static AnnotationNode getAnnotationNode(SyntaxNodeAnalysisContext context) { + if (context.node().kind() != ANNOTATION) { + return null; + } + Optional symbol = context.semanticModel().symbol(context.node()); + if (symbol.isPresent()) { + Optional module = symbol.get().getModule(); + if (module.isEmpty() || !isHttpModule(module.get())) { + return null; + } + return (AnnotationNode) context.node(); + } + // Added as a workaround for: https://github.com/ballerina-platform/ballerina-lang/issues/43525 + return context.node().toSourceCode().trim().startsWith(AT_TOKEN.stringValue() + HTTP + COLON) ? + (AnnotationNode) context.node() : null; + } + + private static boolean hasServiceKeyWord(ClassDefinitionNode classDefinitionNode) { + return classDefinitionNode.classTypeQualifiers() + .stream().anyMatch(token -> SERVICE_KEYWORD.equals(token.text().trim())); + } + private static boolean isListenerBelongsToHttpModule(TypeSymbol listenerType) { if (listenerType.typeKind() == TypeDescKind.UNION) { return ((UnionTypeSymbol) listenerType).memberTypeDescriptors().stream() @@ -378,13 +433,32 @@ public static boolean isHttpServiceType(SemanticModel semanticModel, Node typeNo return false; } - Optional serviceContractType = semanticModel.types().getTypeByName(BALLERINA, HTTP, EMPTY, + Optional serviceType = semanticModel.types().getTypeByName(BALLERINA, HTTP, EMPTY, HTTP_SERVICE_TYPE); - if (serviceContractType.isEmpty() || - !(serviceContractType.get() instanceof TypeDefinitionSymbol serviceContractTypeDef)) { + if (serviceType.isEmpty() || + !(serviceType.get() instanceof TypeDefinitionSymbol serviceTypeDef)) { return false; } + return serviceObjTypeDef.typeDescriptor().subtypeOf(serviceTypeDef.typeDescriptor()); + } + + public static List getResourceMethodWithDefaultAccessor(NodeList members) { + List resourceFunctions = new ArrayList<>(); + for (Node member : members) { + if (member.kind() != RESOURCE_ACCESSOR_DEFINITION && member.kind() != RESOURCE_ACCESSOR_DECLARATION) { + continue; + } + ResourceFunction resourceFunction = member.kind() == RESOURCE_ACCESSOR_DEFINITION + ? new ResourceFunctionDefinition((FunctionDefinitionNode) member) + : new ResourceFunctionDeclaration((MethodDeclarationNode) member); + if (DEFAULT.equalsIgnoreCase(resourceFunction.functionName().text().trim())) { + resourceFunctions.add(resourceFunction); + } + } + return resourceFunctions; + } - return serviceObjTypeDef.typeDescriptor().subtypeOf(serviceContractTypeDef.typeDescriptor()); + public static Document getDocument(SyntaxNodeAnalysisContext context) { + return context.currentPackage().module(context.moduleId()).document(context.documentId()); } } diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpAnnotationAnalyzer.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpAnnotationAnalyzer.java new file mode 100644 index 0000000000..6b12328f52 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpAnnotationAnalyzer.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.ListConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.projects.Document; +import io.ballerina.projects.plugins.AnalysisTask; +import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.scan.Reporter; +import io.ballerina.stdlib.http.compiler.HttpCompilerPluginUtil; + +import java.util.Optional; +import java.util.regex.Pattern; + +import static io.ballerina.stdlib.http.compiler.staticcodeanalyzer.HttpRule.AVOID_PERMISSIVE_CORS; + +class HttpAnnotationAnalyzer implements AnalysisTask { + private final Reporter reporter; + private static final String CORS_FIELD_NAME = "cors"; + private static final String ALLOW_ORIGINS_FIELD_NAME = "allowOrigins"; + public static final Pattern WILDCARD_ORIGIN = Pattern.compile("\"(\s*)\\*(\s*)\""); + + public HttpAnnotationAnalyzer(Reporter reporter) { + this.reporter = reporter; + } + + @Override + public void perform(SyntaxNodeAnalysisContext context) { + AnnotationNode annotationNode = HttpCompilerPluginUtil.getAnnotationNode(context); + if (annotationNode == null) { + return; + } + Optional annotationValue = annotationNode.annotValue(); + if (annotationValue.isEmpty()) { + return; + } + Document document = HttpCompilerPluginUtil.getDocument(context); + validateAnnotationValue(annotationValue.get(), document); + } + + private void validateAnnotationValue(MappingConstructorExpressionNode annotationValueMap, Document document) { + Optional corsField = findSpecificField(annotationValueMap, CORS_FIELD_NAME); + if (corsField.isEmpty() || corsField.get().valueExpr().isEmpty()) { + return; + } + ExpressionNode corsVal = corsField.get().valueExpr().get(); + if (corsVal.kind() != SyntaxKind.MAPPING_CONSTRUCTOR) { + return; + } + MappingConstructorExpressionNode corsMap = (MappingConstructorExpressionNode) corsVal; + Optional allowOrigins = findSpecificField(corsMap, ALLOW_ORIGINS_FIELD_NAME); + if (allowOrigins.isEmpty() || allowOrigins.get().valueExpr().isEmpty()) { + return; + } + ExpressionNode allowOriginsValue = allowOrigins.get().valueExpr().get(); + if (allowOriginsValue.kind() != SyntaxKind.LIST_CONSTRUCTOR) { + return; + } + checkForPermissiveCors((ListConstructorExpressionNode) allowOriginsValue, document); + } + + private Optional findSpecificField(MappingConstructorExpressionNode mapNode, String fieldName) { + return mapNode.fields().stream() + .filter(field -> field.kind() == SyntaxKind.SPECIFIC_FIELD).map(field -> (SpecificFieldNode) field) + .filter(field -> fieldName.equals(field.fieldName().toSourceCode().trim())).findFirst(); + } + + private void checkForPermissiveCors(ListConstructorExpressionNode allowedOrigins, Document document) { + for (Node exp : allowedOrigins.expressions()) { + if (WILDCARD_ORIGIN.matcher(exp.toSourceCode().trim()).find()) { + this.reporter.reportIssue(document, exp.location(), AVOID_PERMISSIVE_CORS.getId()); + } + } + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceClass.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceClass.java new file mode 100644 index 0000000000..cf3e702382 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceClass.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.compiler.syntax.tree.ClassDefinitionNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; + +class HttpHttpServiceClass implements HttpService { + private final ClassDefinitionNode classDefinitionNode; + + public HttpHttpServiceClass(ClassDefinitionNode classDefinitionNode) { + this.classDefinitionNode = classDefinitionNode; + } + + @Override + public NodeList members() { + return this.classDefinitionNode.members(); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceDeclaration.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceDeclaration.java new file mode 100644 index 0000000000..e12a6590fb --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceDeclaration.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; + +class HttpHttpServiceDeclaration implements HttpService { + private final ServiceDeclarationNode serviceDeclarationNode; + + public HttpHttpServiceDeclaration(ServiceDeclarationNode serviceDeclarationNode) { + this.serviceDeclarationNode = serviceDeclarationNode; + } + + @Override + public NodeList members() { + return this.serviceDeclarationNode.members(); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceObjectType.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceObjectType.java new file mode 100644 index 0000000000..5df6a9b0b5 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpHttpServiceObjectType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ObjectTypeDescriptorNode; + +class HttpHttpServiceObjectType implements HttpService { + private final ObjectTypeDescriptorNode objectTypeDescriptorNode; + + public HttpHttpServiceObjectType(ObjectTypeDescriptorNode objectTypeDescriptorNode) { + this.objectTypeDescriptorNode = objectTypeDescriptorNode; + } + + @Override + public NodeList members() { + return this.objectTypeDescriptorNode.members(); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpRule.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpRule.java new file mode 100644 index 0000000000..2bba0a8cc4 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpRule.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.scan.Rule; + +import static io.ballerina.scan.RuleKind.VULNERABILITY; +import static io.ballerina.stdlib.http.compiler.staticcodeanalyzer.RuleFactory.createRule; + +/** + * Represents static code rules specific to the Ballerina Http package. + */ +public enum HttpRule { + AVOID_DEFAULT_RESOURCE_ACCESSOR(createRule(1, "Avoid allowing default resource accessor", VULNERABILITY)), + AVOID_PERMISSIVE_CORS(createRule(2, "Avoid permissive Cross-Origin Resource Sharing", VULNERABILITY)); + + private final Rule rule; + + HttpRule(Rule rule) { + this.rule = rule; + } + + public int getId() { + return this.rule.numericId(); + } + + public Rule getRule() { + return this.rule; + } + + @Override + public String toString() { + return "{\"id\":" + this.getId() + ", \"kind\":\"" + this.rule.kind() + "\"," + + " \"description\" : \"" + this.rule.description() + "\"}"; + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpService.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpService.java new file mode 100644 index 0000000000..5696cabe4f --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpService.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; + +public interface HttpService { + NodeList members(); +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpServiceAnalyzer.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpServiceAnalyzer.java new file mode 100644 index 0000000000..f97e9a018e --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpServiceAnalyzer.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.compiler.syntax.tree.ClassDefinitionNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ObjectTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.projects.Document; +import io.ballerina.projects.plugins.AnalysisTask; +import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.scan.Reporter; +import io.ballerina.stdlib.http.compiler.HttpCompilerPluginUtil; +import io.ballerina.tools.diagnostics.Location; + +import static io.ballerina.stdlib.http.compiler.HttpCompilerPluginUtil.isHttpServiceType; +import static io.ballerina.stdlib.http.compiler.staticcodeanalyzer.HttpRule.AVOID_DEFAULT_RESOURCE_ACCESSOR; + +class HttpServiceAnalyzer implements AnalysisTask { + private final Reporter reporter; + + public HttpServiceAnalyzer(Reporter reporter) { + this.reporter = reporter; + } + + @Override + public void perform(SyntaxNodeAnalysisContext context) { + HttpService service = getService(context); + if (service == null) { + return; + } + Document document = HttpCompilerPluginUtil.getDocument(context); + validateServiceMembers(service.members(), document); + } + + private HttpService getService(SyntaxNodeAnalysisContext context) { + return switch (context.node().kind()) { + case SERVICE_DECLARATION -> { + ServiceDeclarationNode serviceDeclarationNode = HttpCompilerPluginUtil + .getServiceDeclarationNode(context); + yield serviceDeclarationNode == null ? null : new HttpHttpServiceDeclaration(serviceDeclarationNode); + } + case OBJECT_TYPE_DESC -> isHttpServiceType(context.semanticModel(), context.node()) ? + new HttpHttpServiceObjectType((ObjectTypeDescriptorNode) context.node()) : null; + case CLASS_DEFINITION -> { + ClassDefinitionNode serviceClassDefinitionNode = HttpCompilerPluginUtil + .getServiceClassDefinitionNode(context); + yield serviceClassDefinitionNode == null ? null : new HttpHttpServiceClass(serviceClassDefinitionNode); + } + default -> null; + }; + } + + private void validateServiceMembers(NodeList members, Document document) { + // TODO: fix location, currently getting always -1 than expected + HttpCompilerPluginUtil.getResourceMethodWithDefaultAccessor(members).forEach(definition -> { + Location accessorLocation = definition.functionName().location(); + this.reporter.reportIssue(document, accessorLocation, AVOID_DEFAULT_RESOURCE_ACCESSOR.getId()); + }); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpStaticCodeAnalyzer.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpStaticCodeAnalyzer.java new file mode 100644 index 0000000000..e41e6bdbca --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/HttpStaticCodeAnalyzer.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.projects.plugins.CodeAnalysisContext; +import io.ballerina.projects.plugins.CodeAnalyzer; +import io.ballerina.scan.Reporter; + +import java.util.List; + +import static io.ballerina.compiler.syntax.tree.SyntaxKind.ANNOTATION; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLASS_DEFINITION; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.OBJECT_TYPE_DESC; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.SERVICE_DECLARATION; + +/** + * The static code analyzer implementation for Ballerina Http package. + */ +public class HttpStaticCodeAnalyzer extends CodeAnalyzer { + private final Reporter reporter; + + public HttpStaticCodeAnalyzer(Reporter reporter) { + this.reporter = reporter; + } + + @Override + public void init(CodeAnalysisContext analysisContext) { + analysisContext.addSyntaxNodeAnalysisTask(new HttpServiceAnalyzer(reporter), + List.of(SERVICE_DECLARATION, OBJECT_TYPE_DESC, CLASS_DEFINITION)); + analysisContext.addSyntaxNodeAnalysisTask(new HttpAnnotationAnalyzer(reporter), ANNOTATION); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/RuleFactory.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/RuleFactory.java new file mode 100644 index 0000000000..81becd5e16 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/RuleFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.scan.Rule; +import io.ballerina.scan.RuleKind; + +/** + * {@code RuleFactory} contains the logic to create a {@link Rule}. + */ +public class RuleFactory { + public static Rule createRule(int id, String description, RuleKind kind) { + return new RuleImpl(id, description, kind); + } +} diff --git a/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/RuleImpl.java b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/RuleImpl.java new file mode 100644 index 0000000000..c03f2156c6 --- /dev/null +++ b/compiler-plugin/src/main/java/io/ballerina/stdlib/http/compiler/staticcodeanalyzer/RuleImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * 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.compiler.staticcodeanalyzer; + +import io.ballerina.scan.Rule; +import io.ballerina.scan.RuleKind; + +class RuleImpl implements Rule { + private final int id; + private final String description; + private final RuleKind kind; + + RuleImpl(int id, String description, RuleKind kind) { + this.id = id; + this.description = description; + this.kind = kind; + } + + @Override + public String id() { + return Integer.toString(this.id); + } + + @Override + public int numericId() { + return this.id; + } + + @Override + public String description() { + return this.description; + } + + @Override + public RuleKind kind() { + return this.kind; + } +} diff --git a/compiler-plugin/src/main/java/module-info.java b/compiler-plugin/src/main/java/module-info.java index 66d065826f..29131747e7 100644 --- a/compiler-plugin/src/main/java/module-info.java +++ b/compiler-plugin/src/main/java/module-info.java @@ -23,4 +23,5 @@ requires io.swagger.v3.core; requires io.swagger.v3.oas.models; requires io.ballerina.openapi.service; + requires io.ballerina.scan; } diff --git a/compiler-plugin/src/main/resources/rules.json b/compiler-plugin/src/main/resources/rules.json new file mode 100644 index 0000000000..2774fb7821 --- /dev/null +++ b/compiler-plugin/src/main/resources/rules.json @@ -0,0 +1,12 @@ +[ + { + "id": 1, + "kind": "VULNERABILITY", + "description": "Avoid allowing default resource accessor" + }, + { + "id": 2, + "kind": "VULNERABILITY", + "description": "Avoid permissive Cross-Origin Resource Sharing" + } +] diff --git a/gradle.properties b/gradle.properties index fe5035164c..b2ecb15a6d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,10 @@ org.gradle.caching=true group=io.ballerina.stdlib version=2.13.0-SNAPSHOT -ballerinaLangVersion=2201.10.0-20241025-103700-5c9e6a27 +ballerinaLangVersion=2201.11.0-20241112-214900-6b80ab87 ballerinaTomlParserVersion=1.2.2 commonsLang3Version=3.12.0 -nettyVersion=4.1.108.Final +nettyVersion=4.1.115.Final nettyTcnativeVersion=2.0.65.Final bouncycastleVersion=1.78 slf4jVersion=1.7.30 @@ -31,23 +31,27 @@ githubJohnrengelmanShadowVersion=8.1.1 underCouchDownloadVersion=5.4.0 researchgateReleaseVersion=2.8.0 -stdlibIoVersion=1.6.2-20241015-173200-3e70033 -stdlibTimeVersion=2.5.1-20240930-120200-e59222b -stdlibUrlVersion=2.4.1-20240930-120200-b7fb9e1 +stdlibIoVersion=1.6.2-20241112-233100-995cf5f +stdlibTimeVersion=2.6.0-20241113-073800-201b904 +stdlibUrlVersion=2.4.1-20241113-073900-335ff51 -stdlibConstraintVersion=1.5.1-20240930-123400-5ecd396 -stdlibOsVersion=1.8.1-20241001-120600-dd1626e -stdlibTaskVersion=2.5.1-20241002-145700-5bdb843 -stdlibLogVersion=2.10.1-20240930-154200-5ab2aa4 -stdlibCryptoVersion=2.7.3-20240930-132000-5ecc9ab +stdlibConstraintVersion=1.6.0-20241113-090900-d276ad5 +stdlibOsVersion=1.8.1-20241113-122000-cca973b +stdlibTaskVersion=2.5.1-20241113-123500-f905281 +stdlibLogVersion=2.10.1-20241113-120000-4577868 +stdlibCryptoVersion=2.7.3-20241113-081400-d015a39 -stdlibFileVersion=1.10.1-20241007-160900-03f7b64 -stdlibMimeVersion=2.10.1-20241009-141200-8b6c9f0 -stdlibCacheVersion=3.8.1-20241007-154900-63f4403 +stdlibFileVersion=1.10.1-20241113-151700-e1a2e38 +stdlibMimeVersion=2.10.2-20241113-154200-d953747 +stdlibCacheVersion=3.8.1-20241113-125700-b75a1bf -stdlibAuthVersion=2.12.1-20241010-130800-733dbef -stdlibJwtVersion=2.13.1-20241010-123600-5ea6a94 -stdlibOAuth2Version=2.12.1-20241029-084800-d7ba9e5 +stdlibAuthVersion=2.12.1-20241113-162300-ded40eb +stdlibDataJsonDataVersion=0.3.0-20241114-143900-285d739 +stdlibJwtVersion=2.13.1-20241113-162400-b59ccfa +stdlibOAuth2Version=2.12.1-20241113-162400-4c6ddfe + +balScanVersion=0.5.0 + +observeVersion=1.4.0-20241113-092000-b83ae74 +observeInternalVersion=1.3.1-20241113-101700-265054d -observeVersion=1.3.1-20241007-161000-645452d -observeInternalVersion=1.3.1-20241015-172900-cdc3cb3 diff --git a/load-tests/accesslog_enabled/results/summary.csv b/load-tests/accesslog_enabled/results/summary.csv index fd4df0d5d6..d334aec0b1 100644 --- a/load-tests/accesslog_enabled/results/summary.csv +++ b/load-tests/accesslog_enabled/results/summary.csv @@ -55,3 +55,10 @@ HTTP Request,8805084,21,17,44,58,89,0,532,0.00%,2530.3,607.8,18.43,1728929611,50 HTTP Request,9066483,21,17,43,56,87,0,649,0.00%,2605.4,625.9,17.84,1729016017,50,60 HTTP Request,8984950,21,17,43,57,87,0,631,0.00%,2581.9,620.3,18.05,1729102416,50,60 HTTP Request,9050137,21,17,43,56,87,0,585,0.00%,2600.6,624.8,17.76,1729188824,50,60 +HTTP Request,8940035,21,17,44,57,87,0,519,0.00%,2569.0,617.2,18.24,1730916839,50,60 +HTTP Request,7864208,24,19,50,66,99,0,551,0.00%,2259.9,542.9,20.50,1731003227,50,60 +HTTP Request,8472742,22,18,46,60,92,0,600,0.00%,2434.7,584.9,19.12,1731089620,50,60 +HTTP Request,8858344,21,17,44,58,89,0,667,0.00%,2545.6,611.5,18.35,1731175959,50,60 +HTTP Request,8146976,23,18,48,63,97,0,633,0.00%,2341.1,562.4,19.99,1731262456,50,60 +HTTP Request,8351727,23,18,47,62,94,0,482,0.00%,2400.0,576.5,19.49,1731348888,50,60 +HTTP Request,9176406,21,17,42,56,86,0,561,0.00%,2636.9,633.5,17.68,1731435295,50,60 diff --git a/load-tests/h1_h1_passthrough/results/summary.csv b/load-tests/h1_h1_passthrough/results/summary.csv index 6dbbf02ac3..3d3749ff03 100644 --- a/load-tests/h1_h1_passthrough/results/summary.csv +++ b/load-tests/h1_h1_passthrough/results/summary.csv @@ -589,3 +589,10 @@ HTTP Request,10834246,17,12,36,49,85,0,702,0.00%,3113.3,747.9,16.87,1728933488,5 HTTP Request,11107994,17,12,36,48,84,0,570,0.00%,3192.0,766.8,16.42,1729019918,50,60 HTTP Request,10868819,17,12,36,49,85,0,649,0.00%,3123.3,750.3,16.77,1729106333,50,60 HTTP Request,10949926,17,12,36,49,84,0,692,0.00%,3146.6,755.9,16.52,1729192715,50,60 +HTTP Request,10738090,17,12,37,50,86,0,640,0.00%,3085.7,741.3,17.09,1730920736,50,60 +HTTP Request,9533860,20,14,42,57,96,0,603,0.00%,2739.7,658.2,19.03,1731007147,50,60 +HTTP Request,9977622,18,13,40,54,92,0,661,0.00%,2867.2,688.8,18.22,1731093501,50,60 +HTTP Request,11324912,16,12,35,47,83,0,645,0.00%,3254.3,781.8,16.21,1731179847,50,60 +HTTP Request,10177832,18,13,39,53,90,0,722,0.00%,2924.7,702.6,18.02,1731266356,50,60 +HTTP Request,10072128,18,13,39,53,91,0,621,0.00%,2894.4,695.3,18.07,1731352775,50,60 +HTTP Request,11048123,17,12,36,49,85,0,596,0.00%,3174.8,762.7,16.68,1731439189,50,60 diff --git a/load-tests/h1_transformation/results/summary.csv b/load-tests/h1_transformation/results/summary.csv index c3d995611e..ffb3c2d0da 100644 --- a/load-tests/h1_transformation/results/summary.csv +++ b/load-tests/h1_transformation/results/summary.csv @@ -595,3 +595,10 @@ HTTP Request,8800966,23,21,40,45,61,1,366,0.00%,2529.1,691.5,12.39,1728937423,50 HTTP Request,8883008,22,21,39,45,60,1,324,0.00%,2552.6,698.0,12.15,1729023823,50,60 HTTP Request,8736152,23,21,40,46,61,1,312,0.00%,2510.4,686.4,12.34,1729110310,50,60 HTTP Request,8916171,22,21,39,45,59,1,273,0.00%,2562.2,700.6,12.04,1729196733,50,60 +HTTP Request,8880298,22,21,40,45,61,1,353,0.00%,2551.8,697.8,12.47,1730924622,50,60 +HTTP Request,7583898,26,25,46,53,71,1,430,0.00%,2179.4,595.9,14.66,1731011020,50,60 +HTTP Request,8295038,24,22,42,49,65,1,301,0.00%,2383.7,651.8,13.30,1731097424,50,60 +HTTP Request,8781111,23,21,40,46,62,1,349,0.00%,2523.3,690.0,12.60,1731183745,50,60 +HTTP Request,8572569,23,21,41,48,65,1,405,0.00%,2463.4,673.6,13.20,1731270240,50,60 +HTTP Request,8346461,24,22,42,48,64,1,358,0.00%,2398.5,655.8,13.31,1731356643,50,60 +HTTP Request,9073339,22,20,38,44,59,1,315,0.00%,2607.3,712.9,12.04,1731443095,50,60 diff --git a/load-tests/h1c_h1c_passthrough/results/summary.csv b/load-tests/h1c_h1c_passthrough/results/summary.csv index 5b31db0ad5..60a22dd86e 100644 --- a/load-tests/h1c_h1c_passthrough/results/summary.csv +++ b/load-tests/h1c_h1c_passthrough/results/summary.csv @@ -595,3 +595,10 @@ HTTP Request,12534798,15,12,30,37,57,0,323,0.00%,3602.0,640.2,11.61,1728941304,5 HTTP Request,12790104,15,12,30,36,56,0,328,0.00%,3675.4,653.2,11.33,1729027735,50,60 HTTP Request,12978005,14,12,29,36,56,0,254,0.00%,3729.4,662.8,11.32,1729114220,50,60 HTTP Request,12740832,15,12,30,36,55,0,308,0.00%,3661.2,650.7,11.27,1729200655,50,60 +HTTP Request,12616681,15,12,30,37,57,0,455,0.00%,3625.6,644.4,11.59,1730928549,50,60 +HTTP Request,10997396,17,14,35,42,65,0,593,0.00%,3160.3,561.7,13.49,1731014946,50,60 +HTTP Request,11452615,16,14,33,40,61,0,287,0.00%,3291.0,584.9,12.59,1731101304,50,60 +HTTP Request,12915342,14,12,29,36,57,0,307,0.00%,3711.4,659.6,11.43,1731187621,50,60 +HTTP Request,11836042,16,13,32,39,60,0,606,0.00%,3401.2,604.5,12.38,1731274119,50,60 +HTTP Request,11772592,16,13,32,39,60,0,407,0.00%,3383.0,601.3,12.29,1731360546,50,60 +HTTP Request,12811430,15,12,30,36,56,0,334,0.00%,3681.5,654.3,11.38,1731446970,50,60 diff --git a/load-tests/h1c_transformation/results/summary.csv b/load-tests/h1c_transformation/results/summary.csv index bd87f33e53..50d1a451f2 100644 --- a/load-tests/h1c_transformation/results/summary.csv +++ b/load-tests/h1c_transformation/results/summary.csv @@ -590,3 +590,10 @@ HTTP Request,9012796,22,20,35,47,69,1,237,0.00%,2589.9,546.3,12.09,1728945172,50 HTTP Request,8972214,22,20,35,49,71,1,309,0.00%,2578.3,543.8,12.43,1729031607,50,60 HTTP Request,9360625,21,19,34,48,67,1,166,0.00%,2689.9,567.4,11.90,1729118103,50,60 HTTP Request,8868640,23,20,36,48,72,1,191,0.00%,2548.5,537.6,12.38,1729204546,50,60 +HTTP Request,8878817,23,20,36,47,71,1,264,0.00%,2551.5,538.2,12.33,1730932445,50,60 +HTTP Request,7558429,27,24,42,56,83,1,245,0.00%,2172.0,458.1,14.54,1731018813,50,60 +HTTP Request,8419459,24,21,38,50,74,1,240,0.00%,2419.4,510.3,12.80,1731105224,50,60 +HTTP Request,9120676,22,19,35,47,72,1,246,0.00%,2620.9,552.8,12.43,1731191515,50,60 +HTTP Request,8304776,24,21,39,53,76,1,244,0.00%,2386.5,503.4,13.57,1731277974,50,60 +HTTP Request,8105933,25,22,39,53,80,1,243,0.00%,2329.4,491.3,13.90,1731364430,50,60 +HTTP Request,9154945,22,19,35,47,72,1,182,0.00%,2630.8,554.9,12.42,1731450902,50,60 diff --git a/load-tests/h2_h1c_passthrough/results/summary.csv b/load-tests/h2_h1c_passthrough/results/summary.csv index 9f1afe6683..099d188647 100644 --- a/load-tests/h2_h1c_passthrough/results/summary.csv +++ b/load-tests/h2_h1c_passthrough/results/summary.csv @@ -545,3 +545,10 @@ H2-H1C Passthrough,5859,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1728946035,0,1 H2-H1C Passthrough,5857,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1729032456,0,1 H2-H1C Passthrough,5868,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1729118951,0,1 H2-H1C Passthrough,5857,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1729205414,0,1 +H2-H1C Passthrough,5858,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1730933266,0,1 +H2-H1C Passthrough,5853,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1731019670,0,1 +H2-H1C Passthrough,5857,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1731106086,0,1 +H2-H1C Passthrough,5870,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1731192344,0,1 +H2-H1C Passthrough,5854,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1731278819,0,1 +H2-H1C Passthrough,5844,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1731365277,0,1 +H2-H1C Passthrough,5865,0.0,0,0,0,0,0,0,1.0,0.0,0,0,1731451753,0,1 diff --git a/load-tests/interceptors_passthrough/results/summary.csv b/load-tests/interceptors_passthrough/results/summary.csv index 17a6faf054..b6c5f3ba3e 100644 --- a/load-tests/interceptors_passthrough/results/summary.csv +++ b/load-tests/interceptors_passthrough/results/summary.csv @@ -297,3 +297,10 @@ Test Request,227856,919,31,132,263,30027,1,58683,4.17%,65.0,10.0,4838.28,1728949 Test Request,220602,949,31,124,215,29186,1,30036,3.66%,63.1,7.5,4889.81,1729036341,50,60 Test Request,223390,938,29,121,224,30023,1,47321,3.66%,63.7,9.2,4840.76,1729122800,50,60 Test Request,228996,914,30,126,233,29429,1,30034,3.80%,65.5,8.2,4768.81,1729209280,50,60 +Test Request,230471,909,34,128,221,30024,1,46557,3.48%,65.7,9.5,4808.24,1730937126,50,60 +Test Request,247790,845,40,134,218,30028,1,47904,3.29%,70.7,11.4,4707.78,1731023529,50,60 +Test Request,239456,874,36,130,222,30025,1,51253,3.46%,68.4,10.0,4734.91,1731109978,50,60 +Test Request,231261,905,34,133,236,29384,1,30036,3.76%,65.9,8.3,4639.88,1731196247,50,60 +Test Request,225358,929,34,125,224,30028,1,30035,3.77%,64.3,10.2,4874.13,1731282700,50,60 +Test Request,237390,882,36,134,223,30028,1,30035,3.37%,67.8,10.5,4743.81,1731369131,50,60 +Test Request,224290,933,33,130,237,29003,1,30034,3.82%,64.1,7.3,4797.19,1731455640,50,60 diff --git a/load-tests/observability_enabled/results/summary.csv b/load-tests/observability_enabled/results/summary.csv index 3255ff1d7d..26ec6767a3 100644 --- a/load-tests/observability_enabled/results/summary.csv +++ b/load-tests/observability_enabled/results/summary.csv @@ -577,3 +577,10 @@ HTTP Request,16303331,12,5,27,45,111,0,1151,0.00%,4684.9,700.0,22.91,1728953803, HTTP Request,16457558,12,5,27,47,118,0,1083,0.00%,4729.2,706.6,24.51,1729040228,50,60 HTTP Request,16430216,12,5,27,46,116,0,1002,0.00%,4721.4,705.4,23.93,1729126697,50,60 HTTP Request,16357905,12,5,27,46,113,0,1158,0.00%,4700.6,702.3,23.52,1729213179,50,60 +HTTP Request,10199693,20,2,10,21,318,0,30053,0.00%,2931.0,438.0,274.58,1730941039,50,60 +HTTP Request,9262146,22,2,12,26,303,0,30100,0.00%,2661.6,397.8,333.23,1731027432,50,60 +HTTP Request,9719305,21,2,11,23,314,0,30061,0.00%,2793.0,417.3,289.18,1731113856,50,60 +HTTP Request,10504964,19,2,10,21,274,0,30072,0.00%,3018.7,451.1,273.57,1731200154,50,60 +HTTP Request,9334089,22,2,11,24,358,0,30064,0.00%,2682.3,400.8,306.12,1731286591,50,60 +HTTP Request,9753497,21,2,10,21,284,0,30076,0.00%,2802.8,419.0,339.08,1731373000,50,60 +HTTP Request,10384686,19,2,9,20,296,0,30061,0.00%,2984.2,445.9,289.36,1731459535,50,60 diff --git a/load-tests/snowpeak_passthrough/results/summary.csv b/load-tests/snowpeak_passthrough/results/summary.csv index 686fb7d5cc..5d1281d95b 100644 --- a/load-tests/snowpeak_passthrough/results/summary.csv +++ b/load-tests/snowpeak_passthrough/results/summary.csv @@ -429,3 +429,10 @@ Retrieve Available Locations,7264596,28,17,65,69,80,0,193,0.00%,2087.6,201.8,22. Retrieve Available Locations,6952009,29,17,67,71,84,0,504,0.00%,1997.7,193.1,24.02,1729044125,50,60 Retrieve Available Locations,7231847,28,16,66,70,83,0,391,0.00%,2078.1,200.9,23.46,1729130548,50,60 Retrieve Available Locations,6702677,30,18,68,73,86,0,677,0.00%,1926.1,186.2,24.39,1729217054,50,60 +Retrieve Available Locations,5825981,35,21,72,76,92,0,593,0.00%,1674.1,161.8,25.80,1730944907,50,60 +Retrieve Available Locations,5194173,39,25,75,81,101,1,967,0.00%,1492.6,144.3,27.03,1731031308,50,60 +Retrieve Available Locations,5533786,37,23,73,78,96,1,506,0.00%,1590.2,153.7,26.39,1731117716,50,60 +Retrieve Available Locations,6100462,33,20,71,75,91,1,724,0.00%,1752.9,169.5,25.58,1731204041,50,60 +Retrieve Available Locations,5711418,36,22,72,77,93,1,675,0.00%,1641.1,158.7,25.89,1731290454,50,60 +Retrieve Available Locations,5585826,36,23,73,78,94,0,653,0.00%,1605.2,155.2,26.20,1731376857,50,60 +Retrieve Available Locations,6118763,33,20,71,75,88,1,398,0.00%,1758.2,170.0,25.34,1731463444,50,60 diff --git a/native/build.gradle b/native/build.gradle index 34b1f3e2a5..0899f440cb 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -42,8 +42,7 @@ dependencies { implementation group: 'io.ballerina.stdlib', name: 'io-native', version: "${stdlibIoVersion}" implementation group: 'io.ballerina.stdlib', name: 'mime-native', version: "${stdlibMimeVersion}" implementation group: 'io.ballerina.stdlib', name: 'constraint-native', version: "${stdlibConstraintVersion}" - - implementation group: 'org.slf4j', name: 'slf4j-api', version: "${slf4jVersion}" + implementation group: 'io.ballerina.lib', name: 'data.jsondata-native', version: "${stdlibDataJsonDataVersion}" implementation group: 'org.slf4j', name: 'slf4j-jdk14', version: "${slf4jVersion}" implementation group: 'org.apache.commons', name: 'commons-lang3', version: "${commonsLang3Version}" implementation group: 'com.google.code.gson', name: 'gson', version: "${gsonVersion}" diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/BallerinaHTTPConnectorListener.java b/native/src/main/java/io/ballerina/stdlib/http/api/BallerinaHTTPConnectorListener.java index 23d342c7a9..260b55ac3f 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/BallerinaHTTPConnectorListener.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/BallerinaHTTPConnectorListener.java @@ -18,6 +18,7 @@ package io.ballerina.stdlib.http.api; import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.concurrent.StrandMetadata; import io.ballerina.runtime.api.constants.RuntimeConstants; import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.utils.TypeUtils; @@ -28,7 +29,6 @@ import io.ballerina.runtime.observability.ObservabilityConstants; import io.ballerina.runtime.observability.ObserveUtils; import io.ballerina.runtime.observability.ObserverContext; -import io.ballerina.stdlib.http.api.nativeimpl.ModuleUtils; import io.ballerina.stdlib.http.transport.contract.HttpConnectorListener; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import org.slf4j.Logger; @@ -189,15 +189,10 @@ protected void extractPropertiesAndStartResourceExecution(HttpCarbonMessage inbo ObjectType serviceType = (ObjectType) TypeUtils.getReferredType(TypeUtils.getType(service)); Thread.startVirtualThread(() -> { Object result; + boolean isIsolated = serviceType.isIsolated() && serviceType.isIsolated(resourceName); + StrandMetadata metaData = new StrandMetadata(isIsolated, properties); try { - if (serviceType.isIsolated() && serviceType.isIsolated(resourceName)) { - result = runtime.startIsolatedWorker(service, resourceName, null, - ModuleUtils.getOnMessageMetaData(), properties, signatureParams).get(); - - } else { - result = runtime.startNonIsolatedWorker(service, resourceName, null, - ModuleUtils.getOnMessageMetaData(), properties, signatureParams).get(); - } + result = runtime.callMethod(service, resourceName, metaData, signatureParams); callback.handleResult(result); } catch (BError error) { callback.handlePanic(error); @@ -254,15 +249,10 @@ protected void extractPropertiesAndStartInterceptorResourceExecution(HttpCarbonM ObjectType serviceType = (ObjectType) TypeUtils.getReferredType(TypeUtils.getType(service)); Thread.startVirtualThread(() -> { - Object result; + boolean isIsolated = serviceType.isIsolated() && serviceType.isIsolated(resourceName); + StrandMetadata metaData = new StrandMetadata(isIsolated, properties); try { - if (serviceType.isIsolated() && serviceType.isIsolated(resourceName)) { - result = runtime.startIsolatedWorker(service, resourceName, null, - ModuleUtils.getOnMessageMetaData(), properties, signatureParams).get(); - } else { - result = runtime.startNonIsolatedWorker(service, resourceName, null, - ModuleUtils.getOnMessageMetaData(), properties, signatureParams).get(); - } + Object result = runtime.callMethod(service, resourceName, metaData, signatureParams); callback.handleResult(result); } catch (BError error) { callback.handlePanic(error); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java index b15e38bff9..35eb40cc34 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java @@ -17,13 +17,13 @@ package io.ballerina.stdlib.http.api; import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.concurrent.StrandMetadata; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.observability.ObserveUtils; import io.ballerina.runtime.observability.ObserverContext; -import io.ballerina.stdlib.http.api.nativeimpl.ModuleUtils; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import java.util.Locale; @@ -117,8 +117,8 @@ private void returnErrorResponse(BError error) { public void invokeBalMethod(Object[] paramFeed, String methodName) { Thread.startVirtualThread(() -> { try { - runtime.startNonIsolatedWorker(caller, methodName, null, - ModuleUtils.getNotifySuccessMetaData(), null, paramFeed).get(); + StrandMetadata metaData = new StrandMetadata(false, null); + runtime.callMethod(caller, methodName, metaData, paramFeed); stopObserverContext(); } catch (BError error) { sendFailureResponse(error); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java index 03ec1f37ef..130e87af2c 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java @@ -497,6 +497,11 @@ public final class HttpConstants { public static final BString SOCKET_CONFIG_SOCKET_REUSE = StringUtils.fromString("socketReuse"); public static final BString SOCKET_CONFIG_KEEP_ALIVE = StringUtils.fromString("keepAlive"); + //Service parseAsType constants + public static final String ENABLE_CONSTRAINT_VALIDATION = "enableConstraintValidation"; + public static final String ALLOW_DATA_PROJECTION = "allowDataProjection"; + public static final String PARSER_AS_TYPE_OPTIONS = "Options"; + //Client Endpoint (CallerActions) public static final String CLIENT_ENDPOINT_SERVICE_URI = "url"; public static final String CLIENT_ENDPOINT_CONFIG = "config"; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpDispatcher.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpDispatcher.java index 1b288db1d2..f6543492eb 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpDispatcher.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpDispatcher.java @@ -465,8 +465,8 @@ private static Object invokeJwtDecode(Runtime runtime, String authHeader) { if (splitValues.length != 2) { return null; } - Object result = runtime.call(ValueCreator.createObjectValue(ModuleUtils.getHttpPackage(), - JWT_DECODER_CLASS_NAME), JWT_DECODE_METHOD_NAME, StringUtils.fromString(splitValues[1])); + Object result = runtime.callMethod(ValueCreator.createObjectValue(ModuleUtils.getHttpPackage(), + JWT_DECODER_CLASS_NAME), JWT_DECODE_METHOD_NAME, null, StringUtils.fromString(splitValues[1])); if (!(result instanceof Exception)) { jwtInformation[0] = result; } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java index 47408052b0..768df2b278 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java @@ -19,12 +19,12 @@ package io.ballerina.stdlib.http.api; import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.concurrent.StrandMetadata; import io.ballerina.runtime.api.types.ServiceType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; -import io.ballerina.stdlib.http.api.nativeimpl.ModuleUtils; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; import static io.ballerina.stdlib.http.api.HttpErrorType.INTERNAL_INTERCEPTOR_RETURN_ERROR; @@ -171,8 +171,8 @@ private void returnResponse(Object result) { public void invokeBalMethod(Object[] paramFeed, String methodName) { Thread.startVirtualThread(() -> { try { - runtime.startNonIsolatedWorker(caller, methodName, null, ModuleUtils.getNotifySuccessMetaData(), null - , paramFeed).get(); + StrandMetadata metaData = new StrandMetadata(false, null); + runtime.callMethod(caller, methodName, metaData, paramFeed); } catch (BError error) { cleanupRequestMessage(); HttpUtil.handleFailure(requestMessage, error); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpResource.java index f3d1c9c85d..a570520160 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpResource.java @@ -17,18 +17,18 @@ */ package io.ballerina.stdlib.http.api; -import io.ballerina.runtime.api.PredefinedTypes; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.FiniteType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.MethodType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.RemoteMethodType; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java index ba3b1c8b8d..921d31de83 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java @@ -20,12 +20,12 @@ import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.concurrent.StrandMetadata; import io.ballerina.runtime.api.types.ServiceType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; -import io.ballerina.stdlib.http.api.nativeimpl.ModuleUtils; import io.ballerina.stdlib.http.api.nativeimpl.connection.Respond; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; @@ -156,8 +156,8 @@ private void returnResponse(Object result) { public void invokeBalMethod(Object[] paramFeed, String methodName) { Thread.startVirtualThread(() -> { try { - this.getRuntime().startIsolatedWorker(caller, methodName, null, - ModuleUtils.getNotifySuccessMetaData(), null, paramFeed).get(); + StrandMetadata metaData = new StrandMetadata(true, null); + this.getRuntime().callMethod(caller, methodName, metaData, paramFeed); stopObserverContext(); dataContext.notifyOutboundResponseStatus(null); } catch (BError error) { diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpService.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpService.java index c3c3813620..7c52cf64f0 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpService.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpService.java @@ -17,14 +17,15 @@ */ package io.ballerina.stdlib.http.api; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.concurrent.StrandMetadata; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.ServiceType; import io.ballerina.runtime.api.utils.StringUtils; @@ -490,7 +491,8 @@ public static void populateInterceptorServicesRegistries(List { try { - String strandParentFunctionName = Objects.isNull(env.getStrandMetadata()) ? null : - env.getStrandMetadata().getParentFunctionName(); - if (Objects.nonNull(strandParentFunctionName) && strandParentFunctionName.equals("onMessage")) { - env.setStrandLocal(MAIN_STRAND, true); - } - return env.getRuntime().call(client, methodName, paramFeed); + return env.getRuntime().callMethod(client, methodName, new StrandMetadata(true, null), paramFeed); } catch (BError bError) { return HttpUtil.createHttpError("client method invocation failed: " + bError.getErrorMessage(), HttpErrorType.CLIENT_ERROR, bError); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternFormatter.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternFormatter.java index ff37fd8856..06bccab2d1 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternFormatter.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternFormatter.java @@ -18,9 +18,9 @@ package io.ballerina.stdlib.http.api.nativeimpl; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BString; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternRequest.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternRequest.java index 9f983561aa..3da6be6a16 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternRequest.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternRequest.java @@ -18,10 +18,10 @@ package io.ballerina.stdlib.http.api.nativeimpl; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.MapType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; 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 b326d2aa60..ff1d8184b2 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 @@ -18,8 +18,6 @@ package io.ballerina.stdlib.http.api.nativeimpl; import io.ballerina.runtime.api.Environment; -import io.ballerina.runtime.api.PredefinedTypes; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; @@ -28,9 +26,11 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.JsonUtils; import io.ballerina.runtime.api.utils.StringUtils; @@ -252,7 +252,7 @@ private static Object generateStatusCodeResponseType(BObject response, boolean r payloadType = statusCodeRecordType.getFields().get(STATUS_CODE_RESPONSE_BODY_FIELD).getFieldType(); } try { - return env.getRuntime().call(response, BUILD_STATUS_CODE_RESPONSE, + return env.getRuntime().callMethod(response, BUILD_STATUS_CODE_RESPONSE, null, Objects.isNull(payloadType) ? null : ValueCreator.createTypedescValue(payloadType), ValueCreator.createTypedescValue(statusCodeRecordType), requireValidation, status, headers, mediaType, @@ -507,7 +507,7 @@ private static Object validateConstraints(boolean requireValidation, Object conv private static Object getStatusCodeResponseBindingError(Environment env, BObject response, String reasonPhrase) { try { - return env.getRuntime().call(response, GET_STATUS_CODE_RESPONSE_BINDING_ERROR, + return env.getRuntime().callMethod(response, GET_STATUS_CODE_RESPONSE_BINDING_ERROR, null, StringUtils.fromString(reasonPhrase)); } catch (BError error) { return createHttpError(APPLICATION_RES_ERROR_CREATION_FAILED, STATUS_CODE_RESPONSE_BINDING_ERROR, error); @@ -521,7 +521,7 @@ private static Object getStatusCodeResponseDataBindingError(Environment env, BOb BError cause, boolean isDefaultStatusCodeResponse, String errorType) { try { - return env.getRuntime().call(response, GET_STATUS_CODE_RESPONSE_DATA_BINDING_ERROR, + return env.getRuntime().callMethod(response, GET_STATUS_CODE_RESPONSE_DATA_BINDING_ERROR, null, StringUtils.fromString(reasonPhrase), isDefaultStatusCodeResponse, StringUtils.fromString(errorType), cause); } catch (BError error) { diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternUtils.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternUtils.java index 79e9e23422..87ad79e356 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternUtils.java @@ -16,10 +16,10 @@ package io.ballerina.stdlib.http.api.nativeimpl; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ModuleUtils.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ModuleUtils.java index 59ff25b6d4..abb9af0ccb 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ModuleUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ModuleUtils.java @@ -20,12 +20,9 @@ import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.api.async.StrandMetadata; import io.ballerina.stdlib.http.api.HttpConstants; -import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_BUILTIN_PKG_PREFIX; import static io.ballerina.runtime.api.constants.RuntimeConstants.ORG_NAME_SEPARATOR; -import static io.ballerina.stdlib.http.api.HttpConstants.PROTOCOL_HTTP; /** * This class will hold module related utility functions. @@ -36,20 +33,14 @@ public class ModuleUtils { private static Module httpModule; private static Module httpStatusModule; - private static StrandMetadata onMessageMetaData; - private static StrandMetadata notifySuccessMetaData; private static String packageIdentifier; private ModuleUtils() {} public static void setModule(Environment env) { httpModule = env.getCurrentModule(); - onMessageMetaData = new StrandMetadata(BALLERINA_BUILTIN_PKG_PREFIX, PROTOCOL_HTTP, httpModule.getVersion(), - "onMessage"); - notifySuccessMetaData = new StrandMetadata(BALLERINA_BUILTIN_PKG_PREFIX, PROTOCOL_HTTP, httpModule.getVersion(), - "notifySuccess"); packageIdentifier = HttpConstants.PACKAGE + ORG_NAME_SEPARATOR + HttpConstants.PROTOCOL_HTTP + - HttpConstants.COLON + httpModule.getVersion(); + HttpConstants.COLON + httpModule.getMajorVersion(); } public static void setHttpStatusModule(Environment env) { @@ -74,24 +65,6 @@ public static Module getHttpStatusPackage() { return httpStatusModule; } - /** - * Gets the metadata of onMessage() method to invoke resource method. - * - * @return metadata of onMessage() method - */ - public static StrandMetadata getOnMessageMetaData() { - return onMessageMetaData; - } - - /** - * Gets the metadata of notifySuccess() method to invoke Caller.returnResponse() method. - * - * @return metadata of notifySuccess() method - */ - public static StrandMetadata getNotifySuccessMetaData() { - return notifySuccessMetaData; - } - /** * Gets ballerina http package identifier. * diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ParseHeader.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ParseHeader.java index 7443d5a53b..5efba561df 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ParseHeader.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ParseHeader.java @@ -17,10 +17,10 @@ */ package io.ballerina.stdlib.http.api.nativeimpl; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java index 155eaadf9c..f10acc82a5 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java @@ -20,6 +20,9 @@ import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.concurrent.StrandMetadata; +import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.observability.ObserveUtils; @@ -262,7 +265,10 @@ private static void startInterceptResponseMethod(HttpCarbonMessage inboundMessag String methodName = service.getServiceType().equals(HttpConstants.RESPONSE_ERROR_INTERCEPTOR) ? HttpConstants.INTERCEPT_RESPONSE_ERROR : HttpConstants.INTERCEPT_RESPONSE; try { - Object result = runtime.call(serviceObj, methodName, signatureParams); + ObjectType serviceType = (ObjectType) TypeUtils.getReferredType(TypeUtils.getType(serviceObj)); + Object result = runtime.callMethod(serviceObj, methodName, + new StrandMetadata(serviceType.isIsolated() && serviceType.isIsolated(methodName), null), + signatureParams); callback.handleResult(result); } catch (BError bError) { callback.handlePanic(bError); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/HeaderParam.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/HeaderParam.java index 9d4de89504..1a32b033a7 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/HeaderParam.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/HeaderParam.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.Map; -import static io.ballerina.runtime.api.TypeTags.RECORD_TYPE_TAG; +import static io.ballerina.runtime.api.types.TypeTags.RECORD_TYPE_TAG; import static io.ballerina.stdlib.http.api.HttpConstants.HEADER_PARAM; import static io.ballerina.stdlib.http.api.HttpErrorType.INTERNAL_HEADER_VALIDATION_LISTENER_ERROR; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamHandler.java index 213d80318d..d884f7b914 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamHandler.java @@ -18,10 +18,10 @@ package io.ballerina.stdlib.http.api.service.signature; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.MapType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.IdentifierUtils; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamUtils.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamUtils.java index 3fdd17b173..cd12a54da1 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/ParamUtils.java @@ -18,16 +18,16 @@ package io.ballerina.stdlib.http.api.service.signature; -import io.ballerina.runtime.api.PredefinedTypes; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.FiniteType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.JsonUtils; import io.ballerina.runtime.api.utils.StringUtils; @@ -42,19 +42,19 @@ import java.util.List; import java.util.Set; -import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; -import static io.ballerina.runtime.api.TypeTags.BOOLEAN_TAG; -import static io.ballerina.runtime.api.TypeTags.DECIMAL_TAG; -import static io.ballerina.runtime.api.TypeTags.FINITE_TYPE_TAG; -import static io.ballerina.runtime.api.TypeTags.FLOAT_TAG; -import static io.ballerina.runtime.api.TypeTags.INTERSECTION_TAG; -import static io.ballerina.runtime.api.TypeTags.INT_TAG; -import static io.ballerina.runtime.api.TypeTags.MAP_TAG; -import static io.ballerina.runtime.api.TypeTags.NULL_TAG; -import static io.ballerina.runtime.api.TypeTags.READONLY_TAG; -import static io.ballerina.runtime.api.TypeTags.RECORD_TYPE_TAG; -import static io.ballerina.runtime.api.TypeTags.STRING_TAG; -import static io.ballerina.runtime.api.TypeTags.UNION_TAG; +import static io.ballerina.runtime.api.types.TypeTags.ARRAY_TAG; +import static io.ballerina.runtime.api.types.TypeTags.BOOLEAN_TAG; +import static io.ballerina.runtime.api.types.TypeTags.DECIMAL_TAG; +import static io.ballerina.runtime.api.types.TypeTags.FINITE_TYPE_TAG; +import static io.ballerina.runtime.api.types.TypeTags.FLOAT_TAG; +import static io.ballerina.runtime.api.types.TypeTags.INTERSECTION_TAG; +import static io.ballerina.runtime.api.types.TypeTags.INT_TAG; +import static io.ballerina.runtime.api.types.TypeTags.MAP_TAG; +import static io.ballerina.runtime.api.types.TypeTags.NULL_TAG; +import static io.ballerina.runtime.api.types.TypeTags.READONLY_TAG; +import static io.ballerina.runtime.api.types.TypeTags.RECORD_TYPE_TAG; +import static io.ballerina.runtime.api.types.TypeTags.STRING_TAG; +import static io.ballerina.runtime.api.types.TypeTags.UNION_TAG; import static io.ballerina.stdlib.http.api.HttpConstants.PATH_PARAM; import static io.ballerina.stdlib.http.api.HttpConstants.QUERY_PARAM; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/PayloadParam.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/PayloadParam.java index 4ce31f8fcb..375f289a1d 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/PayloadParam.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/PayloadParam.java @@ -18,11 +18,11 @@ package io.ballerina.stdlib.http.api.service.signature; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; @@ -39,7 +39,6 @@ import java.util.ArrayList; import java.util.List; -import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; import static io.ballerina.stdlib.http.api.HttpErrorType.INTERNAL_PAYLOAD_BINDING_LISTENER_ERROR; import static io.ballerina.stdlib.http.api.HttpErrorType.INTERNAL_PAYLOAD_VALIDATION_LISTENER_ERROR; import static io.ballerina.stdlib.http.api.service.signature.builder.AbstractPayloadBuilder.getBuilder; @@ -146,7 +145,7 @@ private void populateFeedWithAlreadyBuiltPayload(Object[] paramFeed, BObject inR Type payloadType, Object dataSource) { try { switch (payloadType.getTag()) { - case ARRAY_TAG: + case TypeTags.ARRAY_TAG: int actualTypeTag = TypeUtils.getReferredType(((ArrayType) payloadType).getElementType()).getTag(); if (actualTypeTag == TypeTags.BYTE_TAG) { paramFeed[index] = validateConstraints(dataSource); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/AbstractPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/AbstractPayloadBuilder.java index 9140d229a8..222542c3a7 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/AbstractPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/AbstractPayloadBuilder.java @@ -18,10 +18,10 @@ package io.ballerina.stdlib.http.api.service.signature.builder; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.TypedescType; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.TypeUtils; @@ -32,13 +32,13 @@ import java.util.List; import java.util.Locale; -import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; -import static io.ballerina.runtime.api.TypeTags.BYTE_ARRAY_TAG; -import static io.ballerina.runtime.api.TypeTags.BYTE_TAG; -import static io.ballerina.runtime.api.TypeTags.MAP_TAG; -import static io.ballerina.runtime.api.TypeTags.NULL_TAG; -import static io.ballerina.runtime.api.TypeTags.STRING_TAG; -import static io.ballerina.runtime.api.TypeTags.XML_TAG; +import static io.ballerina.runtime.api.types.TypeTags.ARRAY_TAG; +import static io.ballerina.runtime.api.types.TypeTags.BYTE_ARRAY_TAG; +import static io.ballerina.runtime.api.types.TypeTags.BYTE_TAG; +import static io.ballerina.runtime.api.types.TypeTags.MAP_TAG; +import static io.ballerina.runtime.api.types.TypeTags.NULL_TAG; +import static io.ballerina.runtime.api.types.TypeTags.STRING_TAG; +import static io.ballerina.runtime.api.types.TypeTags.XML_TAG; /** * The abstract class to build and convert the payload based on the content-type header. If the content type is not diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/ArrayBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/ArrayBuilder.java index 431bc01a26..0c9e0927dd 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/ArrayBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/ArrayBuilder.java @@ -18,9 +18,9 @@ package io.ballerina.stdlib.http.api.service.signature.builder; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.values.BObject; /** diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/BinaryPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/BinaryPayloadBuilder.java index 2c5b19037b..43e70e8f20 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/BinaryPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/BinaryPayloadBuilder.java @@ -18,9 +18,9 @@ package io.ballerina.stdlib.http.api.service.signature.builder; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BObject; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/JsonPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/JsonPayloadBuilder.java index c66147d8c9..be220443ea 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/JsonPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/JsonPayloadBuilder.java @@ -18,8 +18,8 @@ package io.ballerina.stdlib.http.api.service.signature.builder; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.ValueUtils; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BRefValue; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/StringPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/StringPayloadBuilder.java index 90e963d6ac..11f78a3a6f 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/StringPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/StringPayloadBuilder.java @@ -18,10 +18,10 @@ package io.ballerina.stdlib.http.api.service.signature.builder; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/XmlPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/XmlPayloadBuilder.java index e7f024d754..ac5fbc7c74 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/XmlPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/builder/XmlPayloadBuilder.java @@ -18,8 +18,8 @@ package io.ballerina.stdlib.http.api.service.signature.builder; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BXml; import io.ballerina.stdlib.http.api.HttpErrorType; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/JsonToRecordConverter.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/JsonToRecordConverter.java index 457377226c..84971fdddb 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/JsonToRecordConverter.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/JsonToRecordConverter.java @@ -18,14 +18,25 @@ package io.ballerina.stdlib.http.api.service.signature.converter; +import io.ballerina.lib.data.jsondata.json.Native; +import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.utils.ValueUtils; import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BRefValue; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.stdlib.http.api.BallerinaConnectorException; import io.ballerina.stdlib.mime.util.EntityBodyHandler; +import java.util.HashMap; +import java.util.Map; + +import static io.ballerina.stdlib.http.api.HttpConstants.ALLOW_DATA_PROJECTION; +import static io.ballerina.stdlib.http.api.HttpConstants.ENABLE_CONSTRAINT_VALIDATION; +import static io.ballerina.stdlib.http.api.HttpConstants.PARSER_AS_TYPE_OPTIONS; + /** * The converter binds the JSON payload to a record. * @@ -60,7 +71,15 @@ private static Object getRecordEntity(BObject entity, Type entityBodyType) { */ private static Object getRecord(Type entityBodyType, Object bJson) { try { - return ValueUtils.convert(bJson, entityBodyType); + Map valueMap = new HashMap<>(); + Boolean bool = Boolean.FALSE; + valueMap.put(ENABLE_CONSTRAINT_VALIDATION, bool); + valueMap.put(ALLOW_DATA_PROJECTION, bool); + BMap mapValue = ValueCreator.createRecordValue( + io.ballerina.lib.data.ModuleUtils.getModule(), + PARSER_AS_TYPE_OPTIONS, valueMap); + BTypedesc typedescValue = ValueCreator.createTypedescValue(entityBodyType); + return Native.parseAsType(bJson, mapValue, typedescValue); } catch (NullPointerException ex) { throw new BallerinaConnectorException("cannot convert payload to record type: " + entityBodyType.getName()); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/StringToByteArrayConverter.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/StringToByteArrayConverter.java index b1169dd0d0..b0cb33fd9b 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/StringToByteArrayConverter.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/StringToByteArrayConverter.java @@ -18,9 +18,9 @@ package io.ballerina.stdlib.http.api.service.signature.converter; -import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.ArrayType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.http.api.HttpErrorType; import io.ballerina.stdlib.http.api.HttpUtil; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/UrlEncodedStringToMapConverter.java b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/UrlEncodedStringToMapConverter.java index 018009ebd4..800ff00248 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/UrlEncodedStringToMapConverter.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/service/signature/converter/UrlEncodedStringToMapConverter.java @@ -18,11 +18,11 @@ package io.ballerina.stdlib.http.api.service.signature.converter; -import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.MapType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BMap; @@ -36,7 +36,7 @@ import java.util.HashMap; import java.util.Map; -import static io.ballerina.runtime.api.TypeTags.STRING_TAG; +import static io.ballerina.runtime.api.types.TypeTags.STRING_TAG; /** * The converter binds the URL encoded string payload to a Map. diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/util/CacheUtils.java b/native/src/main/java/io/ballerina/stdlib/http/api/util/CacheUtils.java index 6127f0bf61..112a08eaef 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/util/CacheUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/util/CacheUtils.java @@ -18,7 +18,7 @@ package io.ballerina.stdlib.http.api.util; -import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; diff --git a/native/src/main/java/module-info.java b/native/src/main/java/module-info.java index 775a814f2d..774d28ee45 100644 --- a/native/src/main/java/module-info.java +++ b/native/src/main/java/module-info.java @@ -41,6 +41,7 @@ requires io.netty.handler; requires commons.pool; requires io.netty.handler.proxy; + requires io.ballerina.lib.data; exports io.ballerina.stdlib.http.api; exports io.ballerina.stdlib.http.transport.contract.websocket; exports io.ballerina.stdlib.http.transport.contract;