From 4b80b6addd0320cfe395130a5ae7cc84ab692939 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 23 May 2022 17:49:24 +0530 Subject: [PATCH 01/59] Bump update 1 rc version --- gradle.properties | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/gradle.properties b/gradle.properties index 96ee11e7..28469bd4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,25 +1,25 @@ org.gradle.caching=true org.gradle.jvmargs='-Dfile.encoding=UTF-8' group=org.ballerinax.azurefunctions -version=2.1.1-SNAPSHOT +version=2.1.0-SNAPSHOT systemProp.org.gradle.internal.publish.checksums.insecure=true -ballerinaLangVersion=2201.1.0 +ballerinaLangVersion=2201.1.0-rc1.13 ballerinaGradlePluginVersion=0.14.3 -stdlibIoVersion=1.2.2 -stdlibLogVersion=2.3.0 -stdlibHttpVersion=2.3.0 -stdlibAuthVersion=2.3.0 -stdlibFileVersion=1.3.0 -stdlibRegexVersion=1.3.0 -stdlibCacheVersion=3.2.2 -stdlibCryptoVersion=2.2.2 -stdlibTimeVersion=2.2.2 -stdlibMimeVersion=2.3.0 -stdlibOsVersion=1.3.0 -stdlibTaskVersion=2.2.2 -stdlibJwtVersion=2.3.0 -stdlibOAuth2Version=2.3.0 -stdlibUuidVersion=1.3.0 -stdlibUrlVersion=2.2.2 +stdlibIoVersion=1.2.2-20220512-114900-0d9af6e +stdlibLogVersion=2.3.0-20220524-082200-b38eda1 +stdlibHttpVersion=2.3.0-20220524-125800-903e1ba +stdlibAuthVersion=2.3.0-20220524-121500-fca79ed +stdlibFileVersion=1.3.0-20220524-121200-ace183b +stdlibRegexVersion=1.3.0-20220511-152100-0dd5f08 +stdlibCacheVersion=3.2.2-20220524-081900-4cd7ba0 +stdlibCryptoVersion=2.2.2-20220520-104000-8893e40 +stdlibTimeVersion=2.2.2-20220512-114900-b6ad9a5 +stdlibMimeVersion=2.3.0-20220524-121500-b5feb0c +stdlibOsVersion=1.3.0-20220520-104100-691a223 +stdlibTaskVersion=2.2.2-20220520-104500-363b385 +stdlibJwtVersion=2.3.0-20220524-121600-d31ab3d +stdlibOAuth2Version=2.3.0-20220524-121700-a777f0d +stdlibUuidVersion=1.5.0-20220520-105500-0a937d5 +stdlibUrlVersion=2.2.2-20220511-152900-e4473e4 observeVersion=1.0.4 observeInternalVersion=1.0.3 From e004a60b756f01e1359db0eb9ea2ced3372775a5 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 28 Mar 2022 16:21:37 +0530 Subject: [PATCH 02/59] Add initial service based implementation --- ballerina-tests/Ballerina.toml | 2 +- ballerina-tests/main.bal | 246 +- ballerina-tests/tests/resources/base-dot.json | 108 + .../tests/resources/cosmos-db-arr.json | 109 + .../tests/resources/cosmos-db.json | 117 + .../tests/resources/multi-path-param.json | 112 + .../tests/resources/multi-res-query.json | 114 + .../tests/resources/multi-res.json | 112 + .../tests/resources/payload-json-json.json | 118 + .../tests/resources/payload-json-record.json | 118 + .../tests/resources/payload-octa-byte.json | 116 + .../tests/resources/payload-text-byte.json | 116 + .../tests/resources/payload-text-string.json | 116 + .../tests/resources/payload-xml-xml.json | 116 + .../tests/resources/query-arr-no-payload.json | 113 + .../tests/resources/query-no-payload.json | 113 + .../tests/resources/query-param.json | 113 + .../tests/resources/queue-input.json | 19 + .../tests/resources/queue-string.json | 18 + .../resources/res-path-conflict-param.json | 111 + .../tests/resources/res-path-param.json | 108 + ballerina-tests/tests/resources/res-path.json | 108 + .../{request.json => resources/simple.json} | 6 +- ballerina-tests/tests/resources/temp.json | 110 + ballerina-tests/tests/resources/timer.json | 18 + .../tests/resources/trigger-cosmos-base.json | 12 + ballerina-tests/tests/test.bal | 153 +- ballerina/Ballerina.toml | 11 +- ballerina/CompilerPlugin.toml | 4 +- ballerina/Dependencies.toml | 10 +- ballerina/annotation.bal | 34 +- ballerina/blob_listener.bal | 41 + ballerina/build.gradle | 20 +- ballerina/code.bal | 55 +- ballerina/cosmos_listener.bal | 41 + ballerina/dispatcher_service.bal | 45 + ballerina/http_listener.bal | 53 + ballerina/http_service.bal | 48 + ballerina/init.bal | 25 + ballerina/natives.bal | 52 + ballerina/queue_listener.bal | 41 + ballerina/service_types.bal | 23 + ballerina/timer_listener.bal | 41 + build-config/resources/Ballerina.toml | 9 + build-config/resources/CompilerPlugin.toml | 2 +- .../generator/test/HandlerTest.java | 69 - .../{generator => }/test/DeploymentTest.java | 6 +- .../azurefunctions/test/HandlerTest.java | 136 + .../test/ProjectValidationTests.java | 2 +- .../test/utils/ParserTestConstants.java | 2 +- .../test/utils/ParserTestUtils.java | 20 +- .../test/utils/ProcessOutput.java | 2 +- .../test/utils/SyntaxTreeJSONGenerator.java | 20 +- .../{generator => }/test/utils/TestUtils.java | 2 +- .../test/resources/handlers/Ballerina.toml | 4 + .../resources/handlers/code/Ballerina.toml | 7 - .../src/test/resources/handlers/code/main.bal | 187 - .../handlers/generated/generated.bal | 82 - .../handlers/generated/generated.json | 18777 ---------------- .../src/test/resources/handlers/main.bal | 87 + .../src/test/resources/testng.xml | 6 +- compiler-plugin/spotbugs-exclude.xml | 2 +- .../AzureCodeGeneratedTask.java | 88 + .../azurefunctions/AzureCodeModifier.java | 32 + .../{generator => }/AzureCompilerPlugin.java | 8 +- .../AzureFunctionDiagnostics.java | 2 +- .../azurefunctions/AzureFunctionModifier.java | 158 + ...ava => AzureFunctionServiceExtractor.java} | 17 +- .../AzureFunctionServiceVisitor.java | 82 + .../AzureFunctionsException.java | 2 +- .../AzureLifecycleListener.java | 2 +- .../{generator => }/Constants.java | 11 +- .../azurefunctions/FunctionContext.java | 28 + .../azurefunctions/FunctionUpdaterTask.java | 56 + .../{generator => }/FunctionsArtifact.java | 2 +- .../azurefunctions/UniqueIDHolder.java | 15 + .../org/ballerinax/azurefunctions/Util.java | 68 + .../generator/AzureCodeGeneratedTask.java | 89 - .../generator/AzureCodegenTask.java | 138 - .../generator/AzureFunctionVisitor.java | 100 - .../generator/AzureFunctionsCodeAnalyzer.java | 35 - .../AzureFunctionsExtensionProvider.java | 37 - .../generator/FunctionDeploymentContext.java | 97 - .../generator/FunctionHandlerGenerator.java | 106 - .../generator/GeneratorUtil.java | 931 - .../generator/HandlerFactory.java | 154 - .../generator/ParameterHandler.java | 61 - .../generator/ReturnHandler.java | 47 - .../handlers/AbstractParameterHandler.java | 82 - .../handlers/AbstractReturnHandler.java | 66 - .../blob/BlobInputParameterHandler.java | 89 - .../blob/BlobOutputParameterHandler.java | 96 - .../blob/BlobTriggerParameterHandler.java | 87 - .../context/ContextParameterHandler.java | 62 - .../CosmosDBInputParameterHandler.java | 167 - .../cosmosdb/CosmosDBReturnHandler.java | 86 - .../cosmosdb/CosmosDBTriggerHandler.java | 127 - .../http/HTTPOutputParameterHandler.java | 78 - .../handlers/http/HTTPReturnHandler.java | 74 - .../http/HTTPTriggerParameterHandler.java | 86 - .../MetadataBindingParameterHandler.java | 81 - .../queue/QueueOutputParameterHandler.java | 88 - .../handlers/queue/QueueTriggerHandler.java | 88 - .../handlers/timer/TimerTriggerHandler.java | 81 - .../TwilioSmsOutputParameterHandler.java | 94 - .../AzureFunctionsCodeAnalyzerTask.java | 90 - .../validators/MainFunctionValidator.java | 75 - .../validators/SubmoduleValidator.java | 62 - .../azurefunctions/service/Binding.java | 45 + .../azurefunctions/service/InputBinding.java | 15 + .../service/InputBindingBuilder.java | 37 + .../azurefunctions/service/OutputBinding.java | 14 + .../service/OutputBindingBuilder.java | 48 + .../service/RemoteTriggerBinding.java | 113 + .../service/ServiceHandler.java | 183 + .../service/TriggerBinding.java | 99 + .../service/blob/BlobInputBinding.java | 85 + .../service/blob/BlobOutputBinding.java | 87 + .../service/blob/BlobTriggerBinding.java | 84 + .../cosmosdb/CosmosDBInputBinding.java | 130 + .../cosmosdb/CosmosDBOutputBinding.java | 87 + .../cosmosdb/CosmosDBTriggerBinding.java | 114 + .../service/http/HTTPOutputBinding.java | 27 + .../service/http/HTTPTriggerBinding.java | 232 + .../service/queue/QueueOutputBinding.java | 74 + .../service/queue/QueueTriggerBinding.java | 72 + .../service/timer/TimerTriggerBinding.java | 70 + .../twilio/TwilioSmsOutputBinding.java | 100 + gradle.properties | 36 +- native/build.gradle | 76 + native/spotbugs-exclude.xml | 23 + .../stdlib/azure/functions/AZFParameter.java | 33 + .../stdlib/azure/functions/Constants.java | 29 + .../azure/functions/FunctionCallback.java | 109 + .../stdlib/azure/functions/HttpResource.java | 185 + .../functions/InputBindingParameter.java | 22 + .../stdlib/azure/functions/ModuleUtils.java | 30 +- .../functions/NativeHttpToAzureAdaptor.java | 161 + .../azure/functions/NativeRemoteAdapter.java | 127 + .../stdlib/azure/functions/ParamHandler.java | 59 + .../stdlib/azure/functions/PathParameter.java | 22 + .../azure/functions/PayloadParameter.java | 23 + .../azure/functions/QueryParameter.java | 23 + .../stdlib/azure/functions/Utilities.java | 76 + .../builder/AbstractPayloadBuilder.java | 111 + .../azure/functions/builder/ArrayBuilder.java | 46 + .../builder/BinaryPayloadBuilder.java | 64 + .../functions/builder/JsonPayloadBuilder.java | 63 + .../builder/StringPayloadBuilder.java | 71 + .../functions/builder/XmlPayloadBuilder.java | 53 + .../converter/JsonToRecordConverter.java | 82 + .../converter/StringToByteArrayConverter.java | 53 + .../UrlEncodedStringToMapConverter.java | 104 + .../src/main/java/module-info.java | 19 +- settings.gradle | 3 + spec/spec.md | 606 + 156 files changed, 7780 insertions(+), 22958 deletions(-) create mode 100644 ballerina-tests/tests/resources/base-dot.json create mode 100644 ballerina-tests/tests/resources/cosmos-db-arr.json create mode 100644 ballerina-tests/tests/resources/cosmos-db.json create mode 100644 ballerina-tests/tests/resources/multi-path-param.json create mode 100644 ballerina-tests/tests/resources/multi-res-query.json create mode 100644 ballerina-tests/tests/resources/multi-res.json create mode 100644 ballerina-tests/tests/resources/payload-json-json.json create mode 100644 ballerina-tests/tests/resources/payload-json-record.json create mode 100644 ballerina-tests/tests/resources/payload-octa-byte.json create mode 100644 ballerina-tests/tests/resources/payload-text-byte.json create mode 100644 ballerina-tests/tests/resources/payload-text-string.json create mode 100644 ballerina-tests/tests/resources/payload-xml-xml.json create mode 100644 ballerina-tests/tests/resources/query-arr-no-payload.json create mode 100644 ballerina-tests/tests/resources/query-no-payload.json create mode 100644 ballerina-tests/tests/resources/query-param.json create mode 100644 ballerina-tests/tests/resources/queue-input.json create mode 100644 ballerina-tests/tests/resources/queue-string.json create mode 100644 ballerina-tests/tests/resources/res-path-conflict-param.json create mode 100644 ballerina-tests/tests/resources/res-path-param.json create mode 100644 ballerina-tests/tests/resources/res-path.json rename ballerina-tests/tests/{request.json => resources/simple.json} (96%) create mode 100644 ballerina-tests/tests/resources/temp.json create mode 100644 ballerina-tests/tests/resources/timer.json create mode 100644 ballerina-tests/tests/resources/trigger-cosmos-base.json create mode 100644 ballerina/blob_listener.bal create mode 100644 ballerina/cosmos_listener.bal create mode 100644 ballerina/dispatcher_service.bal create mode 100644 ballerina/http_listener.bal create mode 100644 ballerina/http_service.bal create mode 100644 ballerina/init.bal create mode 100644 ballerina/natives.bal create mode 100644 ballerina/queue_listener.bal create mode 100644 ballerina/service_types.bal create mode 100644 ballerina/timer_listener.bal delete mode 100644 compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/HandlerTest.java rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/DeploymentTest.java (92%) create mode 100644 compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/ProjectValidationTests.java (98%) rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/utils/ParserTestConstants.java (95%) rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/utils/ParserTestUtils.java (98%) rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/utils/ProcessOutput.java (96%) rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/utils/SyntaxTreeJSONGenerator.java (88%) rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/{generator => }/test/utils/TestUtils.java (98%) create mode 100644 compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml delete mode 100644 compiler-plugin-tests/src/test/resources/handlers/code/Ballerina.toml delete mode 100644 compiler-plugin-tests/src/test/resources/handlers/code/main.bal delete mode 100644 compiler-plugin-tests/src/test/resources/handlers/generated/generated.bal delete mode 100644 compiler-plugin-tests/src/test/resources/handlers/generated/generated.json create mode 100644 compiler-plugin-tests/src/test/resources/handlers/main.bal create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeModifier.java rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator => }/AzureCompilerPlugin.java (77%) rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator => }/AzureFunctionDiagnostics.java (97%) create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator/AzureFunctionExtractor.java => AzureFunctionServiceExtractor.java} (74%) create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator => }/AzureFunctionsException.java (96%) rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator => }/AzureLifecycleListener.java (95%) rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator => }/Constants.java (81%) create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/{generator => }/FunctionsArtifact.java (99%) create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGeneratedTask.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodegenTask.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionVisitor.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsCodeAnalyzer.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsExtensionProvider.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionDeploymentContext.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionHandlerGenerator.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/GeneratorUtil.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/HandlerFactory.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ReturnHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractReturnHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobInputParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobOutputParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobTriggerParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/context/ContextParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBInputParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBReturnHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBTriggerHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPOutputParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPReturnHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPTriggerParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/metadata/MetadataBindingParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueOutputParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueTriggerHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/timer/TimerTriggerHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/twilio/TwilioSmsOutputParameterHandler.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/AzureFunctionsCodeAnalyzerTask.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/MainFunctionValidator.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/SubmoduleValidator.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java create mode 100644 native/build.gradle create mode 100644 native/spotbugs-exclude.xml create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGenerator.java => native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java (50%) create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/builder/AbstractPayloadBuilder.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/builder/ArrayBuilder.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/builder/BinaryPayloadBuilder.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/builder/StringPayloadBuilder.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/converter/StringToByteArrayConverter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/converter/UrlEncodedStringToMapConverter.java rename compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/BindingType.java => native/src/main/java/module-info.java (62%) create mode 100644 spec/spec.md diff --git a/ballerina-tests/Ballerina.toml b/ballerina-tests/Ballerina.toml index b7910200..71ce6b06 100644 --- a/ballerina-tests/Ballerina.toml +++ b/ballerina-tests/Ballerina.toml @@ -1,4 +1,4 @@ [package] org = "ballerinax" name = "azure_functions_tests" -version = "2.1.0" +version = "3.0.0" diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index a372656d..a794f4b5 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -1,187 +1,107 @@ -// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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/uuid; import ballerinax/azure_functions as af; -// HTTP request/response with no authentication -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { - return "Hello, " + payload + "!"; -} +listener af:HTTPListener ep = new (); -// HTTP request to add data to a queue -@af:Function -public isolated function fromHttpToQueue(af:Context ctx, - @af:HTTPTrigger {} af:HTTPRequest req, - @af:QueueOutput { queueName: "queue1" } af:StringOutputBinding msg) - returns @af:HTTPOutput af:HTTPBinding { - msg.value = req.body; - return { statusCode: 200, payload: "Request: " + req.toString() }; -} +public type DBEntry record { + string id; +}; -// A message put to a queue is copied to another queue -@af:Function -public isolated function fromQueueToQueue(af:Context ctx, - @af:QueueTrigger { queueName: "queue2" } string inMsg, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - ctx.log("In Message: " + inMsg); - ctx.log("Metadata: " + ctx.metadata.toString()); - outMsg.value = inMsg; -} +type Person record { + string name; + int age; +}; -// // A blob added to a container is copied to a queue -@af:Function -public isolated function fromBlobToQueue(af:Context ctx, - @af:BlobTrigger { path: "bpath1/{name}" } byte[] blobIn, - @af:BindingName { } string name, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) - returns error? { - outMsg.value = "Name: " + name + " Content: " + blobIn.toString(); -} +// @af:HTTPTest +service "hello" on ep { + resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from . path "; + } -// // HTTP request to read a blob value -@af:Function -public isolated function httpTriggerBlobInput(@af:HTTPTrigger { } af:HTTPRequest req, - @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) - returns @af:HTTPOutput string { - int length = 0; - if blobIn is byte[] { - length = blobIn.length(); - } - return "Blob: " + req.query["name"].toString() + " Length: " + - length.toString() + " Content: " + blobIn.toString(); -} + resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from foo path " + greeting; + } -// // HTTP request to add a new blob -@af:Function -public isolated function httpTriggerBlobOutput(@af:HTTPTrigger { } af:HTTPRequest req, - @af:BlobOutput { path: "bpath1/{Query.name}" } af:StringOutputBinding bb) - returns @af:HTTPOutput string|error { - bb.value = req.body; - return "Blob: " + req.query["name"].toString() + " Content: " + - bb?.value.toString(); -} + resource function post foo/[string bar](@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from foo param " + bar; + } -// // Sending an SMS -@af:Function -public isolated function sendSMS(@af:HTTPTrigger { } af:HTTPRequest req, - @af:TwilioSmsOutput { fromNumber: "+12069845840" } - af:TwilioSmsOutputBinding tb) - returns @af:HTTPOutput string { - tb.to = req.query["to"].toString(); - tb.body = req.body.toString(); - return "Message - to: " + tb?.to.toString() + " body: " + tb?.body.toString(); -} + resource function post foo/bar(@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from foo bar res"; + } -public type Person record { - string id; - string name; - string country; -}; + resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { + return "Hello from the query " + greeting + " " + name; + } -// // CosmosDB record trigger -@af:Function -public isolated function cosmosDBToQueue1(@af:CosmosDBTrigger { - connectionStringSetting: "CosmosDBConnection", databaseName: "db1", - collectionName: "c1" } Person[] req, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - outMsg.value = req.toString(); -} + resource function post db(@af:Payload string greeting, @af:CosmosDBInput { + connectionStringSetting: "CosmosDBConnection",databaseName: "db1", + collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:HTTPOutput string|error { + return "Hello " + greeting + input1[0].id; + } -@af:Function -public isolated function cosmosDBToQueue2(@af:CosmosDBTrigger { - connectionStringSetting: "CosmosDBConnection", databaseName: "db1", - collectionName: "c2" } json req, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - outMsg.value = req.toString(); -} + resource function post payload/jsonToRecord (@af:Payload Person greeting) returns @af:HTTPOutput string|error { + return "Hello from json to record " + greeting.name; + } -// // HTTP request to read CosmosDB records -@af:Function -public isolated function httpTriggerCosmosDBInput1( - @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - id: "{Query.id}", partitionKey: "{Query.country}" } json dbReq) - returns @af:HTTPOutput string|error { - return dbReq.toString(); -} + resource function post payload/jsonToJson (@af:Payload json greeting) returns @af:HTTPOutput string|error { + string name = check greeting.name; + return "Hello from json to json "+ name; + } -@af:Function -public isolated function httpTriggerCosmosDBInput2( - @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - id: "{Query.id}", partitionKey: "{Query.country}" } Person? dbReq) - returns @af:HTTPOutput string|error { - return dbReq.toString(); + resource function post payload/xmlToXml (@af:Payload xml greeting) returns @af:HTTPOutput string|error { + return greeting.toJsonString(); + } + + resource function post payload/textToString (@af:Payload string greeting) returns @af:HTTPOutput string|error { + return greeting; + } + + resource function post payload/textToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + return string:fromBytes(greeting); + } + + resource function post payload/octaToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + return string:fromBytes(greeting); + } } -@af:Function -public isolated function httpTriggerCosmosDBInput3( - @af:HTTPTrigger { route: "c1/{country}" } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - sqlQuery: "select * from c1 where c1.country = {country}" } - Person[] dbReq) - returns @af:HTTPOutput string|error { - return dbReq.toString(); +@af:QueueTrigger { + queueName: "queue2" } +service "queue" on new af:QueueListener() { + remote function onMessage (@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg; + } +} + +@af:CosmosDBTrigger {connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2"} +listener af:CosmosDBListener cosmosEp = new (); -// // HTTP request to write records to CosmosDB -@af:Function -public isolated function httpTriggerCosmosDBOutput1( - @af:HTTPTrigger { } af:HTTPRequest httpReq, @af:HTTPOutput af:HTTPBinding hb) - returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } json { - json entry = { id: uuid:createType1AsString(), name: "Saman", country: "Sri Lanka" }; - hb.payload = "Adding entry: " + entry.toString(); - return entry; +service "cosmos" on cosmosEp { + remote function onUpdated (@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } } -@af:Function -public isolated function httpTriggerCosmosDBOutput2( - @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:HTTPOutput af:HTTPBinding hb) - returns @af:CosmosDBOutput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } json { - json entry = [{ id: uuid:createType1AsString(), name: "John Doe A", country: "USA" }, - { id: uuid:createType1AsString(), name: "John Doe B", country: "USA" }]; - hb.payload = "Adding entries: " + entry.toString(); - return entry; +@af:TimerTrigger { schedule: "*/10 * * * * *" } +listener af:TimerListener timerListener = new af:TimerListener(); +service "timer" on timerListener { + remote function onTrigger (@af:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg.IsPastDue.toString(); + } } -@af:Function -public isolated function httpTriggerCosmosDBOutput3( - @af:HTTPTrigger { } af:HTTPRequest httpReq) - returns @af:CosmosDBOutput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } Person[] { - Person[] persons = []; - persons.push({id: uuid:createType1AsString(), name: "Jack", country: "UK"}); - persons.push({id: uuid:createType1AsString(), name: "Will", country: "UK"}); - return persons; +@af:QueueTrigger { + queueName: "queue4" } -// // A timer function which is executed every 10 seconds. -@af:Function -public isolated function queuePopulationTimer( - @af:TimerTrigger { schedule: "*/10 * * * * *" } json triggerInfo, - @af:QueueOutput { queueName: "queue4" } af:StringOutputBinding msg) { - msg.value = triggerInfo.toString(); +listener af:QueueListener queueListener1 = new af:QueueListener(); +service "queue-input" on queueListener1 { + remote function onMessage (@af:Payload string inMsg, @af:CosmosDBInput { + connectionStringSetting: "CosmosDBConnection",databaseName: "db1", + collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg + " " +input1[0].id; + } } + diff --git a/ballerina-tests/tests/resources/base-dot.json b/ballerina-tests/tests/resources/base-dot.json new file mode 100644 index 00000000..14adac11 --- /dev/null +++ b/ballerina-tests/tests/resources/base-dot.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello", + "X-WAWS-Unencoded-URL": "/api/hello" + }, + "sys": { + "MethodName": "post-hello", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/cosmos-db-arr.json b/ballerina-tests/tests/resources/cosmos-db-arr.json new file mode 100644 index 00000000..bb9ca613 --- /dev/null +++ b/ballerina-tests/tests/resources/cosmos-db-arr.json @@ -0,0 +1,109 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/db", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "a492fc81-584d-4c6b-96bd-73e64dfae778" + ], + "CLIENT-IP": [ + "10.0.32.12:51102" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.130.212:17220" + ], + "X-Original-URL": [ + "/api/hello/db" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/db" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + }, + "input1": "[{\"id\":\"hello1\",\"_rid\":\"zlIsAKxe8VCBhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCBhB4AAAAAAA==\/\",\"_etag\":\"\\\"3d00aaf8-0000-0700-0000-625412270000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1649676839},{\"id\":\"ssss\",\"_rid\":\"zlIsAKxe8VCChB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCChB4AAAAAAA==\/\",\"_etag\":\"\\\"3e00ed1c-0000-0700-0000-625412970000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1649676951},{\"id\":\"hello2\",\"_rid\":\"zlIsAKxe8VCDhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCDhB4AAAAAAA==\/\",\"_etag\":\"\\\"3e00c262-0000-0700-0000-625413330000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1649677107}]" + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "a492fc81-584d-4c6b-96bd-73e64dfae778", + "CLIENT-IP": "10.0.32.12:51102", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.130.212:17220", + "X-Original-URL": "/api/hello/db", + "X-WAWS-Unencoded-URL": "/api/hello/db", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-db", + "UtcNow": "2022-06-13T12:19:46.7381431Z", + "RandGuid": "c1e78f43-0004-4fc3-b605-371335fa8c6b" + } + } +} diff --git a/ballerina-tests/tests/resources/cosmos-db.json b/ballerina-tests/tests/resources/cosmos-db.json new file mode 100644 index 00000000..d770df42 --- /dev/null +++ b/ballerina-tests/tests/resources/cosmos-db.json @@ -0,0 +1,117 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://sl-update-1-rc.azurewebsites.net/api/hello/hi?id=11", + "Method": "POST", + "Query": { + "id": "11" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "sl-update-1-rc.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "51c48e88-d5fb-447a-adf4-1c144dfe3c66" + ], + "CLIENT-IP": [ + "10.0.32.14:45249" + ], + "X-SITE-DEPLOYMENT-ID": [ + "sl-update-1-rc" + ], + "WAS-DEFAULT-HOSTNAME": [ + "sl-update-1-rc.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.131.153:31932" + ], + "X-Original-URL": [ + "/api/hello/hi?id=11" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/hi?id=11" + ], + "DISGUISED-HOST": [ + "sl-update-1-rc.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": "hi" + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + }, + "input": "\"{\\\"id\\\":\\\"11\\\",\\\"_rid\\\":\\\"P89cAMobTCgBAAAAAAAAAA==\\\",\\\"_self\\\":\\\"dbs\/P89cAA==\/colls\/P89cAMobTCg=\/docs\/P89cAMobTCgBAAAAAAAAAA==\/\\\",\\\"_ts\\\":1652717163,\\\"_etag\\\":\\\"\\\\\\\"0501238d-0000-0700-0000-6282766b0000\\\\\\\"\\\"}\"" + }, + "Metadata": { + "id": "\"11\"", + "RemainingPath": "\"hi\"", + "Query": { + "id": "11" + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "sl-update-1-rc.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "51c48e88-d5fb-447a-adf4-1c144dfe3c66", + "CLIENT-IP": "10.0.32.14:45249", + "X-SITE-DEPLOYMENT-ID": "sl-update-1-rc", + "WAS-DEFAULT-HOSTNAME": "sl-update-1-rc.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.131.153:31932", + "X-Original-URL": "/api/hello/hi?id=11", + "X-WAWS-Unencoded-URL": "/api/hello/hi?id=11", + "DISGUISED-HOST": "sl-update-1-rc.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-05-17T08:30:37.1181668Z", + "RandGuid": "e134c648-4722-4ef5-a889-6a60830cb2d8" + } + } +} diff --git a/ballerina-tests/tests/resources/multi-path-param.json b/ballerina-tests/tests/resources/multi-path-param.json new file mode 100644 index 00000000..d3967bc0 --- /dev/null +++ b/ballerina-tests/tests/resources/multi-path-param.json @@ -0,0 +1,112 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-svc-impl.azurewebsites.net/api/hello/foo/one/bar/two", + "Method": "POST", + "Query": { + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-svc-impl.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "df5cfa56-ac43-4671-8836-07537852bdf6" + ], + "CLIENT-IP": [ + "10.0.32.6:45851" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-svc-impl" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-svc-impl.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.134.89:51660" + ], + "X-Original-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "DISGUISED-HOST": [ + "bal-svc-impl.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": "foo/one/bar/two" + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": { + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-svc-impl.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "df5cfa56-ac43-4671-8836-07537852bdf6", + "CLIENT-IP": "10.0.32.6:45851", + "X-SITE-DEPLOYMENT-ID": "bal-svc-impl", + "WAS-DEFAULT-HOSTNAME": "bal-svc-impl.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.134.89:51660", + "X-Original-URL": "/api/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-04-29T10:51:55.458828Z", + "RandGuid": "c4747555-c815-4569-95ed-5d76f3691a98" + } + } +} diff --git a/ballerina-tests/tests/resources/multi-res-query.json b/ballerina-tests/tests/resources/multi-res-query.json new file mode 100644 index 00000000..365d6cfa --- /dev/null +++ b/ballerina-tests/tests/resources/multi-res-query.json @@ -0,0 +1,114 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-svc-impl.azurewebsites.net/api/hello/foo?name=hello", + "Method": "POST", + "Query": { + "name": "hello" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-svc-impl.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "df5cfa56-ac43-4671-8836-07537852bdf6" + ], + "CLIENT-IP": [ + "10.0.32.6:45851" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-svc-impl" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-svc-impl.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.134.89:51660" + ], + "X-Original-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "DISGUISED-HOST": [ + "bal-svc-impl.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": "hello/foo" + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": { + "test": "hello" + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-svc-impl.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "df5cfa56-ac43-4671-8836-07537852bdf6", + "CLIENT-IP": "10.0.32.6:45851", + "X-SITE-DEPLOYMENT-ID": "bal-svc-impl", + "WAS-DEFAULT-HOSTNAME": "bal-svc-impl.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.134.89:51660", + "X-Original-URL": "/api/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-04-29T10:51:55.458828Z", + "RandGuid": "c4747555-c815-4569-95ed-5d76f3691a98" + } + } +} diff --git a/ballerina-tests/tests/resources/multi-res.json b/ballerina-tests/tests/resources/multi-res.json new file mode 100644 index 00000000..4baa207f --- /dev/null +++ b/ballerina-tests/tests/resources/multi-res.json @@ -0,0 +1,112 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-svc-impl.azurewebsites.net/api/hello/foo/bar?test=hello", + "Method": "POST", + "Query": { + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-svc-impl.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "df5cfa56-ac43-4671-8836-07537852bdf6" + ], + "CLIENT-IP": [ + "10.0.32.6:45851" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-svc-impl" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-svc-impl.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.134.89:51660" + ], + "X-Original-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "DISGUISED-HOST": [ + "bal-svc-impl.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": "foo/bar" + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": { + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-svc-impl.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "df5cfa56-ac43-4671-8836-07537852bdf6", + "CLIENT-IP": "10.0.32.6:45851", + "X-SITE-DEPLOYMENT-ID": "bal-svc-impl", + "WAS-DEFAULT-HOSTNAME": "bal-svc-impl.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.134.89:51660", + "X-Original-URL": "/api/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-04-29T10:51:55.458828Z", + "RandGuid": "c4747555-c815-4569-95ed-5d76f3691a98" + } + } +} diff --git a/ballerina-tests/tests/resources/payload-json-json.json b/ballerina-tests/tests/resources/payload-json-json.json new file mode 100644 index 00000000..ffcd3908 --- /dev/null +++ b/ballerina-tests/tests/resources/payload-json-json.json @@ -0,0 +1,118 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "41" + ], + "Content-Type": [ + "application/json" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "39884e46-dbbd-4396-a921-d24cd2003063" + ], + "X-ARR-LOG-ID": [ + "0320ea36-d38d-476d-a3d2-61cb5b113763" + ], + "CLIENT-IP": [ + "10.0.32.18:48308" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.131.214:19414" + ], + "X-Original-URL": [ + "/api/hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "{\n \"name\" : \"Anjana\",\n \"age\" : 12\n}" + } + }, + "Metadata": { + "name": "\"Anjana\"", + "age": "\"12\"", + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "41", + "Content-Type": "application/json", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "39884e46-dbbd-4396-a921-d24cd2003063", + "X-ARR-LOG-ID": "0320ea36-d38d-476d-a3d2-61cb5b113763", + "CLIENT-IP": "10.0.32.18:48308", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.131.214:19414", + "X-Original-URL": "/api/hello", + "X-WAWS-Unencoded-URL": "/api/hello", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-payload-jsonToJson", + "UtcNow": "2022-06-20T07:50:50.4482488Z", + "RandGuid": "3e4ea15e-30cf-408b-8e72-58409d33e9c4" + } + } +} diff --git a/ballerina-tests/tests/resources/payload-json-record.json b/ballerina-tests/tests/resources/payload-json-record.json new file mode 100644 index 00000000..d325737e --- /dev/null +++ b/ballerina-tests/tests/resources/payload-json-record.json @@ -0,0 +1,118 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "41" + ], + "Content-Type": [ + "application/json" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "39884e46-dbbd-4396-a921-d24cd2003063" + ], + "X-ARR-LOG-ID": [ + "0320ea36-d38d-476d-a3d2-61cb5b113763" + ], + "CLIENT-IP": [ + "10.0.32.18:48308" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.131.214:19414" + ], + "X-Original-URL": [ + "/api/hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "{\n \"name\" : \"Anjana\",\n \"age\" : 12\n}" + } + }, + "Metadata": { + "name": "\"Anjana\"", + "age": "\"12\"", + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "41", + "Content-Type": "application/json", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "39884e46-dbbd-4396-a921-d24cd2003063", + "X-ARR-LOG-ID": "0320ea36-d38d-476d-a3d2-61cb5b113763", + "CLIENT-IP": "10.0.32.18:48308", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.131.214:19414", + "X-Original-URL": "/api/hello", + "X-WAWS-Unencoded-URL": "/api/hello", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-payload-jsonToRecord", + "UtcNow": "2022-06-20T07:50:50.4482488Z", + "RandGuid": "3e4ea15e-30cf-408b-8e72-58409d33e9c4" + } + } +} diff --git a/ballerina-tests/tests/resources/payload-octa-byte.json b/ballerina-tests/tests/resources/payload-octa-byte.json new file mode 100644 index 00000000..d7af5b94 --- /dev/null +++ b/ballerina-tests/tests/resources/payload-octa-byte.json @@ -0,0 +1,116 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/octaToByte", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "20" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "4464b495-cbb0-4398-b79f-9b626afc54af" + ], + "X-ARR-LOG-ID": [ + "03f45b02-ca0c-44a4-8fbd-1a916dc44743" + ], + "CLIENT-IP": [ + "10.0.32.9:31863" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.183:8622" + ], + "X-Original-URL": [ + "/api/hello/payload/octaToByte" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/payload/octaToByte" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "aGVsbG8gZnJvbSBieXRlIGFycgo=" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "20", + "Content-Type": "application/octet-stream", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "4464b495-cbb0-4398-b79f-9b626afc54af", + "X-ARR-LOG-ID": "03f45b02-ca0c-44a4-8fbd-1a916dc44743", + "CLIENT-IP": "10.0.32.9:31863", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.183:8622", + "X-Original-URL": "/api/hello/payload/octaToByte", + "X-WAWS-Unencoded-URL": "/api/hello/payload/octaToByte", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-payload-octaToByte", + "UtcNow": "2022-06-21T09:16:39.7549127Z", + "RandGuid": "4d6607a9-6f7f-47ab-b604-9d38b6d60166" + } + } +} diff --git a/ballerina-tests/tests/resources/payload-text-byte.json b/ballerina-tests/tests/resources/payload-text-byte.json new file mode 100644 index 00000000..84821773 --- /dev/null +++ b/ballerina-tests/tests/resources/payload-text-byte.json @@ -0,0 +1,116 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/textToByte", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "30ab1809-31c6-4289-bdf1-211af03494e6" + ], + "X-ARR-LOG-ID": [ + "583960d3-f15e-40ba-8c6e-f36ef5f9b04d" + ], + "CLIENT-IP": [ + "10.0.32.17:56250" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.183:7912" + ], + "X-Original-URL": [ + "/api/hello/payload/textToByte" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/payload/textToByte" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "hello from byte\n" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "16", + "Content-Type": "text/plain", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "30ab1809-31c6-4289-bdf1-211af03494e6", + "X-ARR-LOG-ID": "583960d3-f15e-40ba-8c6e-f36ef5f9b04d", + "CLIENT-IP": "10.0.32.17:56250", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.183:7912", + "X-Original-URL": "/api/hello/payload/textToByte", + "X-WAWS-Unencoded-URL": "/api/hello/payload/textToByte", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-payload-textToByte", + "UtcNow": "2022-06-21T09:03:30.9844689Z", + "RandGuid": "1a881943-cf07-4ead-b539-d86c5040e2b7" + } + } +} diff --git a/ballerina-tests/tests/resources/payload-text-string.json b/ballerina-tests/tests/resources/payload-text-string.json new file mode 100644 index 00000000..760304e2 --- /dev/null +++ b/ballerina-tests/tests/resources/payload-text-string.json @@ -0,0 +1,116 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/textToString", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "16" + ], + "Content-Type": [ + "text/plain" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "0a6f2d81-c33b-46eb-8de5-fa3f89dbd102" + ], + "X-ARR-LOG-ID": [ + "b726530b-e6cf-45f4-a33d-61a6dc9b50ff" + ], + "CLIENT-IP": [ + "10.0.32.6:44171" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.183:7868" + ], + "X-Original-URL": [ + "/api/hello/payload/textToString" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/payload/textToString" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "hello from byte\n" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "16", + "Content-Type": "text/plain", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "0a6f2d81-c33b-46eb-8de5-fa3f89dbd102", + "X-ARR-LOG-ID": "b726530b-e6cf-45f4-a33d-61a6dc9b50ff", + "CLIENT-IP": "10.0.32.6:44171", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.183:7868", + "X-Original-URL": "/api/hello/payload/textToString", + "X-WAWS-Unencoded-URL": "/api/hello/payload/textToString", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-payload-textToString", + "UtcNow": "2022-06-21T08:19:49.9190374Z", + "RandGuid": "b079ac9b-37d8-4037-a4fe-2c4a367ea4bd" + } + } +} diff --git a/ballerina-tests/tests/resources/payload-xml-xml.json b/ballerina-tests/tests/resources/payload-xml-xml.json new file mode 100644 index 00000000..dfd73511 --- /dev/null +++ b/ballerina-tests/tests/resources/payload-xml-xml.json @@ -0,0 +1,116 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/xmlToXml", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "92" + ], + "Content-Type": [ + "application/xml" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "35be09d4-fc8d-45c5-ba43-1cea2e71d92d" + ], + "X-ARR-LOG-ID": [ + "138a364a-acfc-47b1-a28d-8d6cdadd15ed" + ], + "CLIENT-IP": [ + "10.0.32.13:26380" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.131.47:2686" + ], + "X-Original-URL": [ + "/api/hello/payload/xmlToXml" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/payload/xmlToXml" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "\n\n Anjana<\/name>\n 12<\/age>\n<\/root>" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "92", + "Content-Type": "application/xml", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "35be09d4-fc8d-45c5-ba43-1cea2e71d92d", + "X-ARR-LOG-ID": "138a364a-acfc-47b1-a28d-8d6cdadd15ed", + "CLIENT-IP": "10.0.32.13:26380", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.131.47:2686", + "X-Original-URL": "/api/hello/payload/xmlToXml", + "X-WAWS-Unencoded-URL": "/api/hello/payload/xmlToXml", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-payload-xmlToXml", + "UtcNow": "2022-06-20T14:58:03.9297621Z", + "RandGuid": "2e7871d4-c404-49ad-ae44-e148f33be742" + } + } +} diff --git a/ballerina-tests/tests/resources/query-arr-no-payload.json b/ballerina-tests/tests/resources/query-arr-no-payload.json new file mode 100644 index 00000000..0f64e492 --- /dev/null +++ b/ballerina-tests/tests/resources/query-arr-no-payload.json @@ -0,0 +1,113 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-svc-impl.azurewebsites.net/api/hello", + "Method": "GET", + "Query": { + "name" : ["Jack 1", "Jack 2"] + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-svc-impl.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "df5cfa56-ac43-4671-8836-07537852bdf6" + ], + "CLIENT-IP": [ + "10.0.32.6:45851" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-svc-impl" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-svc-impl.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.134.89:51660" + ], + "X-Original-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "DISGUISED-HOST": [ + "bal-svc-impl.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": null + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": { + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-svc-impl.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "df5cfa56-ac43-4671-8836-07537852bdf6", + "CLIENT-IP": "10.0.32.6:45851", + "X-SITE-DEPLOYMENT-ID": "bal-svc-impl", + "WAS-DEFAULT-HOSTNAME": "bal-svc-impl.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.134.89:51660", + "X-Original-URL": "/api/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-04-29T10:51:55.458828Z", + "RandGuid": "c4747555-c815-4569-95ed-5d76f3691a98" + } + } +} diff --git a/ballerina-tests/tests/resources/query-no-payload.json b/ballerina-tests/tests/resources/query-no-payload.json new file mode 100644 index 00000000..647d96c0 --- /dev/null +++ b/ballerina-tests/tests/resources/query-no-payload.json @@ -0,0 +1,113 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-svc-impl.azurewebsites.net/api/hello", + "Method": "GET", + "Query": { + "name" : "Jack" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-svc-impl.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "df5cfa56-ac43-4671-8836-07537852bdf6" + ], + "CLIENT-IP": [ + "10.0.32.6:45851" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-svc-impl" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-svc-impl.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.134.89:51660" + ], + "X-Original-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/bar?test=hello" + ], + "DISGUISED-HOST": [ + "bal-svc-impl.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": null + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": { + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-svc-impl.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "df5cfa56-ac43-4671-8836-07537852bdf6", + "CLIENT-IP": "10.0.32.6:45851", + "X-SITE-DEPLOYMENT-ID": "bal-svc-impl", + "WAS-DEFAULT-HOSTNAME": "bal-svc-impl.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.134.89:51660", + "X-Original-URL": "/api/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-04-29T10:51:55.458828Z", + "RandGuid": "c4747555-c815-4569-95ed-5d76f3691a98" + } + } +} diff --git a/ballerina-tests/tests/resources/query-param.json b/ballerina-tests/tests/resources/query-param.json new file mode 100644 index 00000000..18ec1f5d --- /dev/null +++ b/ballerina-tests/tests/resources/query-param.json @@ -0,0 +1,113 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/query?name=test1", + "Method": "POST", + "Query": { + "name": "test1" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "072319b5-49e5-4c56-859c-4fcc1a714811" + ], + "CLIENT-IP": [ + "10.0.32.4:29256" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.130.212:18792" + ], + "X-Original-URL": [ + "/api/hello/query?name=test1" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/query?name=test1" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "name": "\"test1\"", + "Query": { + "name": "test1" + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "072319b5-49e5-4c56-859c-4fcc1a714811", + "CLIENT-IP": "10.0.32.4:29256", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.130.212:18792", + "X-Original-URL": "/api/hello/query?name=test1", + "X-WAWS-Unencoded-URL": "/api/hello/query?name=test1", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-query", + "UtcNow": "2022-06-13T10:12:25.6088199Z", + "RandGuid": "80274c51-3807-49ae-bfbf-44c5b74fa4e9" + } + } +} diff --git a/ballerina-tests/tests/resources/queue-input.json b/ballerina-tests/tests/resources/queue-input.json new file mode 100644 index 00000000..c3e41eeb --- /dev/null +++ b/ballerina-tests/tests/resources/queue-input.json @@ -0,0 +1,19 @@ +{ + "Data": { + "inMsg": "\"qqeeewwww\"", + "input1": "[{\"id\":\"hello1\",\"_rid\":\"zlIsAKxe8VCBhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCBhB4AAAAAAA==\/\",\"_etag\":\"\\\"3d00aaf8-0000-0700-0000-625412270000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1649676839},{\"id\":\"ssss\",\"_rid\":\"zlIsAKxe8VCChB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCChB4AAAAAAA==\/\",\"_etag\":\"\\\"3e00ed1c-0000-0700-0000-625412970000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1649676951},{\"id\":\"hello2\",\"_rid\":\"zlIsAKxe8VCDhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCDhB4AAAAAAA==\/\",\"_etag\":\"\\\"3e00c262-0000-0700-0000-625413330000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1649677107},{\"id\":\"01ecf794-9b0a-132e-8eb4-4019891a13ad\",\"name\":\"Jack\",\"country\":\"Sri Lanka\",\"_rid\":\"zlIsAKxe8VCEhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCEhB4AAAAAAA==\/\",\"_etag\":\"\\\"1f0554aa-0000-0700-0000-62bc269a0000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1656497818},{\"id\":\"replace_with_new_document_id\",\"name\":\"Anjana\",\"_rid\":\"zlIsAKxe8VCFhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCFhB4AAAAAAA==\/\",\"_etag\":\"\\\"1f05eac6-0000-0700-0000-62bc3e700000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1656503920},{\"id\":\"tessstt\",\"_rid\":\"zlIsAKxe8VCGhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCGhB4AAAAAAA==\/\",\"_etag\":\"\\\"1f0568ca-0000-0700-0000-62bc41500000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1656504656},{\"id\":\"ehee\",\"_rid\":\"zlIsAKxe8VCHhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCHhB4AAAAAAA==\/\",\"_etag\":\"\\\"1f0507d0-0000-0700-0000-62bc45ca0000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1656505802},{\"id\":\"qqme\",\"_rid\":\"zlIsAKxe8VCIhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCIhB4AAAAAAA==\/\",\"_etag\":\"\\\"0d00f5cb-0000-0700-0000-62be88f40000\\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1656654068}]" + }, + "Metadata": { + "DequeueCount": "1", + "ExpirationTime": "2022-07-14T04:40:05+00:00", + "Id": "\"71d4bded-1113-4cd2-9cc2-8886f3fffea7\"", + "InsertionTime": "2022-07-07T04:40:05+00:00", + "NextVisibleTime": "2022-07-07T04:50:25+00:00", + "PopReceipt": "\"AgAAAAMAAAAAAAAA64VxEr2R2AE=\"", + "sys": { + "MethodName": "queue-input", + "UtcNow": "2022-07-07T04:40:25.4502012Z", + "RandGuid": "a5beb30f-4f4d-4853-99b3-56aa2966d897" + } + } +} diff --git a/ballerina-tests/tests/resources/queue-string.json b/ballerina-tests/tests/resources/queue-string.json new file mode 100644 index 00000000..e925ea26 --- /dev/null +++ b/ballerina-tests/tests/resources/queue-string.json @@ -0,0 +1,18 @@ +{ + "Data": { + "inMsg": "\"aaaaa\"" + }, + "Metadata": { + "DequeueCount": "1", + "ExpirationTime": "2022-06-20T06:00:06+00:00", + "Id": "\"a0004923-f050-417a-9742-6d7c7b984134\"", + "InsertionTime": "2022-06-13T06:00:06+00:00", + "NextVisibleTime": "2022-06-13T06:10:18+00:00", + "PopReceipt": "\"AgAAAAMAAAAAAAAA8d5gQex+2AE=\"", + "sys": { + "MethodName": "queue", + "UtcNow": "2022-06-13T06:00:18.1974545Z", + "RandGuid": "d9ba2785-ee1a-4260-a83b-f57cb54b969e" + } + } +} diff --git a/ballerina-tests/tests/resources/res-path-conflict-param.json b/ballerina-tests/tests/resources/res-path-conflict-param.json new file mode 100644 index 00000000..99d48ef7 --- /dev/null +++ b/ballerina-tests/tests/resources/res-path-conflict-param.json @@ -0,0 +1,111 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/foo/meow", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "17355b71-874c-4cea-ac49-226eaffc9423" + ], + "CLIENT-IP": [ + "10.0.32.7:33881" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:42408" + ], + "X-Original-URL": [ + "/api/hello/foo/meow" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/meow" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": { + "bar": "meow" + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "bar": "\"meow\"", + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "17355b71-874c-4cea-ac49-226eaffc9423", + "CLIENT-IP": "10.0.32.7:33881", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:42408", + "X-Original-URL": "/api/hello/foo/meow", + "X-WAWS-Unencoded-URL": "/api/hello/foo/meow", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-foo-0", + "UtcNow": "2022-06-10T08:23:28.1520104Z", + "RandGuid": "6d6d581c-3dce-4907-8638-c1f18a6e0d64" + } + } +} diff --git a/ballerina-tests/tests/resources/res-path-param.json b/ballerina-tests/tests/resources/res-path-param.json new file mode 100644 index 00000000..b6f69e77 --- /dev/null +++ b/ballerina-tests/tests/resources/res-path-param.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/foo/bar", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "aedb1fdc-e89c-4740-aa5f-40ed87792703" + ], + "CLIENT-IP": [ + "10.0.32.6:54697" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:42010" + ], + "X-Original-URL": [ + "/api/hello/foo/bar" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo/bar" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "aedb1fdc-e89c-4740-aa5f-40ed87792703", + "CLIENT-IP": "10.0.32.6:54697", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:42010", + "X-Original-URL": "/api/hello/foo/bar", + "X-WAWS-Unencoded-URL": "/api/hello/foo/bar", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-foo-bar", + "UtcNow": "2022-06-10T07:57:46.8453883Z", + "RandGuid": "0f4737ff-5b49-4e1d-9639-e51aaca81bc6" + } + } +} diff --git a/ballerina-tests/tests/resources/res-path.json b/ballerina-tests/tests/resources/res-path.json new file mode 100644 index 00000000..6260426a --- /dev/null +++ b/ballerina-tests/tests/resources/res-path.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/foo", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "1d35ad4a-0f3e-4be3-838b-c2983d0283f9" + ], + "CLIENT-IP": [ + "10.0.32.17:60066" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41478" + ], + "X-Original-URL": [ + "/api/hello/foo" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/foo" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "1d35ad4a-0f3e-4be3-838b-c2983d0283f9", + "CLIENT-IP": "10.0.32.17:60066", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41478", + "X-Original-URL": "/api/hello/foo", + "X-WAWS-Unencoded-URL": "/api/hello/foo", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-foo", + "UtcNow": "2022-06-10T08:29:04.8772396Z", + "RandGuid": "a0799c47-3216-47cf-9814-b50c05104c08" + } + } +} diff --git a/ballerina-tests/tests/request.json b/ballerina-tests/tests/resources/simple.json similarity index 96% rename from ballerina-tests/tests/request.json rename to ballerina-tests/tests/resources/simple.json index facc0207..7785182f 100644 --- a/ballerina-tests/tests/request.json +++ b/ballerina-tests/tests/resources/simple.json @@ -1,6 +1,6 @@ { "Data": { - "payload": { + "httpPayload": { "Url": "https://functions1778.azurewebsites.net/api/hello", "Method": "POST", "Query": {}, @@ -25,7 +25,9 @@ "X-Forwarded-TlsVersion": ["1.2"], "DISGUISED-HOST": ["functions1778.azurewebsites.net"] }, - "Params": {}, + "Params": { + "RemainingPath" : null + }, "Identities": [{ "AuthenticationType": null, "IsAuthenticated": false, diff --git a/ballerina-tests/tests/resources/temp.json b/ballerina-tests/tests/resources/temp.json new file mode 100644 index 00000000..4d62b253 --- /dev/null +++ b/ballerina-tests/tests/resources/temp.json @@ -0,0 +1,110 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-svc-impl.azurewebsites.net/api/hello", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-svc-impl.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "8a17b6d7-b17e-4edd-9fd4-025fe03e1859" + ], + "CLIENT-IP": [ + "10.0.32.13:54062" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-svc-impl" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-svc-impl.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.134.3:36070" + ], + "X-Original-URL": [ + "/api/hello" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello" + ], + "DISGUISED-HOST": [ + "bal-svc-impl.azurewebsites.net" + ] + }, + "Params": { + "RemainingPath": null + }, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-svc-impl.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "8a17b6d7-b17e-4edd-9fd4-025fe03e1859", + "CLIENT-IP": "10.0.32.13:54062", + "X-SITE-DEPLOYMENT-ID": "bal-svc-impl", + "WAS-DEFAULT-HOSTNAME": "bal-svc-impl.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.134.3:36070", + "X-Original-URL": "/api/hello", + "X-WAWS-Unencoded-URL": "/api/hello", + "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" + }, + "sys": { + "MethodName": "hello", + "UtcNow": "2022-04-30T04:22:18.5062555Z", + "RandGuid": "ad4274d6-fbe3-435e-a07c-57266317c018" + } + } +} diff --git a/ballerina-tests/tests/resources/timer.json b/ballerina-tests/tests/resources/timer.json new file mode 100644 index 00000000..9be8b31d --- /dev/null +++ b/ballerina-tests/tests/resources/timer.json @@ -0,0 +1,18 @@ +{ + "Data": { + "inMsg": { + "Schedule": { + "AdjustForDST": true + }, + "ScheduleStatus": null, + "IsPastDue": false + } + }, + "Metadata": { + "sys": { + "MethodName": "timer", + "UtcNow": "2022-07-05T11:40:07.4850616Z", + "RandGuid": "9cd4c06c-fc8f-4b63-af94-56bc75c3f0cb" + } + } +} diff --git a/ballerina-tests/tests/resources/trigger-cosmos-base.json b/ballerina-tests/tests/resources/trigger-cosmos-base.json new file mode 100644 index 00000000..c5002035 --- /dev/null +++ b/ballerina-tests/tests/resources/trigger-cosmos-base.json @@ -0,0 +1,12 @@ +{ + "Data": { + "inMsg": "[{\"id\":\"ehee\",\"_rid\":\"zlIsAKxe8VCHhB4AAAAAAA==\",\"_self\":\"dbs\/zlIsAA==\/colls\/zlIsAKxe8VA=\/docs\/zlIsAKxe8VCHhB4AAAAAAA==\/\",\"_ts\":1656505802,\"_etag\":\"\\\"1f0507d0-0000-0700-0000-62bc45ca0000\\\"\",\"_lsn\":9}]" + }, + "Metadata": { + "sys": { + "MethodName": "cosmos", + "UtcNow": "2022-06-29T12:30:04.2341186Z", + "RandGuid": "5e14dca0-efb6-47e0-ae50-a4c994675dce" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index d28bc8e5..256bf876 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -3,11 +3,156 @@ import ballerina/io; import ballerina/http; @test:Config { } -function testHelloWorld() returns error? { +function testBaseDot() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/base-dot.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from . path "}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + + +@test:Config { } +function testSimpleResourcePath() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/res-path.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-foo", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo path Jack"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testSimpleMultiResourcePath() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/res-path-param.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-foo-bar", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo bar res"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + + +@test:Config { } +function testSimpleMultiQueryPath() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-param.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-query", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from the query Jack test1"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + + +@test:Config { } +function testSimpleQueue() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/queue-string.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/queue", readJson); + json expectedResp = {"Outputs":{"outMsg":"helloo aaaaa"},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testCosmosInputArr() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/cosmos-db-arr.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-db", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello Jackhello1"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testJsonJsonPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/payload-json-json.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-payload-jsonToJson", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from json to json Anjana"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} +@test:Config { } +function testJsonRecordPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/payload-json-record.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-payload-jsonToRecord", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from json to record Anjana"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testXmlPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/payload-xml-xml.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-payload-xmlToXml", readJson); + string xmlPayload = "\"\\n Anjana<\\/name>\\n 12<\\/age>\\n<\\/root>\""; + json expectedResp = {"Outputs":{"resp":{"body":xmlPayload}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testTextStringPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/payload-text-string.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-payload-textToString", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"hello from byte\n"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testTextBytePayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/payload-text-byte.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-payload-textToByte", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"hello from byte\n"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testOctaBytePayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/payload-octa-byte.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-payload-octaToByte", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"hello from byte arr\n"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + + +@test:Config { } +function testCosmosTrigger() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/trigger-cosmos-base.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/cosmos", readJson); + json expectedResp = {"Outputs":{"outMsg":"helloo ehee"},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + + +@test:Config { } +function testTimerTrigger() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/timer.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/timer", readJson); + json expectedResp = {"Outputs":{"outMsg":"helloo false"},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testQueueInput() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/request.json"; + string jsonFilePath = "./tests/resources/queue-input.json"; json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/hello", readJson); - json expectedResp = {"Outputs":{},"Logs":[],"ReturnValue":"Hello, Jack!"}; + json resp = check clientEndpoint->post("/queue-input", readJson); + json expectedResp = {"Outputs":{"outMsg":"helloo qqeeewwww hello1"},"Logs":[],"ReturnValue":null}; test:assertEquals(resp, expectedResp); } diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 6466156b..471b66c0 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,4 +1,13 @@ [package] org = "ballerinax" name = "azure_functions" -version = "2.1.0" +version = "3.0.0" + +[[platform.java11.dependency]] +path = "../native/build/libs/azure_functions-native-3.0.0-SNAPSHOT.jar" + +#[[platform.java11.dependency]] +#path = "./lib/http-native-@stdlib.httpnative.version@.jar" + +[[platform.java11.dependency]] +path = "./lib/mime-native-2.3.0.jar" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index 49b572af..fcbcadaf 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -1,6 +1,6 @@ [plugin] id = "azure-functions" -class = "org.ballerinax.azurefunctions.generator.AzureCompilerPlugin" +class = "org.ballerinax.azurefunctions.AzureCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/azure_functions-compiler-plugin-2.1.0.jar" +path = "../compiler-plugin/build/libs/azure_functions-compiler-plugin-3.0.0-SNAPSHOT.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index b3901bc0..d5301fe7 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -89,11 +89,17 @@ dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.value"} ] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] [[package]] org = "ballerina" name = "jballerina.java" version = "0.0.0" +modules = [ + {org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"} +] [[package]] org = "ballerina" @@ -282,9 +288,11 @@ dependencies = [ [[package]] org = "ballerinax" name = "azure_functions" -version = "2.1.0" +version = "3.0.0" dependencies = [ {org = "ballerina", name = "http"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.array"}, {org = "ballerina", name = "lang.boolean"}, {org = "ballerina", name = "lang.int"}, diff --git a/ballerina/annotation.bal b/ballerina/annotation.bal index bca3d928..c2bdc2db 100644 --- a/ballerina/annotation.bal +++ b/ballerina/annotation.bal @@ -15,21 +15,24 @@ // under the License. # @azurefunctions:Function annotation. -public const annotation Function on function; +public const annotation Function on function; //Todo remove +public const annotation AZFunctionConfiguration AzureFunction on function; + +public type AZFunctionConfiguration record {| + string name; +|}; public type AUTH_LEVEL "anonymous"|"function"|"admin"; +public const annotation HTTPTriggerConfiguration HttpTrigger on source listener; # HTTPTrigger annotation configuration. # # + authLevel - The authentication level of the function -# + route - The route template public type HTTPTriggerConfiguration record {| - AUTH_LEVEL authLevel?; - string route?; + AUTH_LEVEL authLevel = "anonymous"; |}; -# @azurefunctions:HTTPTrigger annotation. -public const annotation HTTPTriggerConfiguration HTTPTrigger on parameter; +public annotation Payload on parameter, return; # @azurefunctions:HTTPOutput annotation public const annotation HTTPOutput on parameter, return; @@ -47,7 +50,7 @@ public type QueueConfiguration record {| public const annotation QueueConfiguration QueueOutput on parameter, return; # @azurefunctions:QueueOutput annotation. -public const annotation QueueConfiguration QueueTrigger on parameter; +public const annotation QueueConfiguration QueueTrigger on source listener, service; # TimerTrigger annotation configuration. # @@ -59,7 +62,7 @@ public type TimerTriggerConfiguration record {| |}; # @azurefunctions:TimerTrigger annotation. -public const annotation TimerTriggerConfiguration TimerTrigger on parameter; +public const annotation TimerTriggerConfiguration TimerTrigger on source listener, service; # Blob annotation configuration. # @@ -71,13 +74,13 @@ public type BlobConfiguration record {| |}; # @azurefunctions:BlobTrigger annotation. -public const annotation BlobConfiguration BlobTrigger on parameter; +public const annotation BlobConfiguration BlobTrigger on source listener, service; # @azurefunctions:BlobInput annotation. public const annotation BlobConfiguration BlobInput on parameter; # @azurefunctions:BlobOutput annotation. -public const annotation BlobConfiguration BlobOutput on parameter; +public const annotation BlobConfiguration BlobOutput on return; # CosmosDB trigger annotation configuration. # @@ -119,7 +122,7 @@ public type CosmosDBTriggerConfiguration record {| |}; # @azurefunctions:CosmosDBTrigger annotation. -public const annotation CosmosDBTriggerConfiguration CosmosDBTrigger on parameter; +public const annotation CosmosDBTriggerConfiguration CosmosDBTrigger on source listener, service; # CosmosDB input annotation configuration. # @@ -171,15 +174,18 @@ public const annotation CosmosDBOutputConfiguration CosmosDBOutput on return; # # + accountSidSetting - The app setting which holds the Twilio Account Sid # + authTokenSetting - The app setting which holds the Twilio authentication token -# + fromNumber - The phone number the SMS is sent from +# + from - The phone number the SMS is sent from +# + to - The phone number the SMS is sent to public type TwilioSmsConfiguration record {| string accountSidSetting = "AzureWebJobsTwilioAccountSid"; string authTokenSetting = "AzureWebJobsTwilioAuthToken"; - string fromNumber; + string 'from; + string to; + |}; # @azurefunctions:TwilioSmsOutput annotation. -public const annotation TwilioSmsConfiguration TwilioSmsOutput on parameter; +public const annotation TwilioSmsConfiguration TwilioSmsOutput on return; # BindingName annotation configuration. # diff --git a/ballerina/blob_listener.bal b/ballerina/blob_listener.bal new file mode 100644 index 00000000..4eeaacfb --- /dev/null +++ b/ballerina/blob_listener.bal @@ -0,0 +1,41 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. +public class BlobListener { + DispatcherService? httpService; + + public isolated function init() returns error? { + self.httpService = (); + } + + public function attach(BlobService svc, string[]|string? name = ()) returns error? { + AzureRemoteAdapter adaptor = new(svc); + self.httpService = new (adaptor, "onUpdated"); + check httpListener.attach(self.httpService, name); + } + + public isolated function detach(BlobService svc) returns error? { + } + + public function 'start() returns error? { + check httpListener.'start(); + } + + public isolated function gracefulStop() returns error? { + } + + public isolated function immediateStop() returns error? { + } +} diff --git a/ballerina/build.gradle b/ballerina/build.gradle index a0316ac0..7a51e798 100644 --- a/ballerina/build.gradle +++ b/ballerina/build.gradle @@ -64,23 +64,37 @@ ballerina { packageOrganization = packageOrg module = packageName langVersion = ballerinaLangVersion - platform = "any" } configurations { externalJars } +dependencies { + externalJars(group: 'io.ballerina.stdlib', name: 'http-native', version: "${stdlibHttpVersion}") { + transitive = false + } + externalJars(group: 'io.ballerina.stdlib', name: 'mime-native', version: "${stdlibMimeVersion}") { + transitive = false + } +} task updateTomlFiles { doLast { def newBallerinaToml = ballerinaTomlFilePlaceHolder.text.replace("@project.version@", project.version) newBallerinaToml = newBallerinaToml.replace("@toml.version@", tomlVersion) - ballerinaTomlFile.text = newBallerinaToml def newCompilerPluginToml = compilerPluginTomlFilePlaceHolder.text.replace("@project.version@", project.version) compilerPluginTomlFile.text = newCompilerPluginToml + +// def stdlibDependentHttpNativeVersion = project.stdlibHttpVersion +// newBallerinaToml = newBallerinaToml.replace("@stdlib.httpnative.version@", stdlibDependentHttpNativeVersion) + + def stdlibDependentMimeNativeVersion = project.stdlibMimeVersion + newBallerinaToml = newBallerinaToml.replace("@stdlib.mimenative.version@", stdlibDependentMimeNativeVersion) + + ballerinaTomlFile.text = newBallerinaToml } } @@ -120,9 +134,11 @@ publishing { updateTomlFiles.dependsOn copyStdlibs build.dependsOn "generatePomFileForMavenPublication" +build.dependsOn ":${packageName}-native:build" build.dependsOn ":${packageName}-compiler-plugin:build" build.finalizedBy ":${packageName}-compiler-plugin-tests:build" build.finalizedBy ":${packageName}-ballerina-tests:build" +test.dependsOn ":${packageName}-native:build" test.dependsOn ":${packageName}-compiler-plugin:build" test.finalizedBy ":${packageName}-compiler-plugin-tests:build" test.finalizedBy ":${packageName}-ballerina-tests:build" diff --git a/ballerina/code.bal b/ballerina/code.bal index 0278413d..6db9213c 100644 --- a/ballerina/code.bal +++ b/ballerina/code.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except @@ -21,6 +21,57 @@ import ballerina/lang.'array as arrays; import ballerina/lang.'string as strings; import ballerina/lang.'boolean as booleans; +http:Listener httpListener = check new (check ints:fromString(os:getEnv("FUNCTIONS_CUSTOMHANDLER_PORT"))); + +public type TimerMetadata record { + TimerSchedule Schedule; + anydata ScheduleStatus?; + boolean IsPastDue; +}; + +public type TimerSchedule record { + boolean AdjustForDST; +}; + +type Payload record { + map Data; + Metadata Metadata; +}; + +type Metadata record { + map Query; + map Headers; + Sys sys; +}; + +type Sys record { + string MethodName; + string UtcNow; + string RandGuid; +}; + +type HttpPayload record { + string Url; + string Method; + map Query; + map Headers; + map Params; + Identity[] Identities; + anydata? Body; +}; + +type Identity record { + anydata? AuthenticationType?; + boolean IsAuthenticated?; + anydata? Actor?; + anydata BootstrapContext?; + anydata[] Claims?; + anydata? Label?; + anydata? Name?; + string NameClaimType?; + string RoleClaimType?; +}; + # HTTP binding data. # # + statusCode - The HTTP response status code @@ -151,7 +202,7 @@ public isolated function logRequest(HandlerParams hparams, http:Request request) # Function handler type. type FunctionHandler (function (HandlerParams) returns error?); -@untainted public listener http:Listener hl = new(check ints:fromString(os:getEnv("FUNCTIONS_CUSTOMHANDLER_PORT"))); +@untainted public listener http:Listener hl = new(9000); public isolated function handleFunctionResposne(error? err, HandlerParams hparams) { http:Request request = hparams.request; diff --git a/ballerina/cosmos_listener.bal b/ballerina/cosmos_listener.bal new file mode 100644 index 00000000..bcce0f6a --- /dev/null +++ b/ballerina/cosmos_listener.bal @@ -0,0 +1,41 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. +public class CosmosDBListener { + DispatcherService? httpService; + + public isolated function init() returns error? { + self.httpService = (); + } + + public function attach(CosmosService svc, string[]|string? name = ()) returns error? { + AzureRemoteAdapter adaptor = new(svc); + self.httpService = new (adaptor, "onUpdated"); + check httpListener.attach(self.httpService, name); + } + + public isolated function detach(CosmosService svc) returns error? { + } + + public function 'start() returns error? { + check httpListener.'start(); + } + + public isolated function gracefulStop() returns error? { + } + + public isolated function immediateStop() returns error? { + } +} diff --git a/ballerina/dispatcher_service.bal b/ballerina/dispatcher_service.bal new file mode 100644 index 00000000..f764cf25 --- /dev/null +++ b/ballerina/dispatcher_service.bal @@ -0,0 +1,45 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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; +import ballerina/io; + +isolated service class DispatcherService { + *http:Service; + + private final AzureRemoteAdapter adaptor; + private final string remoteMethodName; + + isolated function init(AzureRemoteAdapter adaptor, string remoteMethodName) { + self.adaptor = adaptor; + self.remoteMethodName = remoteMethodName; + } + + isolated resource function post .(http:Caller caller, http:Request request) returns error? { + http:Response response = new; + json message = check request.getJsonPayload(); + io:println(message.toJsonString()); + map body = >check message.Data; + + map callRegisterMethod = check self.adaptor.callRemoteFunction(body, self.remoteMethodName); + json result = {Outputs: callRegisterMethod.toJson(), Logs: []}; + result = check result.mergeJson({ReturnValue: null}); + io:println(result); + response.setJsonPayload(result); + + check caller->respond(response); + } +} diff --git a/ballerina/http_listener.bal b/ballerina/http_listener.bal new file mode 100644 index 00000000..24d5f7ee --- /dev/null +++ b/ballerina/http_listener.bal @@ -0,0 +1,53 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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/io; + +public class HTTPListener { + ResourceService[] httpServices; + + public isolated function init() returns error? { + self.httpServices = []; + } + + public function attach(HttpService svc, string[]|string? name = ()) returns error? { + HttpToAzureAdaptor adaptor = new(svc); + string[] resList = adaptor.getAzureFunctionNames(); + foreach string resPath in resList{ + io:println(resPath); + ResourceService httpService = new (adaptor); + check httpListener.attach(httpService, resPath); + } + } + + public isolated function detach(HttpService svc) returns error? { + } + + public function 'start() returns error? { + check httpListener.'start(); + } + + public isolated function gracefulStop() returns error? { + } + + public isolated function immediateStop() returns error? { + } +} +public type HttpService distinct service object { + +}; + + diff --git a/ballerina/http_service.bal b/ballerina/http_service.bal new file mode 100644 index 00000000..4347fae5 --- /dev/null +++ b/ballerina/http_service.bal @@ -0,0 +1,48 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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; +import ballerina/io; + +isolated service class ResourceService { + *http:Service; + + private final HttpToAzureAdaptor adaptor; + + isolated function init(HttpToAzureAdaptor adaptor) { + self.adaptor = adaptor; + } + + isolated resource function post .(http:Caller caller, http:Request request) returns error? { + + http:Response response = new; + json message = check request.getJsonPayload(); + // map body = {}; + io:println(message.toJsonString()); + //TODO conver to record instead of map + Payload payload = check message.cloneWithType(Payload); + // io:println(payload); + // body = >check message.Data; + // string functionName = check message.Metadata.sys.MethodName; + string functionName = payload.Metadata.sys.MethodName; + map callRegisterMethod = check self.adaptor.callNativeMethod(payload.Data, functionName); + json result = {Outputs: callRegisterMethod.toJson(), Logs: []}; + result = check result.mergeJson({ReturnValue: null}); + io:println(result); + response.setJsonPayload(result); + check caller->respond(response); + } +} diff --git a/ballerina/init.bal b/ballerina/init.bal new file mode 100644 index 00000000..8d87a491 --- /dev/null +++ b/ballerina/init.bal @@ -0,0 +1,25 @@ +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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/jballerina.java; + +isolated function init() { + setModule(); +} + +isolated function setModule() = @java:Method { + 'class: "io.ballerina.stdlib.azure.functions.ModuleUtils" +} external; diff --git a/ballerina/natives.bal b/ballerina/natives.bal new file mode 100644 index 00000000..b7667e89 --- /dev/null +++ b/ballerina/natives.bal @@ -0,0 +1,52 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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/jballerina.java; + +isolated class HttpToAzureAdaptor { + isolated function init(HttpService 'service) { + externInit(self, 'service); + } + + isolated function getAzureFunctionNames() returns string[] = @java:Method { + 'class: "io.ballerina.stdlib.azure.functions.NativeHttpToAzureAdaptor" + } external; + + isolated function callNativeMethod(map body, string functionName) returns map|error = + @java:Method { + 'class: "io.ballerina.stdlib.azure.functions.NativeHttpToAzureAdaptor" + } external; +} + +isolated function externInit(HttpToAzureAdaptor adaptor, HttpService serviceObj) = @java:Method { + 'class: "io.ballerina.stdlib.azure.functions.NativeHttpToAzureAdaptor" +} external; + + + +isolated class AzureRemoteAdapter { + isolated function init(RemoteService 'service) { + externRemoteInit(self, 'service); + } + + isolated function callRemoteFunction(map body, string functionName) returns map|error = + @java:Method { + 'class: "io.ballerina.stdlib.azure.functions.NativeRemoteAdapter" + } external; +} + +isolated function externRemoteInit(AzureRemoteAdapter adaptor, RemoteService serviceObj) = @java:Method { + 'class: "io.ballerina.stdlib.azure.functions.NativeRemoteAdapter" +} external; diff --git a/ballerina/queue_listener.bal b/ballerina/queue_listener.bal new file mode 100644 index 00000000..4055ca6e --- /dev/null +++ b/ballerina/queue_listener.bal @@ -0,0 +1,41 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. +public class QueueListener { + DispatcherService? httpService; + + public isolated function init() returns error? { + self.httpService = (); + } + + public function attach(QueueService svc, string[]|string? name = ()) returns error? { + AzureRemoteAdapter adaptor = new(svc); + self.httpService = new (adaptor, "onMessage"); + check httpListener.attach(self.httpService, name); + } + + public isolated function detach(QueueService svc) returns error? { + } + + public function 'start() returns error? { + check httpListener.'start(); + } + + public isolated function gracefulStop() returns error? { + } + + public isolated function immediateStop() returns error? { + } +} diff --git a/ballerina/service_types.bal b/ballerina/service_types.bal new file mode 100644 index 00000000..26a1c94e --- /dev/null +++ b/ballerina/service_types.bal @@ -0,0 +1,23 @@ +public type RemoteService QueueService|CosmosService|TimerService|BlobService; + + +public type QueueService distinct service object { + // remote function onMessage(anydata payload) returns anydata|error?; +}; + + +public type CosmosService distinct service object { + // remote function onUpdated(anydata payload) returns anydata|error?; +}; + + +public type TimerService distinct service object { + // remote function onTrigger(anydata payload) returns anydata|error?; +}; + + +public type BlobService distinct service object { + // remote function onTrigger(anydata payload) returns anydata|error?; +}; + + diff --git a/ballerina/timer_listener.bal b/ballerina/timer_listener.bal new file mode 100644 index 00000000..5842bdfc --- /dev/null +++ b/ballerina/timer_listener.bal @@ -0,0 +1,41 @@ +// Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. +public class TimerListener { + DispatcherService? httpService; + + public isolated function init() returns error? { + self.httpService = (); + } + + public function attach(TimerService svc, string[]|string? name = ()) returns error? { + AzureRemoteAdapter adaptor = new(svc); + self.httpService = new (adaptor, "onTrigger"); + check httpListener.attach(self.httpService, name); + } + + public isolated function detach(TimerService svc) returns error? { + } + + public function 'start() returns error? { + check httpListener.'start(); + } + + public isolated function gracefulStop() returns error? { + } + + public isolated function immediateStop() returns error? { + } +} diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml index a36361c7..d25a1946 100644 --- a/build-config/resources/Ballerina.toml +++ b/build-config/resources/Ballerina.toml @@ -2,3 +2,12 @@ org = "ballerinax" name = "azure_functions" version = "@toml.version@" + +[[platform.java11.dependency]] +path = "../native/build/libs/azure_functions-native-@project.version@.jar" + +#[[platform.java11.dependency]] +#path = "./lib/http-native-@stdlib.httpnative.version@.jar" + +[[platform.java11.dependency]] +path = "./lib/mime-native-@stdlib.mimenative.version@.jar" diff --git a/build-config/resources/CompilerPlugin.toml b/build-config/resources/CompilerPlugin.toml index 1c27583f..ec4888ad 100644 --- a/build-config/resources/CompilerPlugin.toml +++ b/build-config/resources/CompilerPlugin.toml @@ -1,6 +1,6 @@ [plugin] id = "azure-functions" -class = "org.ballerinax.azurefunctions.generator.AzureCompilerPlugin" +class = "org.ballerinax.azurefunctions.AzureCompilerPlugin" [[dependency]] path = "../compiler-plugin/build/libs/azure_functions-compiler-plugin-@project.version@.jar" diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/HandlerTest.java deleted file mode 100644 index 86c429e6..00000000 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/HandlerTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.test; - -import com.google.gson.JsonObject; -import io.ballerina.projects.CodeGeneratorResult; -import io.ballerina.projects.DiagnosticResult; -import io.ballerina.projects.Document; -import io.ballerina.projects.DocumentId; -import io.ballerina.projects.Module; -import io.ballerina.projects.Package; -import io.ballerina.projects.PackageCompilation; -import io.ballerina.projects.directory.BuildProject; -import org.ballerinax.azurefunctions.generator.test.utils.ParserTestUtils; -import org.testng.Assert; -import org.testng.annotations.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; - -/** - * Test case for checking generated source of handler. - */ -public class HandlerTest { - protected static final Path RESOURCE_DIRECTORY = Paths.get("src/test/resources/handlers/"); - @Test - public void testGeneratedHandlerSource() { - try { - BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("code")); - CodeGeneratorResult codeGeneratorResult = project.currentPackage().runCodeGeneratorPlugins(); - Package updatedPackage = codeGeneratorResult.updatedPackage().orElseThrow(); - PackageCompilation compilation = updatedPackage.getCompilation(); - DiagnosticResult diagnosticResult = compilation.diagnosticResult(); - Assert.assertFalse(diagnosticResult.hasErrors()); - Module module = project.currentPackage().getDefaultModule(); - Assert.assertEquals(module.documentIds().size(), 2); - for (DocumentId documentId : module.documentIds()) { - Document document = module.document(documentId); - if ("main.bal".equals(document.name())) { - continue; - } - - JsonObject assertJson = - ParserTestUtils.readAssertFile(RESOURCE_DIRECTORY.resolve("generated").resolve("generated" + - ".json")); - - // Validate the tree against the assertion file - ParserTestUtils.assertNode(document.syntaxTree().rootNode().internalNode(), assertJson); - } - } catch (Exception e) { - Assert.fail(e.getMessage()); - } - } -} diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/DeploymentTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java similarity index 92% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/DeploymentTest.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java index 8985734a..4dda6f1d 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/DeploymentTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java @@ -15,10 +15,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test; +package org.ballerinax.azurefunctions.test; -import org.ballerinax.azurefunctions.generator.test.utils.ProcessOutput; -import org.ballerinax.azurefunctions.generator.test.utils.TestUtils; +import org.ballerinax.azurefunctions.test.utils.ProcessOutput; +import org.ballerinax.azurefunctions.test.utils.TestUtils; import org.testng.Assert; import org.testng.annotations.Test; diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java new file mode 100644 index 00000000..89ae3294 --- /dev/null +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.test; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.ballerina.projects.CodeModifierResult; +import io.ballerina.projects.DiagnosticResult; +import io.ballerina.projects.Package; +import io.ballerina.projects.PackageCompilation; +import io.ballerina.projects.directory.BuildProject; +import org.ballerinax.azurefunctions.AzureFunctionServiceExtractor; +import org.ballerinax.azurefunctions.FunctionContext; +import org.ballerinax.azurefunctions.service.Binding; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Test case for checking generated source of handler. + */ +public class HandlerTest { + + public static final Path RESOURCE_DIRECTORY = Paths.get("src/test/resources/handlers/"); + + private JsonParser jsonParser = new JsonParser(); + private Map generatedFunctions = new HashMap<>(); + + @BeforeClass + public void compileSample() { + BuildProject project = BuildProject.load(RESOURCE_DIRECTORY); + CodeModifierResult codeModifierResult = project.currentPackage().runCodeModifierPlugins(); + Package updatedPackage = codeModifierResult.updatedPackage().orElseThrow(); + PackageCompilation compilation = updatedPackage.getCompilation(); + + AzureFunctionServiceExtractor azureFunctionServiceExtractor = + new AzureFunctionServiceExtractor(updatedPackage); + List functionContexts = azureFunctionServiceExtractor.extractFunctions(); + + for (FunctionContext ctx : functionContexts) { + JsonObject functions = new JsonObject(); + JsonArray bindings = new JsonArray(); + List bindingList = ctx.getBindingList(); + for (Binding binding : bindingList) { + bindings.add(binding.getJsonObject()); + } + functions.add("bindings", bindings); + generatedFunctions.put(ctx.getFunctionName(), functions); + } + + DiagnosticResult diagnosticResult = compilation.diagnosticResult(); + Assert.assertFalse(diagnosticResult.hasErrors()); + Assert.assertEquals(generatedFunctions.size(), 14); + } + + @Test + public void testHttpHello() { + JsonObject httpHello = generatedFunctions.get("post-hello"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello\"}," + + "{\"type\":\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(httpHello, parse); + } + + @Test + public void testHttpHelloFoo() { + JsonObject actual = generatedFunctions.get("post-hello-foo"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello/foo\"},{\"type\":\"http\"," + + "\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + + @Test + public void testHttpHelloFooParam() { + JsonObject actual = generatedFunctions.get("post-hello-foo-0"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello/foo/{bar}\"}," + + "{\"type\":\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + + @Test + public void testQueueTrigger() { + JsonObject actual = generatedFunctions.get("queue"); + String str = + "{\"bindings\":[{\"type\":\"queueTrigger\",\"connection\":\"AzureWebJobsStorage\"," + + "\"queueName\":\"queue2\",\"direction\":\"in\",\"name\":\"inMsg\"},{\"type\":\"queue\"," + + "\"connection\":\"AzureWebJobsStorage\",\"queueName\":\"queue3\",\"direction\":\"out\"," + + "\"name\":\"outMsg\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + + @Test + public void testCosmosTrigger() { + JsonObject actual = generatedFunctions.get("cosmos"); + String str = + "{\"bindings\":[{\"type\":\"cosmosDBTrigger\",\"connectionStringSetting\":\"CosmosDBConnection\"," + + "\"databaseName\":\"db1\",\"collectionName\":\"c2\",\"name\":\"inMsg\",\"direction\":\"in\"," + + "\"createLeaseCollectionIfNotExists\":true,\"leasesCollectionThroughput\":400}," + + "{\"type\":\"queue\",\"connection\":\"AzureWebJobsStorage\",\"queueName\":\"queue3\"," + + "\"direction\":\"out\",\"name\":\"outMsg\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } +} diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/ProjectValidationTests.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java similarity index 98% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/ProjectValidationTests.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java index 69df834d..2810f281 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/ProjectValidationTests.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test; +package org.ballerinax.azurefunctions.test; import io.ballerina.projects.DiagnosticResult; import io.ballerina.projects.PackageCompilation; diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ParserTestConstants.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ParserTestConstants.java similarity index 95% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ParserTestConstants.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ParserTestConstants.java index cf621291..228a87be 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ParserTestConstants.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ParserTestConstants.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test.utils; +package org.ballerinax.azurefunctions.test.utils; /** * Constants related to the parser. diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ParserTestUtils.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ParserTestUtils.java similarity index 98% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ParserTestUtils.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ParserTestUtils.java index 8e824622..4c8c0861 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ParserTestUtils.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ParserTestUtils.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test.utils; +package org.ballerinax.azurefunctions.test.utils; import com.google.gson.Gson; import com.google.gson.JsonArray; @@ -45,15 +45,15 @@ import java.util.Collection; import static io.ballerina.compiler.internal.syntax.SyntaxUtils.isSTNodePresent; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.CHILDREN_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.DIAGNOSTICS_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.HAS_DIAGNOSTICS; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.INVALID_NODE_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.IS_MISSING_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.KIND_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.LEADING_MINUTIAE; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.TRAILING_MINUTIAE; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.VALUE_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.CHILDREN_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.DIAGNOSTICS_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.HAS_DIAGNOSTICS; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.INVALID_NODE_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.IS_MISSING_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.KIND_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.LEADING_MINUTIAE; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.TRAILING_MINUTIAE; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.VALUE_FIELD; /** * Convenient methods for testing the parser. diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ProcessOutput.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ProcessOutput.java similarity index 96% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ProcessOutput.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ProcessOutput.java index ad47ac86..602f9256 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/ProcessOutput.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/ProcessOutput.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test.utils; +package org.ballerinax.azurefunctions.test.utils; /** * Represents a Java process output. diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/SyntaxTreeJSONGenerator.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/SyntaxTreeJSONGenerator.java similarity index 88% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/SyntaxTreeJSONGenerator.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/SyntaxTreeJSONGenerator.java index 13367ccf..9f0008c7 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/SyntaxTreeJSONGenerator.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/SyntaxTreeJSONGenerator.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test.utils; +package org.ballerinax.azurefunctions.test.utils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -41,15 +41,15 @@ import java.nio.file.Paths; import java.util.Collection; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.CHILDREN_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.DIAGNOSTICS_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.HAS_DIAGNOSTICS; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.INVALID_NODE_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.IS_MISSING_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.KIND_FIELD; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.LEADING_MINUTIAE; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.TRAILING_MINUTIAE; -import static org.ballerinax.azurefunctions.generator.test.utils.ParserTestConstants.VALUE_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.CHILDREN_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.DIAGNOSTICS_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.HAS_DIAGNOSTICS; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.INVALID_NODE_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.IS_MISSING_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.KIND_FIELD; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.LEADING_MINUTIAE; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.TRAILING_MINUTIAE; +import static org.ballerinax.azurefunctions.test.utils.ParserTestConstants.VALUE_FIELD; /** * Generates a JSON that represents the structure of the syntax tree. This JSON diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/TestUtils.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/TestUtils.java similarity index 98% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/TestUtils.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/TestUtils.java index 4b12596c..d83c1fa5 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/generator/test/utils/TestUtils.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/utils/TestUtils.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator.test.utils; +package org.ballerinax.azurefunctions.test.utils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; diff --git a/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml b/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml new file mode 100644 index 00000000..71ce6b06 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "ballerinax" +name = "azure_functions_tests" +version = "3.0.0" diff --git a/compiler-plugin-tests/src/test/resources/handlers/code/Ballerina.toml b/compiler-plugin-tests/src/test/resources/handlers/code/Ballerina.toml deleted file mode 100644 index 4e552d98..00000000 --- a/compiler-plugin-tests/src/test/resources/handlers/code/Ballerina.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -org = "anjana" -name = "proj" -version = "0.1.0" - -[build-options] -observabilityIncluded = true \ No newline at end of file diff --git a/compiler-plugin-tests/src/test/resources/handlers/code/main.bal b/compiler-plugin-tests/src/test/resources/handlers/code/main.bal deleted file mode 100644 index a372656d..00000000 --- a/compiler-plugin-tests/src/test/resources/handlers/code/main.bal +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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/uuid; -import ballerinax/azure_functions as af; - -// HTTP request/response with no authentication -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { - return "Hello, " + payload + "!"; -} - -// HTTP request to add data to a queue -@af:Function -public isolated function fromHttpToQueue(af:Context ctx, - @af:HTTPTrigger {} af:HTTPRequest req, - @af:QueueOutput { queueName: "queue1" } af:StringOutputBinding msg) - returns @af:HTTPOutput af:HTTPBinding { - msg.value = req.body; - return { statusCode: 200, payload: "Request: " + req.toString() }; -} - -// A message put to a queue is copied to another queue -@af:Function -public isolated function fromQueueToQueue(af:Context ctx, - @af:QueueTrigger { queueName: "queue2" } string inMsg, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - ctx.log("In Message: " + inMsg); - ctx.log("Metadata: " + ctx.metadata.toString()); - outMsg.value = inMsg; -} - -// // A blob added to a container is copied to a queue -@af:Function -public isolated function fromBlobToQueue(af:Context ctx, - @af:BlobTrigger { path: "bpath1/{name}" } byte[] blobIn, - @af:BindingName { } string name, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) - returns error? { - outMsg.value = "Name: " + name + " Content: " + blobIn.toString(); -} - -// // HTTP request to read a blob value -@af:Function -public isolated function httpTriggerBlobInput(@af:HTTPTrigger { } af:HTTPRequest req, - @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) - returns @af:HTTPOutput string { - int length = 0; - if blobIn is byte[] { - length = blobIn.length(); - } - return "Blob: " + req.query["name"].toString() + " Length: " + - length.toString() + " Content: " + blobIn.toString(); -} - -// // HTTP request to add a new blob -@af:Function -public isolated function httpTriggerBlobOutput(@af:HTTPTrigger { } af:HTTPRequest req, - @af:BlobOutput { path: "bpath1/{Query.name}" } af:StringOutputBinding bb) - returns @af:HTTPOutput string|error { - bb.value = req.body; - return "Blob: " + req.query["name"].toString() + " Content: " + - bb?.value.toString(); -} - -// // Sending an SMS -@af:Function -public isolated function sendSMS(@af:HTTPTrigger { } af:HTTPRequest req, - @af:TwilioSmsOutput { fromNumber: "+12069845840" } - af:TwilioSmsOutputBinding tb) - returns @af:HTTPOutput string { - tb.to = req.query["to"].toString(); - tb.body = req.body.toString(); - return "Message - to: " + tb?.to.toString() + " body: " + tb?.body.toString(); -} - -public type Person record { - string id; - string name; - string country; -}; - -// // CosmosDB record trigger -@af:Function -public isolated function cosmosDBToQueue1(@af:CosmosDBTrigger { - connectionStringSetting: "CosmosDBConnection", databaseName: "db1", - collectionName: "c1" } Person[] req, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - outMsg.value = req.toString(); -} - -@af:Function -public isolated function cosmosDBToQueue2(@af:CosmosDBTrigger { - connectionStringSetting: "CosmosDBConnection", databaseName: "db1", - collectionName: "c2" } json req, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - outMsg.value = req.toString(); -} - -// // HTTP request to read CosmosDB records -@af:Function -public isolated function httpTriggerCosmosDBInput1( - @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - id: "{Query.id}", partitionKey: "{Query.country}" } json dbReq) - returns @af:HTTPOutput string|error { - return dbReq.toString(); -} - -@af:Function -public isolated function httpTriggerCosmosDBInput2( - @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - id: "{Query.id}", partitionKey: "{Query.country}" } Person? dbReq) - returns @af:HTTPOutput string|error { - return dbReq.toString(); -} - -@af:Function -public isolated function httpTriggerCosmosDBInput3( - @af:HTTPTrigger { route: "c1/{country}" } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - sqlQuery: "select * from c1 where c1.country = {country}" } - Person[] dbReq) - returns @af:HTTPOutput string|error { - return dbReq.toString(); -} - -// // HTTP request to write records to CosmosDB -@af:Function -public isolated function httpTriggerCosmosDBOutput1( - @af:HTTPTrigger { } af:HTTPRequest httpReq, @af:HTTPOutput af:HTTPBinding hb) - returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } json { - json entry = { id: uuid:createType1AsString(), name: "Saman", country: "Sri Lanka" }; - hb.payload = "Adding entry: " + entry.toString(); - return entry; -} - -@af:Function -public isolated function httpTriggerCosmosDBOutput2( - @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:HTTPOutput af:HTTPBinding hb) - returns @af:CosmosDBOutput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } json { - json entry = [{ id: uuid:createType1AsString(), name: "John Doe A", country: "USA" }, - { id: uuid:createType1AsString(), name: "John Doe B", country: "USA" }]; - hb.payload = "Adding entries: " + entry.toString(); - return entry; -} - -@af:Function -public isolated function httpTriggerCosmosDBOutput3( - @af:HTTPTrigger { } af:HTTPRequest httpReq) - returns @af:CosmosDBOutput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } Person[] { - Person[] persons = []; - persons.push({id: uuid:createType1AsString(), name: "Jack", country: "UK"}); - persons.push({id: uuid:createType1AsString(), name: "Will", country: "UK"}); - return persons; -} - -// // A timer function which is executed every 10 seconds. -@af:Function -public isolated function queuePopulationTimer( - @af:TimerTrigger { schedule: "*/10 * * * * *" } json triggerInfo, - @af:QueueOutput { queueName: "queue4" } af:StringOutputBinding msg) { - msg.value = triggerInfo.toString(); -} diff --git a/compiler-plugin-tests/src/test/resources/handlers/generated/generated.bal b/compiler-plugin-tests/src/test/resources/handlers/generated/generated.bal deleted file mode 100644 index 2a33cd11..00000000 --- a/compiler-plugin-tests/src/test/resources/handlers/generated/generated.bal +++ /dev/null @@ -1,82 +0,0 @@ -import ballerinax/azure_functions as af; -import ballerina/http; -public listener http:Listener __testListener=af:hl ;type PersonOptionalGenerated Person? ;type PersonArrayGenerated Person[] ;service http:Service / on __testListener {isolated resource function 'default hello (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.helloHandler(params),params);check caller->respond(response);}public isolated function helloHandler (af:HandlerParams params)returns error? { -string v1=check hello(check af:getBodyFromHTTPInputData(params,"payload")); -_=check af:setStringReturn(params,v1); -}isolated resource function 'default fromHttpToQueue (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.fromHttpToQueueHandler(params),params);check caller->respond(response);}public isolated function fromHttpToQueueHandler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -af:HTTPBinding v2=fromHttpToQueue(check af:createContext(params,true),check af:getHTTPRequestFromInputData(params,"req"),v1); -_=check af:setStringOutput(params,"msg",v1); -_=check af:setHTTPReturn(params,v2); -}isolated resource function 'default fromQueueToQueue (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.fromQueueToQueueHandler(params),params);check caller->respond(response);}public isolated function fromQueueToQueueHandler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -_=fromQueueToQueue(check af:createContext(params,true),check af:getJsonStringFromInputData(params,"inMsg"),v1); -_=check af:setStringOutput(params,"outMsg",v1); -}isolated resource function 'default fromBlobToQueue (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.fromBlobToQueueHandler(params),params);check caller->respond(response);}public isolated function fromBlobToQueueHandler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -_=check fromBlobToQueue(check af:createContext(params,true),check af:getBytesFromInputData(params,"blobIn"),check af:getStringFromMetadata(params,"name"),v1); -_=check af:setStringOutput(params,"outMsg",v1); -}isolated resource function 'default httpTriggerBlobInput (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerBlobInputHandler(params),params);check caller->respond(response);}public isolated function httpTriggerBlobInputHandler (af:HandlerParams params)returns error? { -string v1=httpTriggerBlobInput(check af:getHTTPRequestFromInputData(params,"req"),check af:getOptionalBytesFromInputData(params,"blobIn")); -_=check af:setStringReturn(params,v1); -}isolated resource function 'default httpTriggerBlobOutput (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerBlobOutputHandler(params),params);check caller->respond(response);}public isolated function httpTriggerBlobOutputHandler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -string v2=check httpTriggerBlobOutput(check af:getHTTPRequestFromInputData(params,"req"),v1); -_=check af:setBlobOutput(params,"bb",v1); -_=check af:setStringReturn(params,v2); -}isolated resource function 'default sendSMS (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.sendSMSHandler(params),params);check caller->respond(response);}public isolated function sendSMSHandler (af:HandlerParams params)returns error? { -af:TwilioSmsOutputBinding v1={}; -string v2=sendSMS(check af:getHTTPRequestFromInputData(params,"req"),v1); -_=check af:setTwilioSmsOutput(params,"tb",v1); -_=check af:setStringReturn(params,v2); -}isolated resource function 'default cosmosDBToQueue1 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.cosmosDBToQueue1Handler(params),params);check caller->respond(response);}public isolated function cosmosDBToQueue1Handler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -_=cosmosDBToQueue1(check af:getBallerinaValueFromInputData(params,"req",PersonArrayGenerated),v1); -_=check af:setStringOutput(params,"outMsg",v1); -}isolated resource function 'default cosmosDBToQueue2 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.cosmosDBToQueue2Handler(params),params);check caller->respond(response);}public isolated function cosmosDBToQueue2Handler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -_=cosmosDBToQueue2(check af:getJsonFromInputData(params,"req"),v1); -_=check af:setStringOutput(params,"outMsg",v1); -}isolated resource function 'default httpTriggerCosmosDBInput1 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerCosmosDBInput1Handler(params),params);check caller->respond(response);}public isolated function httpTriggerCosmosDBInput1Handler (af:HandlerParams params)returns error? { -string v1=check httpTriggerCosmosDBInput1(check af:getHTTPRequestFromInputData(params,"httpReq"),check af:getParsedJsonFromJsonStringFromInputData(params,"dbReq")); -_=check af:setStringReturn(params,v1); -}isolated resource function 'default httpTriggerCosmosDBInput2 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerCosmosDBInput2Handler(params),params);check caller->respond(response);}public isolated function httpTriggerCosmosDBInput2Handler (af:HandlerParams params)returns error? { -string v1=check httpTriggerCosmosDBInput2(check af:getHTTPRequestFromInputData(params,"httpReq"),check af:getOptionalBallerinaValueFromInputData(params,"dbReq",PersonOptionalGenerated)); -_=check af:setStringReturn(params,v1); -}isolated resource function 'default httpTriggerCosmosDBInput3 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerCosmosDBInput3Handler(params),params);check caller->respond(response);}public isolated function httpTriggerCosmosDBInput3Handler (af:HandlerParams params)returns error? { -string v1=check httpTriggerCosmosDBInput3(check af:getHTTPRequestFromInputData(params,"httpReq"),< Person[] >check af:getBallerinaValueFromInputData(params,"dbReq",PersonArrayGenerated)); -_=check af:setStringReturn(params,v1); -}isolated resource function 'default httpTriggerCosmosDBOutput1 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerCosmosDBOutput1Handler(params),params);check caller->respond(response);}public isolated function httpTriggerCosmosDBOutput1Handler (af:HandlerParams params)returns error? { -af:HTTPBinding v1={}; -json v2=httpTriggerCosmosDBOutput1(check af:getHTTPRequestFromInputData(params,"httpReq"),v1); -_=check af:setHTTPOutput(params,"hb",v1); -_=check af:setJsonReturn(params,v2); -}isolated resource function 'default httpTriggerCosmosDBOutput2 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerCosmosDBOutput2Handler(params),params);check caller->respond(response);}public isolated function httpTriggerCosmosDBOutput2Handler (af:HandlerParams params)returns error? { -af:HTTPBinding v1={}; -json v2=httpTriggerCosmosDBOutput2(check af:getHTTPRequestFromInputData(params,"httpReq"),v1); -_=check af:setHTTPOutput(params,"hb",v1); -_=check af:setJsonReturn(params,v2); -}isolated resource function 'default httpTriggerCosmosDBOutput3 (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.httpTriggerCosmosDBOutput3Handler(params),params);check caller->respond(response);}public isolated function httpTriggerCosmosDBOutput3Handler (af:HandlerParams params)returns error? { -Person[] v1=httpTriggerCosmosDBOutput3(check af:getHTTPRequestFromInputData(params,"httpReq")); -_=check af:setBallerinaValueAsJsonReturn(params,v1); -}isolated resource function 'default queuePopulationTimer (http:Caller caller,http:Request request)returns error? { -http:Response response =new;af:HandlerParams params ={request,response};af:handleFunctionResposne(trap self.queuePopulationTimerHandler(params),params);check caller->respond(response);}public isolated function queuePopulationTimerHandler (af:HandlerParams params)returns error? { -af:StringOutputBinding v1={}; -_=queuePopulationTimer(check af:getJsonFromInputData(params,"triggerInfo"),v1); -_=check af:setStringOutput(params,"msg",v1); -}} diff --git a/compiler-plugin-tests/src/test/resources/handlers/generated/generated.json b/compiler-plugin-tests/src/test/resources/handlers/generated/generated.json deleted file mode 100644 index fc4ff2f7..00000000 --- a/compiler-plugin-tests/src/test/resources/handlers/generated/generated.json +++ /dev/null @@ -1,18777 +0,0 @@ -{ - "kind": "MODULE_PART", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "IMPORT_DECLARATION", - "children": [ - { - "kind": "IMPORT_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IMPORT_ORG_NAME", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "ballerinax" - }, - { - "kind": "SLASH_TOKEN" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "azure_functions", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IMPORT_PREFIX", - "children": [ - { - "kind": "AS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "IMPORT_DECLARATION", - "children": [ - { - "kind": "IMPORT_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IMPORT_ORG_NAME", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "ballerina" - }, - { - "kind": "SLASH_TOKEN" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LISTENER_DECLARATION", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LISTENER_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Listener", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "__testListener" - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "hl", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "TYPE_DEFINITION", - "children": [ - { - "kind": "TYPE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "PersonOptionalGenerated", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "Person" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "TYPE_DEFINITION", - "children": [ - { - "kind": "TYPE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "PersonArrayGenerated", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ARRAY_TYPE_DESC", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "Person" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "ARRAY_DIMENSION", - "children": [ - { - "kind": "OPEN_BRACKET_TOKEN" - }, - { - "kind": "CLOSE_BRACKET_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "SERVICE_DECLARATION", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "SERVICE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Service", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SLASH_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "ON_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "__testListener", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "hello", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "helloHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "helloHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "hello" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getBodyFromHTTPInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "payload" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromHttpToQueue", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromHttpToQueueHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromHttpToQueueHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HTTPBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromHttpToQueue" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "createContext" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "BOOLEAN_LITERAL", - "children": [ - { - "kind": "TRUE_KEYWORD" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "req" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "msg" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setHTTPReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromQueueToQueue", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromQueueToQueueHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromQueueToQueueHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromQueueToQueue" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "createContext" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "BOOLEAN_LITERAL", - "children": [ - { - "kind": "TRUE_KEYWORD" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getJsonStringFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "inMsg" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "outMsg" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromBlobToQueue", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromBlobToQueueHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromBlobToQueueHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "fromBlobToQueue" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "createContext" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "BOOLEAN_LITERAL", - "children": [ - { - "kind": "TRUE_KEYWORD" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getBytesFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "blobIn" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getStringFromMetadata" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "name" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "outMsg" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobInput", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobInputHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobInputHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobInput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "req" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getOptionalBytesFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "blobIn" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobOutput", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobOutputHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobOutputHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerBlobOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "req" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setBlobOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "bb" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "sendSMS", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "sendSMSHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "sendSMSHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "TwilioSmsOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "sendSMS" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "req" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setTwilioSmsOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "tb" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue1", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue1Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue1Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue1" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TYPE_CAST_EXPRESSION", - "children": [ - { - "kind": "LT_TOKEN" - }, - { - "kind": "TYPE_CAST_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "ARRAY_TYPE_DESC", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "Person" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "ARRAY_DIMENSION", - "children": [ - { - "kind": "OPEN_BRACKET_TOKEN" - }, - { - "kind": "CLOSE_BRACKET_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "GT_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getBallerinaValueFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "req" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "PersonArrayGenerated" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "outMsg" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue2", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue2Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue2Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "cosmosDBToQueue2" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getJsonFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "req" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "outMsg" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput1", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput1Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput1Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput1" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "httpReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getParsedJsonFromJsonStringFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "dbReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput2", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput2Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput2Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput2" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "httpReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TYPE_CAST_EXPRESSION", - "children": [ - { - "kind": "LT_TOKEN" - }, - { - "kind": "TYPE_CAST_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "Person" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "GT_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getOptionalBallerinaValueFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "dbReq" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "PersonOptionalGenerated" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput3", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput3Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput3Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "STRING_TYPE_DESC", - "children": [ - { - "kind": "STRING_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBInput3" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "httpReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TYPE_CAST_EXPRESSION", - "children": [ - { - "kind": "LT_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "TYPE_CAST_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "ARRAY_TYPE_DESC", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "Person" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "ARRAY_DIMENSION", - "children": [ - { - "kind": "OPEN_BRACKET_TOKEN" - }, - { - "kind": "CLOSE_BRACKET_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "GT_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getBallerinaValueFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "dbReq" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "PersonArrayGenerated" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput1", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput1Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput1Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HTTPBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "JSON_TYPE_DESC", - "children": [ - { - "kind": "JSON_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput1" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "httpReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setHTTPOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "hb" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setJsonReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput2", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput2Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput2Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HTTPBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "JSON_TYPE_DESC", - "children": [ - { - "kind": "JSON_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput2" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "httpReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setHTTPOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "hb" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setJsonReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v2" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput3", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput3Handler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput3Handler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "ARRAY_TYPE_DESC", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "Person" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "ARRAY_DIMENSION", - "children": [ - { - "kind": "OPEN_BRACKET_TOKEN" - }, - { - "kind": "CLOSE_BRACKET_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "httpTriggerCosmosDBOutput3" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getHTTPRequestFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "httpReq" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setBallerinaValueAsJsonReturn" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "RESOURCE_ACCESSOR_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "RESOURCE_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "\u0027default", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "queuePopulationTimer", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Caller", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Request", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "http" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "Response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "IMPLICIT_NEW_EXPRESSION", - "children": [ - { - "kind": "NEW_KEYWORD" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "request" - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "SPECIFIC_FIELD", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "CALL_STATEMENT", - "children": [ - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "handleFunctionResposne" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "TRAP_EXPRESSION", - "children": [ - { - "kind": "TRAP_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "METHOD_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "self" - } - ] - }, - { - "kind": "DOT_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "queuePopulationTimerHandler" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - }, - { - "kind": "ACTION_STATEMENT", - "children": [ - { - "kind": "CHECK_ACTION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "REMOTE_METHOD_CALL_ACTION", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "caller" - } - ] - }, - { - "kind": "RIGHT_ARROW_TOKEN" - }, - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "respond" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "response" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN" - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - }, - { - "kind": "OBJECT_METHOD_DEFINITION", - "children": [ - { - "kind": "LIST", - "children": [ - { - "kind": "PUBLIC_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "ISOLATED_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "FUNCTION_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "queuePopulationTimerHandler", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "FUNCTION_SIGNATURE", - "children": [ - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "REQUIRED_PARAM", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "HandlerParams", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - }, - { - "kind": "RETURN_TYPE_DESCRIPTOR", - "children": [ - { - "kind": "RETURNS_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "OPTIONAL_TYPE_DESC", - "children": [ - { - "kind": "ERROR_TYPE_DESC", - "children": [ - { - "kind": "ERROR_KEYWORD" - } - ] - }, - { - "kind": "QUESTION_MARK_TOKEN", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - } - ] - } - ] - }, - { - "kind": "FUNCTION_BODY_BLOCK", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - }, - { - "kind": "LIST", - "children": [ - { - "kind": "LOCAL_VAR_DECL", - "children": [ - { - "kind": "LIST", - "children": [] - }, - { - "kind": "TYPED_BINDING_PATTERN", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "StringOutputBinding", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - } - ] - }, - { - "kind": "CAPTURE_BINDING_PATTERN", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "MAPPING_CONSTRUCTOR", - "children": [ - { - "kind": "OPEN_BRACE_TOKEN" - }, - { - "kind": "LIST", - "children": [] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "queuePopulationTimer" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "getJsonFromInputData" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "triggerInfo" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - }, - { - "kind": "ASSIGNMENT_STATEMENT", - "children": [ - { - "kind": "WILDCARD_BINDING_PATTERN", - "children": [ - { - "kind": "UNDERSCORE_KEYWORD" - } - ] - }, - { - "kind": "EQUAL_TOKEN" - }, - { - "kind": "CHECK_EXPRESSION", - "children": [ - { - "kind": "CHECK_KEYWORD", - "trailingMinutiae": [ - { - "kind": "WHITESPACE_MINUTIAE", - "value": " " - } - ] - }, - { - "kind": "FUNCTION_CALL", - "children": [ - { - "kind": "QUALIFIED_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "af" - }, - { - "kind": "COLON_TOKEN" - }, - { - "kind": "IDENTIFIER_TOKEN", - "value": "setStringOutput" - } - ] - }, - { - "kind": "OPEN_PAREN_TOKEN" - }, - { - "kind": "LIST", - "children": [ - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "params" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "STRING_LITERAL", - "children": [ - { - "kind": "STRING_LITERAL_TOKEN", - "value": "msg" - } - ] - } - ] - }, - { - "kind": "COMMA_TOKEN" - }, - { - "kind": "POSITIONAL_ARG", - "children": [ - { - "kind": "SIMPLE_NAME_REFERENCE", - "children": [ - { - "kind": "IDENTIFIER_TOKEN", - "value": "v1" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_PAREN_TOKEN" - } - ] - } - ] - }, - { - "kind": "SEMICOLON_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN" - } - ] - } - ] - } - ] - }, - { - "kind": "CLOSE_BRACE_TOKEN", - "trailingMinutiae": [ - { - "kind": "END_OF_LINE_MINUTIAE", - "value": "\n" - } - ] - } - ] - } - ] - }, - { - "kind": "EOF_TOKEN" - } - ] -} diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal new file mode 100644 index 00000000..daf1e6ff --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -0,0 +1,87 @@ +import ballerinax/azure_functions as af; + +listener af:HTTPListener ep = new (); + +public type DBEntry record { + string id; +}; + +type Person record { + string name; + int age; +}; + +// @af:HTTPTest +service "hello" on ep { + resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from . path "; + } + + resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from foo path " + greeting; + } + + resource function post foo/[string bar](@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from foo param " + bar; + } + + resource function post foo/bar(@af:Payload string greeting) returns @af:HTTPOutput string { + return "Hello from foo bar res"; + } + + resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { + return "Hello from the query " + greeting + " " + name; + } + + resource function post db(@af:Payload string greeting, @af:CosmosDBInput { + connectionStringSetting: "CosmosDBConnection",databaseName: "db1", + collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:HTTPOutput string|error { + return "Hello " + greeting + input1[0].id; + } + + resource function post payload/jsonToRecord (@af:Payload Person greeting) returns @af:HTTPOutput string|error { + return "Hello from json to record " + greeting.name; + } + + resource function post payload/jsonToJson (@af:Payload json greeting) returns @af:HTTPOutput string|error { + string name = check greeting.name; + return "Hello from json to json "+ name; + } + + resource function post payload/xmlToXml (@af:Payload xml greeting) returns @af:HTTPOutput string|error { + return greeting.toJsonString(); + } + + resource function post payload/textToString (@af:Payload string greeting) returns @af:HTTPOutput string|error { + return greeting; + } + + resource function post payload/textToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + return string:fromBytes(greeting); + } + + resource function post payload/octaToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + return string:fromBytes(greeting); + } +} + +@af:QueueTrigger { + queueName: "queue2" +} +listener af:QueueListener queueListener = new af:QueueListener(); + +service "queue" on queueListener { + remote function onMessage (@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg; + } +} + +@af:CosmosDBTrigger {connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2"} +listener af:CosmosDBListener cosmosEp = new (); + +service "cosmos" on cosmosEp { + remote function onUpdated (@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } +} diff --git a/compiler-plugin-tests/src/test/resources/testng.xml b/compiler-plugin-tests/src/test/resources/testng.xml index 7c9f97a4..a3a40529 100644 --- a/compiler-plugin-tests/src/test/resources/testng.xml +++ b/compiler-plugin-tests/src/test/resources/testng.xml @@ -20,9 +20,9 @@ - - - + + + diff --git a/compiler-plugin/spotbugs-exclude.xml b/compiler-plugin/spotbugs-exclude.xml index 3df227e8..e312261a 100644 --- a/compiler-plugin/spotbugs-exclude.xml +++ b/compiler-plugin/spotbugs-exclude.xml @@ -29,7 +29,7 @@ - + diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java new file mode 100644 index 00000000..447213d2 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import io.ballerina.projects.plugins.CompilerLifecycleEventContext; +import io.ballerina.projects.plugins.CompilerLifecycleTask; +import org.ballerinax.azurefunctions.service.Binding; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Contains the code generation part of the azure functions. + * + * @since 2.0.0 + */ +public class AzureCodeGeneratedTask implements CompilerLifecycleTask { + + private static final PrintStream OUT = System.out; + + @Override + public void perform(CompilerLifecycleEventContext compilerLifecycleEventContext) { + + AzureFunctionServiceExtractor azureFunctionServiceExtractor = + new AzureFunctionServiceExtractor(compilerLifecycleEventContext.currentPackage()); + List functionContexts = azureFunctionServiceExtractor.extractFunctions(); + Map generatedFunctions = new HashMap<>(); + + for (FunctionContext ctx : functionContexts) { + JsonObject functions = new JsonObject(); + JsonArray bindings = new JsonArray(); + List bindingList = ctx.getBindingList(); + for (Binding binding : bindingList) { + bindings.add(binding.getJsonObject()); + } + functions.add("bindings", bindings); + generatedFunctions.put(ctx.getFunctionName(), functions); + } + + OUT.println("\t@azure_functions:Function: " + String.join(", ", generatedFunctions.keySet())); + Optional generatedArtifactPath = compilerLifecycleEventContext.getGeneratedArtifactPath(); + generatedArtifactPath.ifPresent(path -> { + try { + this.generateFunctionsArtifact(generatedFunctions, path); + } catch (IOException e) { + String msg = "Error generating Azure Functions: " + e.getMessage(); + OUT.println(msg); + throw new RuntimeException(msg, e); + } + OUT.println("\n\tExecute the below command to deploy Ballerina Azure Functions:"); + Path parent = path.getParent(); + if (parent != null) { + OUT.println( + "\taz functionapp deployment source config-zip -g -n " + + " --src " + parent.toString() + File.separator + + Constants.AZURE_FUNCS_OUTPUT_ZIP_FILENAME + "\n\n"); + } + }); + } + + private void generateFunctionsArtifact(Map functions, Path binaryPath) + throws IOException { + new FunctionsArtifact(functions, binaryPath).generate(Constants.AZURE_FUNCS_OUTPUT_ZIP_FILENAME); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeModifier.java new file mode 100644 index 00000000..19800c39 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeModifier.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; + +import io.ballerina.projects.plugins.CodeModifier; +import io.ballerina.projects.plugins.CodeModifierContext; + +/** + * {@code AzureCodeModifier} handles required code-modification for Azure Function Services. + */ +public class AzureCodeModifier extends CodeModifier { + @Override + public void init(CodeModifierContext codeModifierContext) { + codeModifierContext.addSourceModifierTask(new FunctionUpdaterTask()); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCompilerPlugin.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java similarity index 77% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCompilerPlugin.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java index 4433b976..301b849b 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCompilerPlugin.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; import io.ballerina.projects.plugins.CompilerPlugin; import io.ballerina.projects.plugins.CompilerPluginContext; @@ -28,8 +28,10 @@ public class AzureCompilerPlugin extends CompilerPlugin { @Override public void init(CompilerPluginContext pluginContext) { - pluginContext.addCodeAnalyzer(new AzureFunctionsCodeAnalyzer()); - pluginContext.addCodeGenerator(new AzureCodeGenerator()); +// pluginContext.addCodeAnalyzer(new AzureFunctionsCodeAnalyzer()); +// pluginContext.addCodeGenerator(new AzureCodeGenerator()); + //TODO add code anaylzer to validate listener annotations + pluginContext.addCodeModifier(new AzureCodeModifier()); pluginContext.addCompilerLifecycleListener(new AzureLifecycleListener()); } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionDiagnostics.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java similarity index 97% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionDiagnostics.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java index 88eb0dce..589f2b29 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionDiagnostics.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; import io.ballerina.tools.diagnostics.Diagnostic; import io.ballerina.tools.diagnostics.DiagnosticInfo; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java new file mode 100644 index 00000000..7b03eeec --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -0,0 +1,158 @@ +package org.ballerinax.azurefunctions; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.symbols.UnionTypeSymbol; +import io.ballerina.compiler.syntax.tree.AbstractNodeFactory; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.BasicLiteralNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.LiteralValueToken; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeFactory; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.TreeModifier; + +import java.util.Optional; + +/** + * Responsible for generating annotations for each function. + * + * @since 2.0.0 + */ +public class AzureFunctionModifier extends TreeModifier { + + private SemanticModel semanticModel; + private UniqueIDHolder holder; + private String modulePrefix; + + public AzureFunctionModifier(SemanticModel semanticModel) { + super(); + this.semanticModel = semanticModel; + this.holder = new UniqueIDHolder(); + this.modulePrefix = "af"; //TODO fixme + } + + @Override + public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclarationNode) { + String servicePath = resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + ExpressionNode listenerExpressionNode = serviceDeclarationNode.expressions().get(0); + Optional typeSymbol = semanticModel.typeOf(listenerExpressionNode); + if (typeSymbol.isEmpty()) { + return super.transform(serviceDeclarationNode); + } + TypeReferenceTypeSymbol typeSymbol1; + if (typeSymbol.get().typeKind() == TypeDescKind.UNION) { + UnionTypeSymbol union = (UnionTypeSymbol) typeSymbol.get(); + typeSymbol1 = (TypeReferenceTypeSymbol) union.memberTypeDescriptors().get(0); + + } else { + typeSymbol1 = (TypeReferenceTypeSymbol) typeSymbol.get(); + } + Optional name = typeSymbol1.definition().getName(); + if (name.isEmpty()) { + return super.transform(serviceDeclarationNode); + } + NodeList members = serviceDeclarationNode.members(); + if (!name.get().equals("HTTPListener")) { + return super.transform(serviceDeclarationNode); + } + NodeList newMembersList = NodeFactory.createNodeList(); + for (Node node : members) { + Optional modifiedMember = getModifiedMember(node, servicePath); + if (modifiedMember.isEmpty()) { + newMembersList = newMembersList.add(node); + } else { + newMembersList = newMembersList.add(modifiedMember.get()); + } + } + return new ServiceDeclarationNode.ServiceDeclarationNodeModifier(serviceDeclarationNode) + .withMembers(newMembersList).apply(); + } + + public Optional getModifiedMember(Node node, String servicePath) { + if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + return Optional.empty(); + } + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; + String method = functionDefinitionNode.functionName().text(); + StringBuilder resourcePath = new StringBuilder(); + resourcePath.append(servicePath); + for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { + if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { + resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); + continue; + } + if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { + resourcePath.append("/" + holder.getNextValue()); +// resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); + continue; + } + } + String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + Optional metadata = functionDefinitionNode.metadata(); + NodeList existingAnnotations = NodeFactory.createNodeList(); + MetadataNode metadataNode; + if (metadata.isPresent()) { + metadataNode = metadata.get(); + existingAnnotations = metadataNode.annotations(); + } else { + metadataNode = NodeFactory.createMetadataNode(null, existingAnnotations); + } + + //Create and add annotation + + NodeList modifiedAnnotations = existingAnnotations.add(getFunctionNameAnnotation(functionName)); + MetadataNode modifiedMetadata = + new MetadataNode.MetadataNodeModifier(metadataNode).withAnnotations(modifiedAnnotations).apply(); + FunctionDefinitionNode updatedFunctionNode = + new FunctionDefinitionNode.FunctionDefinitionNodeModifier(functionDefinitionNode) + .withMetadata(modifiedMetadata).apply(); + return Optional.of(updatedFunctionNode); + } + + public AnnotationNode getFunctionNameAnnotation(String functionName) { + QualifiedNameReferenceNode azureFunctionAnnotRef = + NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(modulePrefix), + NodeFactory.createToken(SyntaxKind.COLON_TOKEN), + NodeFactory.createIdentifierToken("AzureFunction")); + LiteralValueToken literalValueToken = + NodeFactory.createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, "\"" + functionName + + "\"", NodeFactory.createEmptyMinutiaeList(), AbstractNodeFactory + .createEmptyMinutiaeList()); + BasicLiteralNode basicLiteralNode = + NodeFactory.createBasicLiteralNode(SyntaxKind.STRING_LITERAL, literalValueToken); + SpecificFieldNode name = NodeFactory.createSpecificFieldNode(null, NodeFactory.createIdentifierToken("name"), + NodeFactory.createToken(SyntaxKind.COLON_TOKEN), basicLiteralNode); + SeparatedNodeList updatedFields = NodeFactory.createSeparatedNodeList(name); + MappingConstructorExpressionNode annotationValue = + NodeFactory.createMappingConstructorExpressionNode( + NodeFactory.createToken(SyntaxKind.OPEN_BRACE_TOKEN), updatedFields, + NodeFactory.createToken(SyntaxKind.CLOSE_BRACE_TOKEN)); + return NodeFactory.createAnnotationNode(NodeFactory.createToken(SyntaxKind.AT_TOKEN), azureFunctionAnnotRef, + annotationValue); + } + + public String resourcePathToString(NodeList nodes) { + StringBuilder out = new StringBuilder(); + for (Node node : nodes) { + if (node.kind() == SyntaxKind.STRING_LITERAL) { + BasicLiteralNode basicLiteralNode = (BasicLiteralNode) node; + out.append(basicLiteralNode.literalToken().text()); + } + } + return out.substring(1, out.toString().length() - 1); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionExtractor.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceExtractor.java similarity index 74% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionExtractor.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceExtractor.java index 128371a2..6b23da39 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionExtractor.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceExtractor.java @@ -15,9 +15,9 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.projects.Document; import io.ballerina.projects.DocumentId; @@ -32,23 +32,24 @@ * * @since 2.0.0 */ -public class AzureFunctionExtractor { +public class AzureFunctionServiceExtractor { private final Package currentPackage; - public AzureFunctionExtractor(Package currentPackage) { + public AzureFunctionServiceExtractor(Package currentPackage) { this.currentPackage = currentPackage; } - public List extractFunctions() { + public List extractFunctions() { Module module = this.currentPackage.getDefaultModule(); - List moduleFunctions = new ArrayList<>(); + List moduleFunctions = new ArrayList<>(); for (DocumentId documentId : module.documentIds()) { Document document = module.document(documentId); Node node = document.syntaxTree().rootNode(); - AzureFunctionVisitor azureFunctionVisitor = new AzureFunctionVisitor(); + SemanticModel semanticModel = module.getCompilation().getSemanticModel(); + AzureFunctionServiceVisitor azureFunctionVisitor = new AzureFunctionServiceVisitor(semanticModel); node.accept(azureFunctionVisitor); - moduleFunctions.addAll(azureFunctionVisitor.getFunctions()); + moduleFunctions.addAll(azureFunctionVisitor.getFunctionContexts()); } return moduleFunctions; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java new file mode 100644 index 00000000..bc514a09 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import org.ballerinax.azurefunctions.service.ServiceHandler; +import org.ballerinax.azurefunctions.service.TriggerBinding; + +import java.util.ArrayList; +import java.util.List; +/** + * Visitor for extracting Azure functions from a ballerina document. + * + * @since 2.0.0 + */ +public class AzureFunctionServiceVisitor extends NodeVisitor { + + // private String moduleName; + private List functionContexts; + private SemanticModel semanticModel; + + public AzureFunctionServiceVisitor(SemanticModel semanticModel) { + this.functionContexts = new ArrayList<>(); + this.semanticModel = semanticModel; + } + + @Override + public void visit(ModulePartNode modulePartNode) { + super.visit(modulePartNode); + } + +// @Override +// public void visit(ImportDeclarationNode importDeclarationNode) { +// if (importDeclarationNode.orgName().isEmpty()) { +// return; +// } +// String orgName = importDeclarationNode.orgName().get().orgName().text(); +// if (!Constants.AZURE_FUNCTIONS_PACKAGE_ORG.equals(orgName)) { +// return; +// } +// if (importDeclarationNode.moduleName().size() != 1) { +// return; +// } +// String moduleName = importDeclarationNode.moduleName().get(0).text(); +// if (Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(moduleName)) { +// this.moduleName = moduleName; +// } +// if (importDeclarationNode.prefix().isEmpty()) { +// return; +// } +// this.moduleName = importDeclarationNode.prefix().get().prefix().text(); +// } + + @Override + public void visit(ServiceDeclarationNode serviceDeclarationNode) { + TriggerBinding builder = ServiceHandler.getBuilder(serviceDeclarationNode, semanticModel); + List contexts = builder.getBindings(); + functionContexts.addAll(contexts); + } + + public List getFunctionContexts() { + return functionContexts; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsException.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java similarity index 96% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsException.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java index a32ce082..e48305ad 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsException.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; import io.ballerina.tools.diagnostics.Diagnostic; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureLifecycleListener.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureLifecycleListener.java similarity index 95% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureLifecycleListener.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureLifecycleListener.java index 9d05710e..6e8a0bdc 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureLifecycleListener.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureLifecycleListener.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; import io.ballerina.projects.plugins.CompilerLifecycleContext; import io.ballerina.projects.plugins.CompilerLifecycleListener; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java similarity index 81% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/Constants.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index 7f6ec089..12faa849 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; /** * Constants for Azure Functions. @@ -44,4 +44,13 @@ public class Constants { public static final String HTTP_IMPORT = "http"; public static final String AWS_FUNCTION_TYPE = "Function"; public static final String AZ_FUNCTION_PREFIX = "az-func"; + + public static final String ANNOTATION_HTTP_TRIGGER = "HttpTrigger"; + public static final String ANNOTATION_QUEUE_TRIGGER = "QueueTrigger"; + public static final String ANNOTATION_COSMOS_TRIGGER = "CosmosDBTrigger"; + public static final String ANNOTATION_TIMER_TRIGGER = "TimerTrigger"; + public static final String ANNOTATION_BLOB_TRIGGER = "BlobTrigger"; + + public static final String DIRECTION_IN = "in"; + public static final String DIRECTION_OUT = "out"; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java new file mode 100644 index 00000000..66a49679 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java @@ -0,0 +1,28 @@ +package org.ballerinax.azurefunctions; + +import org.ballerinax.azurefunctions.service.Binding; + +import java.util.List; + +/** + * Represents a the function.json structure. + * + * @since 2.0.0 + */ +public class FunctionContext { + private String functionName; + private List bindingList; + + public FunctionContext(String functionName, List bindingList) { + this.functionName = functionName; + this.bindingList = bindingList; + } + + public String getFunctionName() { + return functionName; + } + + public List getBindingList() { + return bindingList; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java new file mode 100644 index 00000000..799d46d8 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java @@ -0,0 +1,56 @@ +package org.ballerinax.azurefunctions; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.projects.Document; +import io.ballerina.projects.DocumentId; +import io.ballerina.projects.Module; +import io.ballerina.projects.ModuleId; +import io.ballerina.projects.plugins.ModifierTask; +import io.ballerina.projects.plugins.SourceModifierContext; +import io.ballerina.tools.diagnostics.DiagnosticSeverity; + +/** + * {@code FunctionUpdaterTask} modifies the source by adding required meta-info for the azure function service + * declarations. + */ +public class FunctionUpdaterTask implements ModifierTask { + + @Override + public void modify(SourceModifierContext context) { + boolean erroneousCompilation = context.compilation().diagnosticResult() + .diagnostics().stream() + .anyMatch(d -> DiagnosticSeverity.ERROR.equals(d.diagnosticInfo().severity())); + // if the compilation already contains any error, do not proceed + if (erroneousCompilation) { + return; + } + + Module module = context.currentPackage().getDefaultModule(); + SemanticModel semanticModel = module.getCompilation().getSemanticModel(); + for (DocumentId documentId : module.documentIds()) { + Document document = module.document(documentId); + ModulePartNode rootNode = document.syntaxTree().rootNode(); + AzureFunctionModifier azureFunctionVisitor = new AzureFunctionModifier(semanticModel); + Node newNode = rootNode.apply(azureFunctionVisitor); + SyntaxTree updatedSyntaxTree = document.syntaxTree().modifyWith(newNode); + context.modifySourceFile(updatedSyntaxTree.textDocument(), documentId); + } + + // for test files + for (ModuleId modId : context.currentPackage().moduleIds()) { + Module currentModule = context.currentPackage().module(modId); + for (DocumentId docId : currentModule.testDocumentIds()) { + Document document = module.document(docId); + ModulePartNode rootNode = document.syntaxTree().rootNode(); + AzureFunctionModifier azureFunctionVisitor = new AzureFunctionModifier(semanticModel); + Node newNode = rootNode.apply(azureFunctionVisitor); + SyntaxTree updatedSyntaxTree = document.syntaxTree().modifyWith(newNode); + context.modifyTestSourceFile(updatedSyntaxTree.textDocument(), docId); + } + } + + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java similarity index 99% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionsArtifact.java rename to compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index 17336280..ba7e0268 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; +package org.ballerinax.azurefunctions; import com.google.gson.Gson; import com.google.gson.GsonBuilder; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java new file mode 100644 index 00000000..6cd8da17 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java @@ -0,0 +1,15 @@ +package org.ballerinax.azurefunctions; + +/** + * Responsible for generating unique identifier for params. + * + * @since 2.0.0 + */ +public class UniqueIDHolder { + + private int currentIndex = 0; + + public int getNextValue() { + return this.currentIndex++; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java new file mode 100644 index 00000000..eca62c22 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -0,0 +1,68 @@ +package org.ballerinax.azurefunctions; + +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.syntax.tree.BasicLiteralNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.tools.text.LineRange; +import io.ballerina.tools.text.TextDocument; +import io.ballerina.tools.text.TextRange; + +import java.util.Optional; + +/** + * Contains the utilities required for the compiler extension. + * + * @since 2.0.0 + */ +public class Util { + + public static Optional extractValueFromAnnotationField(SpecificFieldNode fieldNode) { + Optional expressionNode = fieldNode.valueExpr(); + ExpressionNode expressionNode1 = expressionNode.get(); + if (expressionNode1.kind() == SyntaxKind.STRING_LITERAL) { + String text1 = ((BasicLiteralNode) expressionNode1).literalToken().text(); + return Optional.of(text1.substring(1, text1.length() - 1)); + } else if (expressionNode1.kind() == SyntaxKind.DECIMAL_INTEGER_LITERAL_TOKEN) { + String text1 = ((BasicLiteralNode) expressionNode1).literalToken().text(); + return Optional.of(text1); + } + return Optional.empty(); + } + + /** + * Find node of this symbol. + * + * @param symbol {@link Symbol} + * @return {@link NonTerminalNode} + */ + public static NonTerminalNode findNode(ServiceDeclarationNode serviceDeclarationNode, Symbol symbol) { + if (symbol.getLocation().isEmpty()) { + return null; + } + SyntaxTree syntaxTree = serviceDeclarationNode.syntaxTree(); + TextDocument textDocument = syntaxTree.textDocument(); + LineRange symbolRange = symbol.getLocation().get().lineRange(); + int start = textDocument.textPositionFrom(symbolRange.startLine()); + int end = textDocument.textPositionFrom(symbolRange.endLine()); + return ((ModulePartNode) syntaxTree.rootNode()).findNode(TextRange.from(start, end - start), true); + } + + public static String resourcePathToString(NodeList nodes) { + StringBuilder out = new StringBuilder(); + for (Node node : nodes) { + if (node.kind() == SyntaxKind.STRING_LITERAL) { + BasicLiteralNode basicLiteralNode = (BasicLiteralNode) node; + out.append(basicLiteralNode.literalToken().text()); + } + } + return out.substring(1, out.toString().length() - 1); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGeneratedTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGeneratedTask.java deleted file mode 100644 index 80b58b1e..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGeneratedTask.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; -import io.ballerina.projects.plugins.CompilerLifecycleEventContext; -import io.ballerina.projects.plugins.CompilerLifecycleTask; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; - -/** - * Contains the code generation part of the azure functions. - * - * @since 2.0.0 - */ -public class AzureCodeGeneratedTask implements CompilerLifecycleTask { - - private static final PrintStream OUT = System.out; - - @Override - public void perform(CompilerLifecycleEventContext compilerLifecycleEventContext) { - Path azfJson = compilerLifecycleEventContext.currentPackage().project().targetDir().resolve("azf.json"); - Gson gson = new Gson(); - try (FileReader file = new FileReader(azfJson.toAbsolutePath().toString(), - StandardCharsets.UTF_8)) { - Type map = new TypeToken>() { - }.getType(); - Map generatedFunctions = gson.fromJson(file, map); - file.close(); - Files.deleteIfExists(azfJson); - if (generatedFunctions.isEmpty()) { - // no azure functions, nothing else to do - return; - } - OUT.println("\t@azure_functions:Function: " + String.join(", ", generatedFunctions.keySet())); - Optional generatedArtifactPath = compilerLifecycleEventContext.getGeneratedArtifactPath(); - generatedArtifactPath.ifPresent(path -> { - try { - this.generateFunctionsArtifact(generatedFunctions, path); - } catch (IOException e) { - String msg = "Error generating Azure Functions: " + e.getMessage(); - OUT.println(msg); - throw new RuntimeException(msg, e); - } - OUT.println("\n\tExecute the below command to deploy Ballerina Azure Functions:"); - Path parent = path.getParent(); - if (parent != null) { - OUT.println( - "\taz functionapp deployment source config-zip -g -n " + - " --src " + parent.toString() + File.separator + - Constants.AZURE_FUNCS_OUTPUT_ZIP_FILENAME + "\n\n"); - } - }); - } catch (IOException e) { - OUT.println("Internal error occurred. Unable to read target/azf.json " + e.getMessage()); - } - } - - private void generateFunctionsArtifact(Map functions, Path binaryPath) - throws IOException { - new FunctionsArtifact(functions, binaryPath).generate(Constants.AZURE_FUNCS_OUTPUT_ZIP_FILENAME); - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodegenTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodegenTask.java deleted file mode 100644 index bba02eb7..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodegenTask.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.ModulePartNode; -import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import io.ballerina.projects.Module; -import io.ballerina.projects.Package; -import io.ballerina.projects.plugins.GeneratorTask; -import io.ballerina.projects.plugins.SourceGeneratorContext; -import io.ballerina.tools.diagnostics.DiagnosticFactory; -import io.ballerina.tools.diagnostics.DiagnosticInfo; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import io.ballerina.tools.diagnostics.Location; -import io.ballerina.tools.text.LinePosition; -import io.ballerina.tools.text.LineRange; -import io.ballerina.tools.text.TextDocument; -import io.ballerina.tools.text.TextDocuments; -import io.ballerina.tools.text.TextRange; - -import java.io.FileWriter; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * An {@code AnalysisTask} that is triggered for Cloud.toml validation. - * - * @since 1.0.0 - */ -public class AzureCodegenTask implements GeneratorTask { - - @Override - public void generate(SourceGeneratorContext sourceGeneratorContext) { - Package currentPackage = sourceGeneratorContext.currentPackage(); - AzureFunctionExtractor azureFunctionExtractor = new AzureFunctionExtractor(currentPackage); - List extractedFunctions = azureFunctionExtractor.extractFunctions(); - Module module = currentPackage.getDefaultModule(); - SemanticModel semanticModel = sourceGeneratorContext.compilation().getSemanticModel(module.moduleId()); - Map typeDefinitions = new HashMap<>(); - FunctionHandlerGenerator functionGenerator = new FunctionHandlerGenerator(semanticModel, typeDefinitions); - Map generatedFunctions = new LinkedHashMap<>(); - List contextList = new ArrayList<>(); - for (FunctionDefinitionNode function : extractedFunctions) { - try { - FunctionDeploymentContext context = functionGenerator.generateHandlerFunction(function); - contextList.add(context); - generatedFunctions.put(function.functionName().text(), context.getFunctionDefinition()); - } catch (AzureFunctionsException e) { - sourceGeneratorContext.reportDiagnostic(e.getDiagnostic()); - } - } - try { - writeObjectToJson(sourceGeneratorContext.currentPackage().project().targetDir(), generatedFunctions); - } catch (IOException e) { - DiagnosticInfo diagnosticInfo = new DiagnosticInfo("azf-001", "azf.json creation failed " - + e.getMessage(), DiagnosticSeverity.ERROR); - sourceGeneratorContext.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagnosticInfo, - new NullLocation())); - return; - } - if (generatedFunctions.isEmpty()) { - // no azure functions, nothing else to do - return; - } - TextDocument textDocument = generateHandlerDocument(typeDefinitions, contextList); - sourceGeneratorContext.addSourceFile(textDocument, Constants.AZ_FUNCTION_PREFIX, module.moduleId()); - } - - private TextDocument generateHandlerDocument(Map typeDefinitions, - List ctx) { - ModulePartNode modulePartNode = - GeneratorUtil.createModulePartNode(ctx, typeDefinitions); - return TextDocuments.from(modulePartNode.toSourceCode()); - } - - private void writeObjectToJson(Path targetPath, Map ctxMap) - throws IOException { - Gson gson = new Gson(); - Path jsonPath = targetPath.resolve("azf.json"); - Files.deleteIfExists(jsonPath); - Path targetDir = jsonPath.getParent(); - if (targetDir == null) { - return; - } - if (!Files.exists(targetDir)) { - Files.createDirectories(targetDir); - } - Files.createFile(jsonPath); - try (FileWriter r = new FileWriter(jsonPath.toAbsolutePath().toString(), StandardCharsets.UTF_8)) { - gson.toJson(ctxMap, r); - } - } -} - -/** - * Represents Null Location in a ballerina document. - * - * @since 2.0.0 - */ -class NullLocation implements Location { - - @Override - public LineRange lineRange() { - LinePosition from = LinePosition.from(0, 0); - return LineRange.from("", from, from); - } - - @Override - public TextRange textRange() { - return TextRange.from(0, 0); - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionVisitor.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionVisitor.java deleted file mode 100644 index 28425c29..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionVisitor.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; -import io.ballerina.compiler.syntax.tree.MetadataNode; -import io.ballerina.compiler.syntax.tree.ModulePartNode; -import io.ballerina.compiler.syntax.tree.NodeVisitor; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; - -import java.util.ArrayList; -import java.util.List; - -/** - * Visitor for extracting Azure functions from a ballerina document. - * - * @since 2.0.0 - */ -public class AzureFunctionVisitor extends NodeVisitor { - - private final List functions; - private String moduleName; - - public AzureFunctionVisitor() { - this.functions = new ArrayList<>(); - } - - @Override - public void visit(ModulePartNode modulePartNode) { - super.visit(modulePartNode); - } - - @Override - public void visit(ImportDeclarationNode importDeclarationNode) { - if (importDeclarationNode.orgName().isEmpty()) { - return; - } - String orgName = importDeclarationNode.orgName().get().orgName().text(); - if (!Constants.AZURE_FUNCTIONS_PACKAGE_ORG.equals(orgName)) { - return; - } - if (importDeclarationNode.moduleName().size() != 1) { - return; - } - String moduleName = importDeclarationNode.moduleName().get(0).text(); - if (Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(moduleName)) { - this.moduleName = moduleName; - } - if (importDeclarationNode.prefix().isEmpty()) { - return; - } - this.moduleName = importDeclarationNode.prefix().get().prefix().text(); - } - - @Override - public void visit(FunctionDefinitionNode functionDefinitionNode) { - if (this.moduleName == null) { - return; - } - if (functionDefinitionNode.metadata().isEmpty()) { - return; - } - - MetadataNode metadataNode = functionDefinitionNode.metadata().get(); - for (AnnotationNode annotation : metadataNode.annotations()) { - if (annotation.annotReference().kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { - continue; - } - QualifiedNameReferenceNode qualifiedNameReferenceNode = - (QualifiedNameReferenceNode) annotation.annotReference(); - String modulePrefix = qualifiedNameReferenceNode.modulePrefix().text(); - String identifier = qualifiedNameReferenceNode.identifier().text(); - if (modulePrefix.equals(this.moduleName) && Constants.AWS_FUNCTION_TYPE.equals(identifier)) { - functions.add(functionDefinitionNode); - } - } - } - - public List getFunctions() { - return functions; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsCodeAnalyzer.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsCodeAnalyzer.java deleted file mode 100644 index 50454506..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsCodeAnalyzer.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import io.ballerina.projects.plugins.CodeAnalysisContext; -import io.ballerina.projects.plugins.CodeAnalyzer; -import org.ballerinax.azurefunctions.generator.validators.AzureFunctionsCodeAnalyzerTask; - -/** - * Contains the code analyzers for azure functions. - * - * @since 2.0.0 - */ -public class AzureFunctionsCodeAnalyzer extends CodeAnalyzer { - - @Override - public void init(CodeAnalysisContext codeAnalysisContext) { - codeAnalysisContext.addCompilationAnalysisTask(new AzureFunctionsCodeAnalyzerTask()); - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsExtensionProvider.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsExtensionProvider.java deleted file mode 100644 index 4e559397..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureFunctionsExtensionProvider.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import org.ballerinalang.annotation.JavaSPIService; -import org.ballerinalang.spi.SystemPackageRepositoryProvider; -import org.wso2.ballerinalang.compiler.packaging.repo.JarRepo; -import org.wso2.ballerinalang.compiler.packaging.repo.Repo; - -/** - * This represents the Ballerina AzureFunctions extension package repository provider. - */ -@JavaSPIService("org.ballerinalang.spi.SystemPackageRepositoryProvider") -public class AzureFunctionsExtensionProvider implements SystemPackageRepositoryProvider { - - @SuppressWarnings("rawtypes") - @Override - public Repo loadRepository() { - return new JarRepo(SystemPackageRepositoryProvider.getClassUri(this)); - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionDeploymentContext.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionDeploymentContext.java deleted file mode 100644 index 598101f7..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionDeploymentContext.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; - -import java.util.ArrayList; -import java.util.List; - -/** - * Represents the Azure functions context. - */ -public class FunctionDeploymentContext { - - private static final String VAR_PREFIX = "v"; - private List parameterHandlers = new ArrayList<>(); - private ReturnHandler returnHandler; - private JsonObject functionDefinition; - private FunctionDefinitionNode sourceFunction; - private FunctionDefinitionNode function; - private int varCounter = 0; - private boolean isolatedFunction = false; - - public FunctionDeploymentContext() { - this.setFunctionDefinition(new JsonObject()); - this.getFunctionDefinition().add(Constants.FUNCTION_BINDINGS_NAME, new JsonArray()); - } - - public String getNextVarName() { - return VAR_PREFIX + (++varCounter); - } - - public List getParameterHandlers() { - return parameterHandlers; - } - - public void setParameterHandlers(List parameterHandlers) { - this.parameterHandlers = parameterHandlers; - } - - public ReturnHandler getReturnHandler() { - return returnHandler; - } - - public void setReturnHandler(ReturnHandler returnHandler) { - this.returnHandler = returnHandler; - } - - public JsonObject getFunctionDefinition() { - return functionDefinition; - } - - public void setFunctionDefinition(JsonObject functionDefinition) { - this.functionDefinition = functionDefinition; - } - - public FunctionDefinitionNode getSourceFunction() { - return sourceFunction; - } - - public void setSourceFunction(FunctionDefinitionNode sourceFunction) { - this.sourceFunction = sourceFunction; - } - - public FunctionDefinitionNode getFunction() { - return function; - } - - public void setFunction(FunctionDefinitionNode function) { - this.function = function; - } - - public boolean isIsolatedFunction() { - return isolatedFunction; - } - - public void setIsolatedFunction(boolean isolatedFunction) { - this.isolatedFunction = isolatedFunction; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionHandlerGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionHandlerGenerator.java deleted file mode 100644 index 2b328ea9..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/FunctionHandlerGenerator.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.FunctionSymbol; -import io.ballerina.compiler.api.symbols.FunctionTypeSymbol; -import io.ballerina.compiler.api.symbols.TypeDescKind; -import io.ballerina.compiler.api.symbols.TypeSymbol; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Responsible for generating intermediate handler functions for each azure function. - * - * @since 2.0.0 - */ -public class FunctionHandlerGenerator { - - private final SemanticModel semanticModel; - private final Map typeDefinitions; - - public FunctionHandlerGenerator(SemanticModel semanticModel, - Map generatedTypeDefinitions) { - this.semanticModel = semanticModel; - this.typeDefinitions = generatedTypeDefinitions; - } - - public FunctionDeploymentContext generateHandlerFunction(FunctionDefinitionNode sourceFunc) - throws AzureFunctionsException { - FunctionDeploymentContext ctx = this.createFuncDeplContext(sourceFunc); - List positionalArgumentNodes = new ArrayList<>(); - for (ParameterHandler ph : ctx.getParameterHandlers()) { - positionalArgumentNodes.add(NodeFactory.createPositionalArgumentNode(ph.invocationProcess())); - } - TypeDescriptorNode originalFunctionTypeDesc = - GeneratorUtil.getCheckedReturnTypeDescOfOriginalFunction(ctx.getSourceFunction()); - - boolean isCheckRequired = GeneratorUtil.isCheckingRequiredForOriginalFunction(ctx.getSourceFunction()); - String returnName = GeneratorUtil.addFunctionCallStatement(originalFunctionTypeDesc, ctx, - GeneratorUtil.createFunctionInvocationNode(sourceFunc.functionName().text(), - positionalArgumentNodes.toArray(new PositionalArgumentNode[0])), isCheckRequired); - - for (ParameterHandler ph : ctx.getParameterHandlers()) { - ph.postInvocationProcess(); - } - ReturnHandler returnHandler = ctx.getReturnHandler(); - if (returnHandler != null) { - returnHandler.postInvocationProcess(GeneratorUtil.createVariableRef(returnName)); - } - return ctx; - } - - private FunctionDeploymentContext createFuncDeplContext(FunctionDefinitionNode sourceFunc) - throws AzureFunctionsException { - FunctionDeploymentContext ctx = new FunctionDeploymentContext(); - ctx.setSourceFunction(sourceFunc); - ctx.setIsolatedFunction(GeneratorUtil.isIsolatedFunction(sourceFunc)); - ctx.setFunction(GeneratorUtil.createHandlerFunction(ctx)); - - for (ParameterNode parameter : sourceFunc.functionSignature().parameters()) { - ctx.getParameterHandlers() - .add(HandlerFactory.createParameterHandler(parameter, semanticModel, typeDefinitions)); - } - - for (ParameterHandler ph : ctx.getParameterHandlers()) { - ph.init(ctx); - } - FunctionTypeSymbol functionTypeSymbol = - ((FunctionSymbol) semanticModel.symbol(sourceFunc.functionName()).orElseThrow()).typeDescriptor(); - - Optional returnSymbolOptional = functionTypeSymbol.returnTypeDescriptor(); - if (returnSymbolOptional.isPresent() && !(returnSymbolOptional.get().typeKind() == TypeDescKind.NIL)) { - ctx.setReturnHandler(HandlerFactory.createReturnHandler(returnSymbolOptional.get(), - sourceFunc.functionSignature().returnTypeDesc().orElseThrow())); - if (ctx.getReturnHandler() != null) { - ctx.getReturnHandler().init(ctx); - } - } - return ctx; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/GeneratorUtil.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/GeneratorUtil.java deleted file mode 100644 index 800b837f..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/GeneratorUtil.java +++ /dev/null @@ -1,931 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonNull; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import io.ballerina.compiler.api.ModuleID; -import io.ballerina.compiler.api.symbols.AnnotationSymbol; -import io.ballerina.compiler.api.symbols.ArrayTypeSymbol; -import io.ballerina.compiler.api.symbols.ModuleSymbol; -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.api.symbols.TypeDescKind; -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.ArrayTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.AssignmentStatementNode; -import io.ballerina.compiler.syntax.tree.BasicLiteralNode; -import io.ballerina.compiler.syntax.tree.CaptureBindingPatternNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.ExpressionStatementNode; -import io.ballerina.compiler.syntax.tree.FunctionArgumentNode; -import io.ballerina.compiler.syntax.tree.FunctionBodyBlockNode; -import io.ballerina.compiler.syntax.tree.FunctionCallExpressionNode; -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; -import io.ballerina.compiler.syntax.tree.IdentifierToken; -import io.ballerina.compiler.syntax.tree.ImplicitNewExpressionNode; -import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; -import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode; -import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; -import io.ballerina.compiler.syntax.tree.MappingFieldNode; -import io.ballerina.compiler.syntax.tree.MinutiaeList; -import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; -import io.ballerina.compiler.syntax.tree.ModulePartNode; -import io.ballerina.compiler.syntax.tree.NilTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.OptionalTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; -import io.ballerina.compiler.syntax.tree.RemoteMethodCallActionNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.SeparatedNodeList; -import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.compiler.syntax.tree.SpecificFieldNode; -import io.ballerina.compiler.syntax.tree.StatementNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.Token; -import io.ballerina.compiler.syntax.tree.TrapExpressionNode; -import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import io.ballerina.compiler.syntax.tree.TypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.TypedBindingPatternNode; -import io.ballerina.compiler.syntax.tree.UnionTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.VariableDeclarationNode; -import io.ballerina.compiler.syntax.tree.WildcardBindingPatternNode; -import io.ballerina.tools.diagnostics.Diagnostic; -import io.ballerina.tools.diagnostics.DiagnosticInfo; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import io.ballerina.tools.diagnostics.Location; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Utility functions for Azure Functions. - * - * @since 2.0.0 - */ -public class GeneratorUtil { - - /** - * Generates boilerplate Handler function for a specific azure function. - * - * @param ctx function context - * @return boilerplate handler function - */ - public static FunctionDefinitionNode createHandlerFunction(FunctionDeploymentContext ctx) { - String baseName = ctx.getSourceFunction().functionName().text(); - QualifiedNameReferenceNode azHandlerParamsType = - NodeFactory - .createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.AF_IMPORT_ALIAS), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), NodeFactory - .createIdentifierToken("HandlerParams", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - RequiredParameterNode requiredParameterNode = - NodeFactory.createRequiredParameterNode(NodeFactory.createEmptyNodeList(), azHandlerParamsType, - NodeFactory.createIdentifierToken(Constants.REQUEST_PARAMS_NAME)); - OptionalTypeDescriptorNode optionalErrorTypeDescriptorNode = - NodeFactory.createOptionalTypeDescriptorNode( - NodeFactory.createParameterizedTypeDescriptorNode(SyntaxKind.ERROR_TYPE_DESC, - NodeFactory.createToken(SyntaxKind.ERROR_KEYWORD), null), - NodeFactory.createToken(SyntaxKind.QUESTION_MARK_TOKEN, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - ReturnTypeDescriptorNode returnTypeDescriptorNode = - NodeFactory.createReturnTypeDescriptorNode(NodeFactory - .createToken(SyntaxKind.RETURNS_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), - NodeFactory.createEmptyNodeList(), optionalErrorTypeDescriptorNode); - FunctionSignatureNode functionSignatureNode = - NodeFactory.createFunctionSignatureNode(NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createSeparatedNodeList(requiredParameterNode), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN), returnTypeDescriptorNode); - - FunctionBodyBlockNode emptyFunctionBodyNode = - NodeFactory.createFunctionBodyBlockNode( - NodeFactory.createToken(SyntaxKind.OPEN_BRACE_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline()), null, - NodeFactory.createEmptyNodeList(), NodeFactory.createToken(SyntaxKind.CLOSE_BRACE_TOKEN)); - - List qualifierList = new ArrayList<>(); - Token publicToken = NodeFactory.createToken(SyntaxKind.PUBLIC_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()); - qualifierList.add(publicToken); - - if (ctx.isIsolatedFunction()) { - Token isolatedToken = NodeFactory.createToken(SyntaxKind.ISOLATED_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()); - qualifierList.add(isolatedToken); - } - - return NodeFactory.createFunctionDefinitionNode( - SyntaxKind.FUNCTION_DEFINITION, null, NodeFactory.createNodeList(qualifierList), - NodeFactory.createToken(SyntaxKind.FUNCTION_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), NodeFactory.createIdentifierToken(baseName + - "Handler", NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()), - NodeFactory.createEmptyNodeList(), functionSignatureNode, emptyFunctionBodyNode); - } - - public static FunctionDefinitionNode createResourceFunction(FunctionDeploymentContext ctx) { - //TODO change isolated - String baseName = ctx.getSourceFunction().functionName().text(); - QualifiedNameReferenceNode httpCallerType = - NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.HTTP_IMPORT), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("Caller", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - RequiredParameterNode callerParamNode = - NodeFactory.createRequiredParameterNode(NodeFactory.createEmptyNodeList(), httpCallerType, - NodeFactory.createIdentifierToken(Constants.HTTP_CALLER_PARAMS_NAME)); - - QualifiedNameReferenceNode httpRequestType = - NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.HTTP_IMPORT), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("Request", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - RequiredParameterNode requestParamNode = - NodeFactory.createRequiredParameterNode(NodeFactory.createEmptyNodeList(), httpRequestType, - NodeFactory.createIdentifierToken(Constants.HTTP_REQUEST_PARAMS_NAME)); - - OptionalTypeDescriptorNode optionalErrorTypeDescriptorNode = - NodeFactory.createOptionalTypeDescriptorNode( - NodeFactory.createParameterizedTypeDescriptorNode(SyntaxKind.ERROR_TYPE_DESC, - NodeFactory.createToken(SyntaxKind.ERROR_KEYWORD), null), - NodeFactory.createToken(SyntaxKind.QUESTION_MARK_TOKEN, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - ReturnTypeDescriptorNode returnTypeDescriptorNode = - NodeFactory.createReturnTypeDescriptorNode(NodeFactory - .createToken(SyntaxKind.RETURNS_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), - NodeFactory.createEmptyNodeList(), optionalErrorTypeDescriptorNode); - FunctionSignatureNode functionSignatureNode = - NodeFactory.createFunctionSignatureNode(NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createSeparatedNodeList(callerParamNode, - NodeFactory.createToken(SyntaxKind.COMMA_TOKEN), requestParamNode), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN), returnTypeDescriptorNode); - - QualifiedNameReferenceNode httpResponseType = - NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.HTTP_IMPORT), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("Response", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - CaptureBindingPatternNode response = - NodeFactory.createCaptureBindingPatternNode( - NodeFactory.createIdentifierToken("response", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - TypedBindingPatternNode responseTypeBindingNode = - NodeFactory.createTypedBindingPatternNode(httpResponseType, response); - - ImplicitNewExpressionNode implicitNewExpressionNode = - NodeFactory.createImplicitNewExpressionNode(NodeFactory.createToken(SyntaxKind.NEW_KEYWORD), null); - - VariableDeclarationNode responseDeclNode = - NodeFactory.createVariableDeclarationNode(NodeFactory.createEmptyNodeList(), null, - responseTypeBindingNode, NodeFactory.createToken(SyntaxKind.EQUAL_TOKEN), - implicitNewExpressionNode, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - - QualifiedNameReferenceNode afHandlerParamType = - NodeFactory - .createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.AF_IMPORT_ALIAS), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken(Constants.REQUEST_PARAMS_TYPE, - NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - CaptureBindingPatternNode params = - NodeFactory.createCaptureBindingPatternNode( - NodeFactory.createIdentifierToken(Constants.REQUEST_PARAMS_NAME, - NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - TypedBindingPatternNode paramsTypeBindingNode = - NodeFactory.createTypedBindingPatternNode(afHandlerParamType, params); - - SpecificFieldNode requestParam = - NodeFactory.createSpecificFieldNode(null, NodeFactory.createIdentifierToken("request"), null, null); - - SpecificFieldNode responseParam = - NodeFactory.createSpecificFieldNode(null, NodeFactory.createIdentifierToken("response"), null, null); - - MappingConstructorExpressionNode argList = - NodeFactory.createMappingConstructorExpressionNode(NodeFactory.createToken(SyntaxKind.OPEN_BRACE_TOKEN), - NodeFactory - .createSeparatedNodeList(requestParam, NodeFactory.createToken(SyntaxKind.COMMA_TOKEN), - responseParam), NodeFactory.createToken(SyntaxKind.CLOSE_BRACE_TOKEN)); - - VariableDeclarationNode paramDeclNode = - NodeFactory.createVariableDeclarationNode(NodeFactory.createEmptyNodeList(), null, - paramsTypeBindingNode, NodeFactory.createToken(SyntaxKind.EQUAL_TOKEN), argList, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - TrapExpressionNode trappedHandlerCall = NodeFactory.createTrapExpressionNode(SyntaxKind.TRAP_EXPRESSION, - NodeFactory.createToken(SyntaxKind.TRAP_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace()), createMethodInvocationNode( - baseName + "Handler", paramsArg)); - PositionalArgumentNode handlerArg = NodeFactory.createPositionalArgumentNode(trappedHandlerCall); - - ExpressionStatementNode expressionStatementNode = - NodeFactory.createExpressionStatementNode(SyntaxKind.CALL_STATEMENT, createAfFunctionInvocationNode( - "handleFunctionResposne", false, handlerArg, paramsArg), - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - - SimpleNameReferenceNode caller = - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken("caller")); - SimpleNameReferenceNode respond = - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken("respond")); - SimpleNameReferenceNode responseVar = - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken("response")); - RemoteMethodCallActionNode remoteMethodCallActionNode = NodeFactory - .createRemoteMethodCallActionNode(caller, NodeFactory.createToken(SyntaxKind.RIGHT_ARROW_TOKEN), - respond, NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createSeparatedNodeList(NodeFactory.createPositionalArgumentNode(responseVar)), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN)); - ExpressionStatementNode checkedResponseExpr = - NodeFactory.createExpressionStatementNode(SyntaxKind.ACTION_STATEMENT, - NodeFactory.createCheckExpressionNode(SyntaxKind.CHECK_ACTION, - NodeFactory.createToken(SyntaxKind.CHECK_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil - .generateMinutiaeListWithWhitespace()), remoteMethodCallActionNode), - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - - FunctionBodyBlockNode functionBodyNode = - NodeFactory.createFunctionBodyBlockNode( - NodeFactory.createToken(SyntaxKind.OPEN_BRACE_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline()), null, - NodeFactory.createNodeList(responseDeclNode, paramDeclNode, expressionStatementNode, - checkedResponseExpr), - NodeFactory.createToken(SyntaxKind.CLOSE_BRACE_TOKEN)); - - NodeList relativeResPath = NodeFactory - .createNodeList(NodeFactory.createIdentifierToken(baseName, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - List qualifierList = new ArrayList<>(); - if (ctx.isIsolatedFunction()) { - Token isolatedToken = NodeFactory.createToken(SyntaxKind.ISOLATED_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()); - qualifierList.add(isolatedToken); - } - - Token resToken = NodeFactory.createToken(SyntaxKind.RESOURCE_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()); - qualifierList.add(resToken); - - return NodeFactory.createFunctionDefinitionNode( - SyntaxKind.FUNCTION_DEFINITION, null, NodeFactory.createNodeList(qualifierList), - NodeFactory.createToken(SyntaxKind.FUNCTION_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), NodeFactory - .createIdentifierToken("'default", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), relativeResPath, functionSignatureNode, - functionBodyNode); - } - - /** - * Generates top level ballerina document node with imports, typedesc, main method and handler functions. - * - * @param functionDeploymentContexts list of contexts containing original and generated function information - * @param generatedTypeDefinitions type descriptors that needs to be generated - * @return module part node - */ - public static ModulePartNode createModulePartNode(Collection functionDeploymentContexts, - Map generatedTypeDefinitions) { - - ImportDeclarationNode afImport = - NodeFactory.createImportDeclarationNode(NodeFactory.createToken(SyntaxKind.IMPORT_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), GeneratorUtil.generateMinutiaeListWithWhitespace()), - NodeFactory.createImportOrgNameNode( - NodeFactory.createIdentifierToken(Constants.AZURE_FUNCTIONS_PACKAGE_ORG), - NodeFactory.createToken(SyntaxKind.SLASH_TOKEN)), - NodeFactory.createSeparatedNodeList( - NodeFactory.createIdentifierToken(Constants.AZURE_FUNCTIONS_MODULE_NAME)), - NodeFactory.createImportPrefixNode(NodeFactory.createToken(SyntaxKind.AS_KEYWORD, - GeneratorUtil.generateMinutiaeListWithWhitespace(), - GeneratorUtil.generateMinutiaeListWithWhitespace()), - NodeFactory.createIdentifierToken(Constants.AF_IMPORT_ALIAS)), - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline())); - - ImportDeclarationNode httpImport = - NodeFactory.createImportDeclarationNode(NodeFactory.createToken(SyntaxKind.IMPORT_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), GeneratorUtil.generateMinutiaeListWithWhitespace()), - NodeFactory.createImportOrgNameNode(NodeFactory.createIdentifierToken(Constants.BALLERINA_ORG), - NodeFactory.createToken(SyntaxKind.SLASH_TOKEN)), - NodeFactory.createSeparatedNodeList(NodeFactory.createIdentifierToken("http")), - null, NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline())); - List memberDeclarationNodeList = new ArrayList<>(); - QualifiedNameReferenceNode httpListener = - NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken("http"), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("Listener", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - QualifiedNameReferenceNode listenerRef = - NodeFactory - .createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.AF_IMPORT_ALIAS), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("hl", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - ListenerDeclarationNode listener = - NodeFactory.createListenerDeclarationNode(null, NodeFactory.createToken(SyntaxKind.PUBLIC_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), GeneratorUtil.generateMinutiaeListWithWhitespace()), - NodeFactory.createToken(SyntaxKind.LISTENER_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace()), httpListener, - NodeFactory.createIdentifierToken( - "__testListener"), NodeFactory.createToken(SyntaxKind.EQUAL_TOKEN), listenerRef, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - memberDeclarationNodeList.add(listener); - - memberDeclarationNodeList.addAll(generatedTypeDefinitions.values()); - - QualifiedNameReferenceNode httpService = - NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken("http"), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("Service", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace())); - - NodeList absResPathList = NodeFactory.createNodeList(NodeFactory.createToken(SyntaxKind.SLASH_TOKEN)); - - SeparatedNodeList listenerReff = NodeFactory.createSeparatedNodeList(NodeFactory - .createSimpleNameReferenceNode( - NodeFactory.createIdentifierToken("__testListener", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()))); - - List resourceFunctions = new ArrayList<>(); - for (FunctionDeploymentContext context : functionDeploymentContexts) { - resourceFunctions.add(createResourceFunction(context)); - resourceFunctions.add(context.getFunction()); - } - - ServiceDeclarationNode serviceDeclarationNode = - NodeFactory.createServiceDeclarationNode(null, NodeFactory.createEmptyNodeList(), - NodeFactory.createToken(SyntaxKind.SERVICE_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), httpService, absResPathList, - NodeFactory.createToken(SyntaxKind.ON_KEYWORD, generateMinutiaeListWithWhitespace(), - generateMinutiaeListWithWhitespace()), listenerReff, - NodeFactory.createToken(SyntaxKind.OPEN_BRACE_TOKEN), - NodeFactory.createNodeList(resourceFunctions), - NodeFactory.createToken(SyntaxKind.CLOSE_BRACE_TOKEN)); - - memberDeclarationNodeList.add(serviceDeclarationNode); - - NodeList nodeList = NodeFactory.createNodeList(memberDeclarationNodeList); - Token eofToken = NodeFactory.createToken(SyntaxKind.EOF_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline()); - return NodeFactory.createModulePartNode(NodeFactory.createNodeList(afImport, httpImport), nodeList, eofToken); - } - - public static MinutiaeList generateMinutiaeListWithWhitespace() { - return NodeFactory.createMinutiaeList(NodeFactory.createWhitespaceMinutiae(" ")); - } - - public static MinutiaeList generateMinutiaeListWithNewline() { - return NodeFactory.createMinutiaeList(NodeFactory.createWhitespaceMinutiae("\n")); - } - - public static boolean isContextType(ParameterSymbol symbol) { - Optional module = symbol.typeDescriptor().getModule(); - if (module.isEmpty()) { - return false; - } - ModuleID id = module.get().id(); - if (!(id.orgName().equals(Constants.AZURE_FUNCTIONS_PACKAGE_ORG) && - id.moduleName().equals(Constants.AZURE_FUNCTIONS_MODULE_NAME))) { - return false; - } - Optional name = symbol.typeDescriptor().getName(); - if (name.isEmpty()) { - return false; - } - return name.get().equals(Constants.AZURE_FUNCTIONS_CONTEXT_NAME); - } - - public static void addFunctionBinding(FunctionDeploymentContext ctx, Map binding) { - if (binding == null) { - return; - } - JsonArray bindings = (JsonArray) ctx.getFunctionDefinition().get(Constants.FUNCTION_BINDINGS_NAME); - bindings.add(createBindingObject(binding)); - } - - public static JsonObject createBindingObject(Map binding) { - JsonObject obj = new JsonObject(); - for (Map.Entry entry : binding.entrySet()) { - obj.add(entry.getKey(), objectToJson(entry.getValue())); - } - return obj; - } - - public static JsonElement objectToJson(Object obj) { - if (obj instanceof String) { - return new JsonPrimitive((String) obj); - } else if (obj instanceof Number) { - return new JsonPrimitive((Number) obj); - } else if (obj instanceof Boolean) { - return new JsonPrimitive((Boolean) obj); - } else if (obj instanceof String[]) { - JsonArray array = new JsonArray(); - for (String item : (String[]) obj) { - array.add(item); - } - return array; - } else if (obj == null) { - return JsonNull.INSTANCE; - } else { - throw new IllegalStateException("Unsupported type to convert to JSON: " + obj.getClass()); - } - } - - //TODO remove when semantic api supports return type annotations - //https://github.com/ballerina-platform/ballerina-lang/issues/27225 - public static Optional extractAzureFunctionAnnotation(NodeList annons) { - for (AnnotationNode an : annons) { - Node node = an.annotReference(); - if (node.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { - QualifiedNameReferenceNode refNode = (QualifiedNameReferenceNode) node; - String text = refNode.modulePrefix().text(); - if (text.equals(Constants.AF_IMPORT_ALIAS) || text.equals(Constants.AZURE_FUNCTIONS_MODULE_NAME)) { - return Optional.of(an); - } - } - } - return Optional.empty(); - } - - public static Optional extractAzureFunctionAnnotation(List annotations) { - for (AnnotationSymbol annotationSymbol : annotations) { - Optional module = annotationSymbol.getModule(); - if (module.isEmpty()) { - return Optional.empty(); - } - ModuleID id = module.get().id(); - if (id.orgName().equals(Constants.AZURE_FUNCTIONS_PACKAGE_ORG) && - id.moduleName().equals(Constants.AZURE_FUNCTIONS_MODULE_NAME)) { - return Optional.of(annotationSymbol); - } - } - return Optional.empty(); - } - - public static boolean isAzurePkgType(ParameterSymbol variableSymbol, String name) { - Optional module = variableSymbol.typeDescriptor().getModule(); - if (module.isEmpty()) { - return false; - } - ModuleID id = module.get().id(); - if (!(id.orgName().equals(Constants.AZURE_FUNCTIONS_PACKAGE_ORG) && - id.moduleName().equals(Constants.AZURE_FUNCTIONS_MODULE_NAME))) { - return false; - } - Optional name1 = variableSymbol.typeDescriptor().getName(); - if (name1.isEmpty()) { - return false; - } - return name1.get().equals(name); - } - - public static boolean isStringType(ParameterSymbol variableSymbol) { - return variableSymbol.typeDescriptor().typeKind() == TypeDescKind.STRING; - } - - public static boolean isOptionalStringType(ParameterSymbol variableSymbol) { - if (variableSymbol.typeDescriptor().typeKind() == TypeDescKind.UNION) { - UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) variableSymbol.typeDescriptor(); - for (TypeSymbol memberTypeDescriptor : unionTypeSymbol.memberTypeDescriptors()) { - if (memberTypeDescriptor.typeKind() == TypeDescKind.STRING) { - return true; - } - } - } - return false; - } - - public static boolean isOptionalByteArrayType(ParameterSymbol variableSymbol) { - if (variableSymbol.typeDescriptor().typeKind() == TypeDescKind.UNION) { - UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) variableSymbol.typeDescriptor(); - for (TypeSymbol memberTypeDescriptor : unionTypeSymbol.memberTypeDescriptors()) { - if (memberTypeDescriptor.typeKind() == TypeDescKind.ARRAY) { - ArrayTypeSymbol arrayTypeDescriptor = (ArrayTypeSymbol) memberTypeDescriptor; - if (arrayTypeDescriptor.memberTypeDescriptor().typeKind() == TypeDescKind.BYTE) { - return true; - } - } - } - } - return false; - } - - public static boolean isByteArrayType(ParameterSymbol variableSymbol) { - if (variableSymbol.typeDescriptor().typeKind() == TypeDescKind.ARRAY) { - ArrayTypeSymbol arrayTypeDescriptor = (ArrayTypeSymbol) variableSymbol.typeDescriptor(); - return arrayTypeDescriptor.memberTypeDescriptor().typeKind() == TypeDescKind.BYTE; - } - return false; - } - - public static boolean isOptionalRecordType(ParameterSymbol variableSymbol) { - if (variableSymbol.typeDescriptor().typeKind() == TypeDescKind.UNION) { - UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) variableSymbol.typeDescriptor(); - for (TypeSymbol memberTypeDescriptor : unionTypeSymbol.memberTypeDescriptors()) { - if (memberTypeDescriptor.typeKind() == TypeDescKind.TYPE_REFERENCE) { - return true; - } - } - } - return false; - } - - public static boolean isRecordArrayType(ParameterSymbol variableSymbol) { - return isRecordArrayType(variableSymbol.typeDescriptor()); - } - - public static boolean isRecordArrayType(TypeSymbol variableSymbol) { - if (variableSymbol.typeKind() == TypeDescKind.ARRAY) { - ArrayTypeSymbol arrayTypeSymbol = (ArrayTypeSymbol) variableSymbol; - return arrayTypeSymbol.memberTypeDescriptor().typeKind() == TypeDescKind.TYPE_REFERENCE; - } - return false; - } - - public static TypeDefinitionNode createArrayTypeDefinitionNode(ArrayTypeDescriptorNode arrayTypeDescriptorNode) { - String typeName = ((SimpleNameReferenceNode) arrayTypeDescriptorNode.memberTypeDesc()).name().text(); - return NodeFactory.createTypeDefinitionNode(null, null, NodeFactory.createToken(SyntaxKind.TYPE_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()), - NodeFactory.createIdentifierToken(typeName + "ArrayGenerated", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), arrayTypeDescriptorNode, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - } - - public static TypeDefinitionNode createOptionalTypeDefinitionNode( - OptionalTypeDescriptorNode optionalTypeDescriptorNode) { - String typeName = ((SimpleNameReferenceNode) optionalTypeDescriptorNode.typeDescriptor()).name().text(); - return NodeFactory.createTypeDefinitionNode(null, null, NodeFactory.createToken(SyntaxKind.TYPE_KEYWORD, - NodeFactory.createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()), - NodeFactory.createIdentifierToken(typeName + "OptionalGenerated", NodeFactory.createEmptyMinutiaeList(), - generateMinutiaeListWithWhitespace()), - optionalTypeDescriptorNode, NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN)); - } - - public static boolean isJsonType(ParameterSymbol variableSymbol) { - return variableSymbol.typeDescriptor().typeKind() == TypeDescKind.JSON; - } - - public static Map extractAnnotationKeyValues(AnnotationNode annotation) - throws AzureFunctionsException { - if (annotation.annotValue().isEmpty()) { - return Collections.emptyMap(); - } - MappingConstructorExpressionNode mappingConstructorExpressionNode = annotation.annotValue().get(); - Map annonMap = new HashMap<>(); - for (MappingFieldNode field : mappingConstructorExpressionNode.fields()) { - if (field.kind() != SyntaxKind.SPECIFIC_FIELD) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(field.location(), "AZ0009", - "specific.field.required", DiagnosticSeverity.ERROR, "specific field expected for " + - "annotation field")); - } - SpecificFieldNode keyValue = (SpecificFieldNode) field; - Node node = keyValue.fieldName(); - if (node.kind() != SyntaxKind.IDENTIFIER_TOKEN) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(field.location(), "AZ0010", - "identifier.token.expected", DiagnosticSeverity.ERROR, "identifier token expected for key")); - } - String key = ((IdentifierToken) node).text(); - if (keyValue.valueExpr().isEmpty()) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(field.location(), "AZ0011", - "annotation.value.expected", DiagnosticSeverity.ERROR, "annotation value expected")); - } - ExpressionNode valueExpr = keyValue.valueExpr().get(); - if (valueExpr.kind() != SyntaxKind.STRING_LITERAL) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(field.location(), "AZ0011", - "unsupported.annotation.value", DiagnosticSeverity.ERROR, - "unsupported annotation value " + valueExpr.kind())); - } - String value = ((BasicLiteralNode) valueExpr).literalToken().text(); - value = value.substring(1, value.length() - 1); - - annonMap.put(key, value); - } - return annonMap; - } - - public static TypeSymbol getMainParamType(TypeSymbol typeSymbol) { - if (typeSymbol.typeKind() == TypeDescKind.UNION) { - UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) typeSymbol; - for (TypeSymbol memberTypeDescriptor : unionTypeSymbol.memberTypeDescriptors()) { - if (!(memberTypeDescriptor.typeKind() == TypeDescKind.ERROR || - memberTypeDescriptor.typeKind() == TypeDescKind.NIL)) { - return memberTypeDescriptor; - } - } - } - return typeSymbol; - } - - public static SimpleNameReferenceNode addAzurePkgRecordVarDef(FunctionDeploymentContext ctx, String type, - String name) { - QualifiedNameReferenceNode typeDesc = - NodeFactory - .createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.AF_IMPORT_ALIAS), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken(type)); - TypedBindingPatternNode typedBindingPatternNode = NodeFactory.createTypedBindingPatternNode(typeDesc, - NodeFactory.createCaptureBindingPatternNode(NodeFactory.createIdentifierToken(name, - generateMinutiaeListWithWhitespace(), NodeFactory.createEmptyMinutiaeList()))); - MappingConstructorExpressionNode emptyInit = - NodeFactory.createMappingConstructorExpressionNode(NodeFactory.createToken(SyntaxKind.OPEN_BRACE_TOKEN), - NodeFactory.createSeparatedNodeList(), NodeFactory.createToken(SyntaxKind.CLOSE_BRACE_TOKEN)); - VariableDeclarationNode variableDeclarationNode = NodeFactory - .createVariableDeclarationNode(NodeFactory.createEmptyNodeList(), null, typedBindingPatternNode, - NodeFactory.createToken(SyntaxKind.EQUAL_TOKEN), emptyInit, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline())); - - FunctionDefinitionNode function = ctx.getFunction(); - ctx.setFunction(addStatementToFunctionBody(variableDeclarationNode, function)); - return NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(name)); - } - - private static FunctionDefinitionNode addStatementToFunctionBody(StatementNode statementNode, - FunctionDefinitionNode function) { - FunctionBodyBlockNode functionBodyBlockNode = (FunctionBodyBlockNode) function.functionBody(); - NodeList newBodyStatements = functionBodyBlockNode.statements().add(statementNode); - FunctionBodyBlockNode newFunctionBodyBlock = - functionBodyBlockNode.modify().withStatements(newBodyStatements).apply(); - return function.modify().withFunctionBody(newFunctionBodyBlock).apply(); - } - - public static ExpressionNode createFunctionInvocationNode(String functionName, PositionalArgumentNode... args) { - SimpleNameReferenceNode simpleNameReferenceNode = - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(functionName)); - SeparatedNodeList separatedNodeList = getFunctionParamList(args); - return NodeFactory.createFunctionCallExpressionNode(simpleNameReferenceNode, - NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), separatedNodeList, - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN)); - } - - public static ExpressionNode createMethodInvocationNode(String functionName, PositionalArgumentNode... args) { - SimpleNameReferenceNode simpleNameReferenceNode = - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(functionName)); - SimpleNameReferenceNode selfRef = - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken("self")); - SeparatedNodeList separatedNodeList = getFunctionParamList(args); - return NodeFactory.createMethodCallExpressionNode(selfRef, NodeFactory.createToken(SyntaxKind.DOT_TOKEN), - simpleNameReferenceNode, NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), separatedNodeList, - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN)); - } - - public static ExpressionNode createAfFunctionInvocationNode(String functionName, - boolean checked, PositionalArgumentNode... args) { - QualifiedNameReferenceNode qualifiedNameReferenceNode = - NodeFactory - .createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(Constants.AF_IMPORT_ALIAS), - NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken(functionName)); - SeparatedNodeList separatedNodeList = getFunctionParamList(args); - - FunctionCallExpressionNode expression = - NodeFactory.createFunctionCallExpressionNode(qualifiedNameReferenceNode, - NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), separatedNodeList, - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN)); - - if (checked) { - return NodeFactory.createCheckExpressionNode(SyntaxKind.CHECK_EXPRESSION, - NodeFactory.createToken(SyntaxKind.CHECK_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace()), - expression); - } - return expression; - } - - private static SeparatedNodeList getFunctionParamList(PositionalArgumentNode... args) { - List nodeList = new ArrayList<>(); - for (PositionalArgumentNode arg : args) { - nodeList.add(arg); - nodeList.add(NodeFactory.createToken(SyntaxKind.COMMA_TOKEN)); - } - if (args.length > 0) { - nodeList.remove(nodeList.size() - 1); - } - return NodeFactory.createSeparatedNodeList(nodeList); - } - - /** - * Calls internal function in Azure Functions module and adds into function body. - * - * @param ctx Function Deployment Context - * @param name name of the internal function needs to be called - * @param checked is checking required - * @param exprs parameter arguments - */ - public static void addAzurePkgFunctionCallStatement(FunctionDeploymentContext ctx, String name, - boolean checked, PositionalArgumentNode... exprs) { - NilTypeDescriptorNode nilTypeDescriptorNode = - NodeFactory.createNilTypeDescriptorNode(NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace())); - addFunctionCallStatement(nilTypeDescriptorNode, ctx, createAfFunctionInvocationNode(name, false, exprs), - checked); - } - - public static SimpleNameReferenceNode createVariableRef(String varName) { - return NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(varName)); - } - - public static BasicLiteralNode createStringLiteral(String content) { - return NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, "\"" + content + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList())); - } - - /** - * Adds Function call statement to the function body. - * - * @param typeDescriptorNode type of the left hand side - * @param ctx Function Deployment Context - * @param inv expression of the right side - * @param checked is checking required for the expression - * @return variable name - */ - public static String addFunctionCallStatement(TypeDescriptorNode typeDescriptorNode, FunctionDeploymentContext ctx, - ExpressionNode inv, boolean checked) { - ExpressionNode expr; - if (checked) { - expr = NodeFactory.createCheckExpressionNode(SyntaxKind.CHECK_EXPRESSION, - NodeFactory.createToken(SyntaxKind.CHECK_KEYWORD, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace()), inv); - } else { - expr = inv; - } - - if (typeDescriptorNode.kind() == SyntaxKind.NIL_TYPE_DESC) { - createWildcardAssignmentNode(ctx, expr); - return "_"; - } - if (typeDescriptorNode.kind() == SyntaxKind.OPTIONAL_TYPE_DESC) { - Node typeDescriptor = ((OptionalTypeDescriptorNode) typeDescriptorNode).typeDescriptor(); - if (typeDescriptor.kind() == SyntaxKind.ERROR_TYPE_DESC) { - createWildcardAssignmentNode(ctx, expr); - return "_"; - } - } - if (typeDescriptorNode.kind() == SyntaxKind.OPTIONAL_TYPE_DESC) { - createWildcardAssignmentNode(ctx, expr); - return "_"; - } - String varName = ctx.getNextVarName(); - CaptureBindingPatternNode captureBindingPatternNode = - NodeFactory.createCaptureBindingPatternNode(NodeFactory.createIdentifierToken(varName, - generateMinutiaeListWithWhitespace(), NodeFactory.createEmptyMinutiaeList())); - TypedBindingPatternNode typedBindingPatternNode = - NodeFactory.createTypedBindingPatternNode(typeDescriptorNode, captureBindingPatternNode); - VariableDeclarationNode variableDeclarationNode = NodeFactory - .createVariableDeclarationNode(NodeFactory.createEmptyNodeList(), null, typedBindingPatternNode, - NodeFactory.createToken(SyntaxKind.EQUAL_TOKEN), expr, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline())); - ctx.setFunction(addStatementToFunctionBody(variableDeclarationNode, ctx.getFunction())); - return varName; - } - - private static void createWildcardAssignmentNode(FunctionDeploymentContext ctx, ExpressionNode expr) { - WildcardBindingPatternNode wildcardBindingPatternNode = NodeFactory - .createWildcardBindingPatternNode(NodeFactory.createToken(SyntaxKind.UNDERSCORE_KEYWORD)); - AssignmentStatementNode assignmentStatementNode = NodeFactory - .createAssignmentStatementNode(wildcardBindingPatternNode, - NodeFactory.createToken(SyntaxKind.EQUAL_TOKEN), expr, - NodeFactory.createToken(SyntaxKind.SEMICOLON_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithNewline())); - ctx.setFunction(addStatementToFunctionBody(assignmentStatementNode, ctx.getFunction())); - } - - /** - * Creates Azure Function Diagnostic object for unexpected syntax in ballerina documents. - * - * @param location location of the node - * @param code diagnostic code - * @param template diagnostic message template - * @param severity severity of the diagnostic - * @param message message if the diagnostic - * @return Azure Function diagnostic - */ - public static Diagnostic getAFDiagnostic(Location location, String code, - String template, DiagnosticSeverity severity, String message) { - DiagnosticInfo diagnosticInfo = new DiagnosticInfo(code, template, severity); - return new AzureFunctionDiagnostics(location, diagnosticInfo, message); - } - - public static TypeDescriptorNode getCheckedReturnTypeDescOfOriginalFunction(FunctionDefinitionNode functionNode) { - Optional returnTypeDescriptorNode = - functionNode.functionSignature().returnTypeDesc(); - if (returnTypeDescriptorNode.isEmpty()) { - return NodeFactory.createNilTypeDescriptorNode(NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace())); - } - Node returnType = returnTypeDescriptorNode.get().type(); - if (!(returnType instanceof TypeDescriptorNode)) { - return NodeFactory.createNilTypeDescriptorNode(NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace())); - } - - return getCheckedTypeDesc((TypeDescriptorNode) returnType); - } - - public static boolean isCheckingRequiredForOriginalFunction(FunctionDefinitionNode functionDefinitionNode) { - Optional returnTypeDescriptorNode = - functionDefinitionNode.functionSignature().returnTypeDesc(); - if (returnTypeDescriptorNode.isEmpty()) { - return false; - } - TypeDescriptorNode typeDescriptorNode = - (TypeDescriptorNode) returnTypeDescriptorNode.get().type(); - - if (typeDescriptorNode.kind() == SyntaxKind.UNION_TYPE_DESC) { - UnionTypeDescriptorNode unionTypeDescriptorNode = (UnionTypeDescriptorNode) typeDescriptorNode; - TypeDescriptorNode leftTypeDesc = unionTypeDescriptorNode.leftTypeDesc(); - if (leftTypeDesc.kind() == SyntaxKind.ERROR_TYPE_DESC) { - return true; - } - TypeDescriptorNode rightTypeDesc = unionTypeDescriptorNode.rightTypeDesc(); - return rightTypeDesc.kind() == SyntaxKind.ERROR_TYPE_DESC; - } - if (typeDescriptorNode.kind() == SyntaxKind.OPTIONAL_TYPE_DESC) { - OptionalTypeDescriptorNode optionalTypeDescriptorNode = (OptionalTypeDescriptorNode) typeDescriptorNode; - Node node = optionalTypeDescriptorNode.typeDescriptor(); - if (node.kind() == SyntaxKind.ERROR_TYPE_DESC) { - return true; - } - } - return typeDescriptorNode.kind() == SyntaxKind.ERROR_TYPE_DESC; - } - - public static TypeDescriptorNode getCheckedTypeDesc(TypeDescriptorNode typeDescriptorNode) { - if (typeDescriptorNode.kind() == SyntaxKind.UNION_TYPE_DESC) { - UnionTypeDescriptorNode unionTypeDescriptorNode = (UnionTypeDescriptorNode) typeDescriptorNode; - TypeDescriptorNode leftTypeDesc = getCheckedTypeDesc(unionTypeDescriptorNode.leftTypeDesc()); - if (leftTypeDesc.kind() != SyntaxKind.NIL_TYPE_DESC) { - return leftTypeDesc; - } - TypeDescriptorNode rightTypeDesc = getCheckedTypeDesc(unionTypeDescriptorNode.rightTypeDesc()); - if (rightTypeDesc.kind() != SyntaxKind.NIL_TYPE_DESC) { - return rightTypeDesc; - } - - return leftTypeDesc; - } - if (typeDescriptorNode.kind() == SyntaxKind.ERROR_TYPE_DESC) { - return NodeFactory.createNilTypeDescriptorNode(NodeFactory.createToken(SyntaxKind.OPEN_PAREN_TOKEN), - NodeFactory.createToken(SyntaxKind.CLOSE_PAREN_TOKEN, NodeFactory.createEmptyMinutiaeList(), - GeneratorUtil.generateMinutiaeListWithWhitespace())); - } - return typeDescriptorNode; - } - - public static boolean isIsolatedFunction(FunctionDefinitionNode functionDefinitionNode) { - NodeList tokens = functionDefinitionNode.qualifierList(); - for (Token token: tokens) { - if (token.kind() == SyntaxKind.ISOLATED_KEYWORD) { - return true; - } - } - return false; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/HandlerFactory.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/HandlerFactory.java deleted file mode 100644 index b5f19142..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/HandlerFactory.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.api.symbols.AnnotationSymbol; -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.api.symbols.Symbol; -import io.ballerina.compiler.api.symbols.SymbolKind; -import io.ballerina.compiler.api.symbols.TypeSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.ParameterNode; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.handlers.blob.BlobInputParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.blob.BlobOutputParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.blob.BlobTriggerParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.context.ContextParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.cosmosdb.CosmosDBInputParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.cosmosdb.CosmosDBReturnHandler; -import org.ballerinax.azurefunctions.generator.handlers.cosmosdb.CosmosDBTriggerHandler; -import org.ballerinax.azurefunctions.generator.handlers.http.HTTPOutputParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.http.HTTPReturnHandler; -import org.ballerinax.azurefunctions.generator.handlers.http.HTTPTriggerParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.metadata.MetadataBindingParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.queue.QueueOutputParameterHandler; -import org.ballerinax.azurefunctions.generator.handlers.queue.QueueTriggerHandler; -import org.ballerinax.azurefunctions.generator.handlers.timer.TimerTriggerHandler; -import org.ballerinax.azurefunctions.generator.handlers.twilio.TwilioSmsOutputParameterHandler; - -import java.util.Map; -import java.util.Optional; - -/** - * Factory class to create parameter and return handlers. - */ -public class HandlerFactory { - - public static ParameterHandler createParameterHandler(ParameterNode param, SemanticModel semanticModel, - Map generatedTypeDefinitions) - throws AzureFunctionsException { - if (param.kind() != SyntaxKind.REQUIRED_PARAM) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.location(), "AZ0004", - "required.param.supported", DiagnosticSeverity.ERROR, "only required params are supported")); - } - RequiredParameterNode requiredParameterNode = (RequiredParameterNode) param; - if (requiredParameterNode.paramName().isEmpty()) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.location(), "AZ0005", - "required.param.name", DiagnosticSeverity.ERROR, "param name is required")); - } - Optional paramSymbol = semanticModel.symbol(requiredParameterNode.paramName().get()); - if (paramSymbol.isEmpty() || paramSymbol.get().kind() != SymbolKind.PARAMETER) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.location(), "AZ0010", - "symbol.not.found", DiagnosticSeverity.ERROR, "parameter symbol not found")); - } - ParameterSymbol variableSymbol = (ParameterSymbol) paramSymbol.get(); - if (GeneratorUtil.isContextType(variableSymbol)) { - return new ContextParameterHandler(variableSymbol, requiredParameterNode); - } - - Optional annotationNode = - GeneratorUtil.extractAzureFunctionAnnotation(variableSymbol.annotations()); - if (annotationNode.isEmpty()) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(requiredParameterNode.location(), "AZ0006", - "missing.required.annotation", DiagnosticSeverity.ERROR, "azure functions annotation is required")); - } - - String annotationName = annotationNode.get().getName().orElseThrow(); - switch (annotationName) { - case "HTTPOutput": - return new HTTPOutputParameterHandler(variableSymbol, requiredParameterNode); - case "HTTPTrigger": - return new HTTPTriggerParameterHandler(variableSymbol, requiredParameterNode); - case "QueueOutput": - return new QueueOutputParameterHandler(variableSymbol, requiredParameterNode); - case "QueueTrigger": - return new QueueTriggerHandler(variableSymbol, requiredParameterNode); - case "TimerTrigger": - return new TimerTriggerHandler(variableSymbol, requiredParameterNode); - case "BlobTrigger": - return new BlobTriggerParameterHandler(variableSymbol, requiredParameterNode); - case "BlobInput": - return new BlobInputParameterHandler(variableSymbol, requiredParameterNode); - case "BlobOutput": - return new BlobOutputParameterHandler(variableSymbol, requiredParameterNode); - case "TwilioSmsOutput": - return new TwilioSmsOutputParameterHandler(variableSymbol, requiredParameterNode); - case "BindingName": - return new MetadataBindingParameterHandler(variableSymbol, requiredParameterNode); - case "CosmosDBTrigger": - return new CosmosDBTriggerHandler(variableSymbol, requiredParameterNode, - generatedTypeDefinitions); - case "CosmosDBInput": - return new CosmosDBInputParameterHandler(variableSymbol, requiredParameterNode, - generatedTypeDefinitions); - default: - throw new AzureFunctionsException( - GeneratorUtil.getAFDiagnostic(annotationNode.get().getLocation().orElseThrow(), - "AZ0006", "unsupported.param.handler", DiagnosticSeverity.ERROR, - "param handler not found for the type: " + annotationName)); - } - } - - public static ReturnHandler createReturnHandler(TypeSymbol symbol, - ReturnTypeDescriptorNode returnTypeDescriptorNode) - throws AzureFunctionsException { - //TODO avoid using syntax tree and use semantic api instead when the its supported. - //https://github.com/ballerina-platform/ballerina-lang/issues/27225 - TypeSymbol retType = GeneratorUtil.getMainParamType(symbol); - NodeList annotations = returnTypeDescriptorNode.annotations(); - Optional azureAnnotations = GeneratorUtil.extractAzureFunctionAnnotation(annotations); - if (azureAnnotations.isEmpty()) { - return null; - } - Node annotReference = azureAnnotations.get().annotReference(); - if (annotReference.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { - throw new AzureFunctionsException( - GeneratorUtil.getAFDiagnostic(annotReference.location(), "AZ0002", "unexpected.node.type", - DiagnosticSeverity.ERROR, "unexpected node type")); - } - - String name = ((QualifiedNameReferenceNode) annotReference).identifier().text(); - if ("HTTPOutput".equals(name)) { - return new HTTPReturnHandler(retType, azureAnnotations.get()); - } else if ("CosmosDBOutput".equals(name)) { - return new CosmosDBReturnHandler(retType, azureAnnotations.get()); - } else { - throw new AzureFunctionsException( - GeneratorUtil.getAFDiagnostic(annotReference.location(), "AZ0001", "unsupported.return.handler", - DiagnosticSeverity.ERROR, "return handler not found for the type: " + symbol.signature())); - } - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ParameterHandler.java deleted file mode 100644 index 2e3337a9..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ParameterHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import io.ballerina.compiler.syntax.tree.ExpressionNode; - -/** - * Represents an Azure function parameter handler. - */ -public interface ParameterHandler { - - /** - * Initializes the {@link ParameterHandler}. This can be used for any initialization operations before - * the preInvocationProcess call is made. - * - * @param context The handler context - * @throws AzureFunctionsException thrown if an error occurs - */ - void init(FunctionDeploymentContext context) throws AzureFunctionsException; - - /** - * Called when generating the azure function invocation statement. This will be used for scenarios - * such as generating the parameter values for the function invocation by consuming the incoming HTTP - * data, which can be accessed using the context. After any required statement are generated, this must - * return an expression which is used for the parameter value of the azure function call. - * - * @return The expression which represents the parameter value - * @throws AzureFunctionsException thrown if an error occurs - */ - ExpressionNode invocationProcess() throws AzureFunctionsException; - - /** - * Called after the function call statement is generated. This can be used for scenarios like processing - * any output bindings, where we need to extra data from the parameter and populate the output JSON value - * that is referenced using the context instance. - */ - void postInvocationProcess() throws AzureFunctionsException; - - /** - * Retreives the binding type. - * - * @return The binding type - */ - BindingType getBindingType(); - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ReturnHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ReturnHandler.java deleted file mode 100644 index 55dca77b..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/ReturnHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator; - -import io.ballerina.compiler.syntax.tree.ExpressionNode; - -/** - * Represents an Azure function return value handler. - */ -public interface ReturnHandler { - - /** - * Initializes the {@link ReturnHandler}. This can be used for any - * initialization operations before the invocationVariable call is made. - * - * @param context The handler context - * - * @throws AzureFunctionsException thrown if an error occurs - */ - public void init(FunctionDeploymentContext context) throws AzureFunctionsException; - - /** - * Called after the function invocation statement is done, and the return value is passed here - * as an expression to be use for further statement creation to populate the JSON result. - * - * @param returnValueExpr The function invocation return value expression - * - * @throws AzureFunctionsException thrown if an error occurs - */ - public void postInvocationProcess(ExpressionNode returnValueExpr) throws AzureFunctionsException; - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractParameterHandler.java deleted file mode 100644 index 477f21fb..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractParameterHandler.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.FunctionDeploymentContext; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.ParameterHandler; - -import java.util.Map; - -/** - * Abstract class with common operations implemented for {@link ParameterHandler}. - */ -public abstract class AbstractParameterHandler implements ParameterHandler { - - protected FunctionDeploymentContext ctx; - - protected RequiredParameterNode param; - - protected String name; - - protected BindingType bindingType; - - protected ParameterSymbol variableSymbol; - - public AbstractParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param, - BindingType bindingType) { - this.variableSymbol = variableSymbol; - this.param = param; - this.name = this.param.paramName().get().text(); - this.bindingType = bindingType; - } - - public void init(FunctionDeploymentContext ctx) throws AzureFunctionsException { - this.ctx = ctx; - this.processBinding(); - } - - private String extractBindingDirection() { - if (BindingType.INPUT.equals(this.bindingType) || BindingType.TRIGGER.equals(this.bindingType)) { - return "in"; - } else { - return "out"; - } - } - - private void processBinding() throws AzureFunctionsException { - Map binding = this.generateBinding(); - if (binding == null) { - return; - } - binding.put("direction", this.extractBindingDirection()); - binding.put("name", this.name); - GeneratorUtil.addFunctionBinding(this.ctx, binding); - } - - public BindingType getBindingType() { - return bindingType; - } - - public abstract Map generateBinding() throws AzureFunctionsException; - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractReturnHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractReturnHandler.java deleted file mode 100644 index 4e9d809e..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/AbstractReturnHandler.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers; - -import io.ballerina.compiler.api.symbols.TypeSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.FunctionDeploymentContext; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.ReturnHandler; - -import java.util.Map; - -/** - * Abstract class with common operations implemented for {@link ReturnHandler}. - */ -public abstract class AbstractReturnHandler implements ReturnHandler { - - protected FunctionDeploymentContext ctx; - - protected TypeSymbol retType; - - protected AnnotationNode annotation; - - public AbstractReturnHandler(TypeSymbol retType, AnnotationNode annotation) { - this.retType = retType; - this.annotation = annotation; - } - - public void init(FunctionDeploymentContext ctx) throws AzureFunctionsException { - this.ctx = ctx; - this.processBinding(); - } - - private void processBinding() throws AzureFunctionsException { - Map binding = this.generateBinding(); - if (binding == null) { - return; - } - binding.put("direction", "out"); - binding.put("name", "$return"); - GeneratorUtil.addFunctionBinding(this.ctx, binding); - } - - public abstract Map generateBinding() throws AzureFunctionsException; - - public AnnotationNode getAnnotation() { - return annotation; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobInputParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobInputParameterHandler.java deleted file mode 100644 index 2266780e..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobInputParameterHandler.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.blob; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@BlobInput". - */ -public class BlobInputParameterHandler extends AbstractParameterHandler { - - public BlobInputParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.INPUT); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (GeneratorUtil.isOptionalByteArrayType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil - .createAfFunctionInvocationNode("getOptionalBytesFromInputData", true, paramsArg, stringArg); - } else if (GeneratorUtil.isOptionalStringType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil - .createAfFunctionInvocationNode("getOptionalStringConvertedBytesFromInputData", true, paramsArg, - stringArg); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "blob"); - binding.put("path", annonMap.get("path")); - binding.put("dataType", "binary"); - String connection = (String) annonMap.get("connection"); - if (connection == null) { - connection = Constants.DEFAULT_STORAGE_CONNECTION_NAME; - } - binding.put("connection", connection); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobOutputParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobOutputParameterHandler.java deleted file mode 100644 index 3d2f965d..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobOutputParameterHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.blob; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the output parameter handler annotation "@BlobOutput". - */ -public class BlobOutputParameterHandler extends AbstractParameterHandler { - - private SimpleNameReferenceNode var; - - public BlobOutputParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.OUTPUT); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - if (GeneratorUtil.isAzurePkgType(this.variableSymbol, "BytesOutputBinding")) { - this.var = GeneratorUtil.addAzurePkgRecordVarDef(this.ctx, "BytesOutputBinding", this.ctx.getNextVarName()); - } else if (GeneratorUtil.isAzurePkgType(this.variableSymbol, "StringOutputBinding")) { - this.var = GeneratorUtil - .addAzurePkgRecordVarDef(this.ctx, "StringOutputBinding", this.ctx.getNextVarName()); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.typeName().location(), "AZ0007", - "required.type", DiagnosticSeverity.ERROR, - "Type must be 'BytesOutputBinding' or 'StringOutputBinding'")); - } - return this.var; - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef("params")); - - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - - PositionalArgumentNode varArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(var.name().text())); - - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setBlobOutput", true, paramsArg, stringArg, varArg); - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "blob"); - binding.put("path", annonMap.get("path")); - // According to: https://github.com/Azure/azure-functions-host/issues/6091 - binding.put("dataType", "string"); - String connection = (String) annonMap.get("connection"); - if (connection == null) { - connection = Constants.DEFAULT_STORAGE_CONNECTION_NAME; - } - binding.put("connection", connection); - return binding; - - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobTriggerParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobTriggerParameterHandler.java deleted file mode 100644 index 135451b3..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/blob/BlobTriggerParameterHandler.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.blob; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@BlobTrigger". - */ -public class BlobTriggerParameterHandler extends AbstractParameterHandler { - - public BlobTriggerParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.TRIGGER); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (GeneratorUtil.isByteArrayType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil.createAfFunctionInvocationNode("getBytesFromInputData", true, paramsArg, stringArg); - } else if (GeneratorUtil.isStringType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil.createAfFunctionInvocationNode("getStringConvertedBytesFromInputData", true, paramsArg, - stringArg); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "blobTrigger"); - binding.put("path", annonMap.get("path")); - binding.put("dataType", "binary"); - String connection = (String) annonMap.get("connection"); - if (connection == null) { - connection = Constants.DEFAULT_STORAGE_CONNECTION_NAME; - } - binding.put("connection", connection); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/context/ContextParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/context/ContextParameterHandler.java deleted file mode 100644 index 58d496c3..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/context/ContextParameterHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.context; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.Map; - -/** - * Implementation for the input parameter handler for the Context object. - */ -public class ContextParameterHandler extends AbstractParameterHandler { - - public ContextParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.CONTEXT); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode params = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken("params"))); - PositionalArgumentNode trueArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createBasicLiteralNode(SyntaxKind.BOOLEAN_LITERAL, - NodeFactory.createToken(SyntaxKind.TRUE_KEYWORD))); - - return GeneratorUtil.createAfFunctionInvocationNode("createContext", true, params, trueArg); - } - - @Override - public void postInvocationProcess() { - } - - @Override - public Map generateBinding() { - return null; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBInputParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBInputParameterHandler.java deleted file mode 100644 index 3d94ddf2..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBInputParameterHandler.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.cosmosdb; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ArrayTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.OptionalTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@CosmosDBInput". - */ -public class CosmosDBInputParameterHandler extends AbstractParameterHandler { - - private Map generatedTypeDefinitions; - - public CosmosDBInputParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param, - Map generatedTypeDefinitions) { - super(variableSymbol, param, BindingType.INPUT); - this.generatedTypeDefinitions = generatedTypeDefinitions; - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - boolean singleRecordQuery = this.isSingleRecordQuery(); - PositionalArgumentNode params = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (singleRecordQuery) { - if (GeneratorUtil.isJsonType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, - "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList()))); - return GeneratorUtil - .createAfFunctionInvocationNode("getParsedJsonFromJsonStringFromInputData", true, params, - stringArg); - } else if (GeneratorUtil.isOptionalRecordType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, - "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList()))); - OptionalTypeDescriptorNode optionalTypeDescriptor = (OptionalTypeDescriptorNode) param.typeName(); - TypeDefinitionNode optionalTypeDefinitionNode = - GeneratorUtil.createOptionalTypeDefinitionNode(optionalTypeDescriptor); - generatedTypeDefinitions.put(optionalTypeDefinitionNode.typeName().text(), optionalTypeDefinitionNode); - PositionalArgumentNode typeDesc = - NodeFactory.createPositionalArgumentNode(NodeFactory.createSimpleNameReferenceNode( - NodeFactory.createIdentifierToken(optionalTypeDefinitionNode.typeName().text()))); - ExpressionNode checkedExpr = - GeneratorUtil - .createAfFunctionInvocationNode("getOptionalBallerinaValueFromInputData", true, params, - stringArg, typeDesc); - return NodeFactory.createTypeCastExpressionNode(NodeFactory.createToken(SyntaxKind.LT_TOKEN), - NodeFactory.createTypeCastParamNode(NodeFactory.createEmptyNodeList(), optionalTypeDescriptor), - NodeFactory.createToken(SyntaxKind.GT_TOKEN), checkedExpr); - } else { - throw new AzureFunctionsException( - GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } else { - if (GeneratorUtil.isJsonType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, - "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList()))); - return GeneratorUtil.createAfFunctionInvocationNode("getJsonFromInput", true, params, stringArg); - } else if (GeneratorUtil.isRecordArrayType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, - "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList()))); - ArrayTypeDescriptorNode arrayTypeDescriptor = (ArrayTypeDescriptorNode) param.typeName(); - TypeDefinitionNode arrayTypeDefinitionNode = GeneratorUtil - .createArrayTypeDefinitionNode(arrayTypeDescriptor); - generatedTypeDefinitions.put(arrayTypeDefinitionNode.typeName().text(), arrayTypeDefinitionNode); - PositionalArgumentNode typeDesc = - NodeFactory.createPositionalArgumentNode(NodeFactory.createSimpleNameReferenceNode( - NodeFactory.createIdentifierToken(arrayTypeDefinitionNode.typeName().text()))); - ExpressionNode checkedExpr = - GeneratorUtil.createAfFunctionInvocationNode("getBallerinaValueFromInputData", true, params, - stringArg, typeDesc); - - return NodeFactory.createTypeCastExpressionNode(NodeFactory.createToken(SyntaxKind.LT_TOKEN), - NodeFactory.createTypeCastParamNode(NodeFactory.createEmptyNodeList(), arrayTypeDescriptor), - NodeFactory.createToken(SyntaxKind.GT_TOKEN), checkedExpr); - } else { - throw new AzureFunctionsException( - GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "cosmosDB"); - binding.put("connectionStringSetting", annonMap.get("connectionStringSetting")); - binding.put("databaseName", annonMap.get("databaseName")); - binding.put("collectionName", annonMap.get("collectionName")); - binding.put("id", annonMap.get("id")); - binding.put("sqlQuery", annonMap.get("sqlQuery")); - binding.put("partitionKey", annonMap.get("partitionKey")); - binding.put("preferredLocations", annonMap.get("preferredLocations")); - return binding; - } - - private boolean isSingleRecordQuery() throws AzureFunctionsException { - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - return annonMap.get("id") != null; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBReturnHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBReturnHandler.java deleted file mode 100644 index 0e078d5b..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBReturnHandler.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.cosmosdb; - -import io.ballerina.compiler.api.symbols.TypeDescKind; -import io.ballerina.compiler.api.symbols.TypeSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.IdentifierToken; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractReturnHandler; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Implementation for the return handler annotation "@CosmosDBOutput". - */ -public class CosmosDBReturnHandler extends AbstractReturnHandler { - - public CosmosDBReturnHandler(TypeSymbol retType, AnnotationNode annotation) { - super(retType, annotation); - } - - @Override - public void postInvocationProcess(ExpressionNode returnValueExpr) throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(Constants.PARAMS)); - if (retType.typeKind() == TypeDescKind.JSON) { - PositionalArgumentNode returnExpr = NodeFactory.createPositionalArgumentNode(returnValueExpr); - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setJsonReturn", true, paramsArg, returnExpr); - } else if (retType.typeKind() == TypeDescKind.TYPE_REFERENCE) { - PositionalArgumentNode returnExpr = NodeFactory.createPositionalArgumentNode(returnValueExpr); - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setBallerinaValueAsJsonReturn", true, paramsArg, - returnExpr); - } else if (GeneratorUtil.isRecordArrayType(retType)) { - PositionalArgumentNode returnExpr = NodeFactory.createPositionalArgumentNode(returnValueExpr); - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setBallerinaValueAsJsonReturn", true, paramsArg, - returnExpr); - } else { - IdentifierToken identifier = ((QualifiedNameReferenceNode) annotation.annotReference()).identifier(); - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(returnValueExpr.location(), "AZ0007", - "unsupported.return.annotation", DiagnosticSeverity.ERROR, "Type '" + identifier.text() + "' is " + - "not supported")); - } - //TODO handle record array - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotation); - binding.put("type", "cosmosDB"); - binding.put("connectionStringSetting", annonMap.get("connectionStringSetting")); - binding.put("databaseName", annonMap.get("databaseName")); - binding.put("collectionName", annonMap.get("collectionName")); - binding.put("createIfNotExists", annonMap.get("createIfNotExists")); - binding.put("partitionKey", annonMap.get("partitionKey")); - binding.put("collectionThroughput", annonMap.get("collectionThroughput")); - binding.put("preferredLocations", annonMap.get("preferredLocations")); - binding.put("useMultipleWriteLocations", annonMap.get("useMultipleWriteLocations")); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBTriggerHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBTriggerHandler.java deleted file mode 100644 index 1bd7611f..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/cosmosdb/CosmosDBTriggerHandler.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.cosmosdb; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ArrayTypeDescriptorNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@CosmosDBTrigger". - */ -public class CosmosDBTriggerHandler extends AbstractParameterHandler { - - private Map generatedTypeDefinitions; - - public CosmosDBTriggerHandler(ParameterSymbol variableSymbol, RequiredParameterNode param, - Map generatedTypeDefinitions) { - super(variableSymbol, param, BindingType.TRIGGER); - this.generatedTypeDefinitions = generatedTypeDefinitions; - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode params = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (GeneratorUtil.isJsonType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList()))); - return GeneratorUtil.createAfFunctionInvocationNode("getJsonFromInputData", true, params, stringArg); - } else if (GeneratorUtil.isRecordArrayType(this.variableSymbol)) { - PositionalArgumentNode stringArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), - NodeFactory.createEmptyMinutiaeList()))); - ArrayTypeDescriptorNode arrayTypeDescriptor = (ArrayTypeDescriptorNode) param.typeName(); - TypeDefinitionNode arrayTypeDefinitionNode = GeneratorUtil - .createArrayTypeDefinitionNode(arrayTypeDescriptor); - generatedTypeDefinitions.put(arrayTypeDefinitionNode.typeName().text(), arrayTypeDefinitionNode); - PositionalArgumentNode typeDesc = - NodeFactory.createPositionalArgumentNode(NodeFactory.createSimpleNameReferenceNode( - NodeFactory.createIdentifierToken(arrayTypeDefinitionNode.typeName().text()))); - ExpressionNode checkedExpr = - GeneratorUtil - .createAfFunctionInvocationNode("getBallerinaValueFromInputData", true, params, stringArg, - typeDesc); - return NodeFactory.createTypeCastExpressionNode(NodeFactory.createToken(SyntaxKind.LT_TOKEN), - NodeFactory.createTypeCastParamNode(NodeFactory.createEmptyNodeList(), arrayTypeDescriptor), - NodeFactory.createToken(SyntaxKind.GT_TOKEN), checkedExpr); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "cosmosDBTrigger"); - binding.put("connectionStringSetting", annonMap.get("connectionStringSetting")); - binding.put("databaseName", annonMap.get("databaseName")); - binding.put("collectionName", annonMap.get("collectionName")); - binding.put("leaseConnectionStringSetting", annonMap.get("leaseConnectionStringSetting")); - binding.put("leaseDatabaseName", annonMap.get("leaseDatabaseName")); - binding.put("leaseCollectionName", annonMap.get("leaseCollectionName")); - Boolean createLeaseCollectionIfNotExists = (Boolean) annonMap.get("createLeaseCollectionIfNotExists"); - if (createLeaseCollectionIfNotExists == null) { - createLeaseCollectionIfNotExists = Constants.DEFAULT_COSMOS_DB_CREATELEASECOLLECTIONIFNOTEXISTS; - } - binding.put("createLeaseCollectionIfNotExists", createLeaseCollectionIfNotExists); - binding.put("leasesCollectionThroughput", annonMap.get("leasesCollectionThroughput")); - binding.put("leaseCollectionPrefix", annonMap.get("leaseCollectionPrefix")); - binding.put("feedPollDelay", annonMap.get("feedPollDelay")); - binding.put("leaseAcquireInterval", annonMap.get("leaseAcquireInterval")); - binding.put("leaseExpirationInterval", annonMap.get("leaseExpirationInterval")); - binding.put("leaseRenewInterval", annonMap.get("leaseRenewInterval")); - binding.put("checkpointFrequency", annonMap.get("checkpointFrequency")); - binding.put("maxItemsPerInvocation", annonMap.get("maxItemsPerInvocation")); - binding.put("startFromBeginning", annonMap.get("startFromBeginning")); - binding.put("preferredLocations", annonMap.get("preferredLocations")); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPOutputParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPOutputParameterHandler.java deleted file mode 100644 index 17e860b1..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPOutputParameterHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.http; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Implementation for the output parameter handler annotation "@HTTPOutput". - */ -public class HTTPOutputParameterHandler extends AbstractParameterHandler { - - private SimpleNameReferenceNode var; - - public HTTPOutputParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.OUTPUT); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - if (!GeneratorUtil.isAzurePkgType(this.variableSymbol, "HTTPBinding")) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.typeName().location(), "AZ0007", - "required.type", DiagnosticSeverity.ERROR, "type must be HTTPBinding")); - } - this.var = GeneratorUtil.addAzurePkgRecordVarDef(this.ctx, "HTTPBinding", this.ctx.getNextVarName()); - return this.var; - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(Constants.PARAMS)); - - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - - PositionalArgumentNode varArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(var.name().text())); - - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setHTTPOutput", true, paramsArg, stringArg, varArg); - } - - @Override - public Map generateBinding() { - Map binding = new LinkedHashMap<>(); - binding.put("type", "http"); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPReturnHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPReturnHandler.java deleted file mode 100644 index 43eb4883..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPReturnHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.http; - -import io.ballerina.compiler.api.symbols.TypeDescKind; -import io.ballerina.compiler.api.symbols.TypeSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.IdentifierToken; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractReturnHandler; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * Implementation for the return handler annotation "@HTTPOutput". - */ -public class HTTPReturnHandler extends AbstractReturnHandler { - - public HTTPReturnHandler(TypeSymbol retType, AnnotationNode annotation) { - super(retType, annotation); - } - - @Override - public void postInvocationProcess(ExpressionNode returnValueExpr) throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(Constants.PARAMS)); - if (retType.typeKind() == TypeDescKind.STRING) { - PositionalArgumentNode returnExpr = NodeFactory.createPositionalArgumentNode(returnValueExpr); - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setStringReturn", true, paramsArg, returnExpr); - } else if (retType.typeKind() == TypeDescKind.JSON) { - PositionalArgumentNode returnExpr = NodeFactory.createPositionalArgumentNode(returnValueExpr); - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setJsonReturn", true, paramsArg, returnExpr); - } else if (retType.typeKind() == TypeDescKind.TYPE_REFERENCE && retType.signature().endsWith("HTTPBinding")) { - PositionalArgumentNode returnExpr = NodeFactory.createPositionalArgumentNode(returnValueExpr); - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setHTTPReturn", true, paramsArg, returnExpr); - } else { - IdentifierToken identifier = ((QualifiedNameReferenceNode) annotation.annotReference()).identifier(); - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(returnValueExpr.location(), "AZ0007", - "unsupported.return.annotation", DiagnosticSeverity.ERROR, "Type '" + identifier.text() + "' is " + - "not supported")); - } - } - - @Override - public Map generateBinding() { - Map binding = new LinkedHashMap<>(); - binding.put("type", "http"); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPTriggerParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPTriggerParameterHandler.java deleted file mode 100644 index 3fdb8d4e..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/http/HTTPTriggerParameterHandler.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.http; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@HTTPTrigger". - */ -public class HTTPTriggerParameterHandler extends AbstractParameterHandler { - - public HTTPTriggerParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.TRIGGER); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (GeneratorUtil.isAzurePkgType(this.variableSymbol, "HTTPRequest")) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil - .createAfFunctionInvocationNode("getHTTPRequestFromInputData", true, paramsArg, stringArg); - } else if (GeneratorUtil.isStringType(this.variableSymbol)) { - PositionalArgumentNode stringArg = NodeFactory.createPositionalArgumentNode(NodeFactory - .createBasicLiteralNode(SyntaxKind.STRING_LITERAL, NodeFactory - .createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, "\"" + this.name + "\"", - NodeFactory.createEmptyMinutiaeList(), NodeFactory.createEmptyMinutiaeList()))); - return GeneratorUtil.createAfFunctionInvocationNode("getBodyFromHTTPInputData", true, paramsArg, stringArg); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "httpTrigger"); - binding.put("authLevel", annonMap.get("authLevel")); - binding.put("route", annonMap.get("route")); - binding.put("methods", new String[]{ "get", "post", "put", "delete" }); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/metadata/MetadataBindingParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/metadata/MetadataBindingParameterHandler.java deleted file mode 100644 index 3a548771..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/metadata/MetadataBindingParameterHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.metadata; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@BindingName". - */ -public class MetadataBindingParameterHandler extends AbstractParameterHandler { - - public MetadataBindingParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) - throws AzureFunctionsException { - super(variableSymbol, param, BindingType.METADATA); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - Object name = annonMap.get("name"); - if (name != null) { - this.name = name.toString(); - } - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (GeneratorUtil.isJsonType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil.createAfFunctionInvocationNode("getJsonFromMetadata", true, paramsArg, stringArg); - } else if (GeneratorUtil.isStringType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil.createAfFunctionInvocationNode("getStringFromMetadata", true, paramsArg, stringArg); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() { - } - - @Override - public Map generateBinding() { - return null; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueOutputParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueOutputParameterHandler.java deleted file mode 100644 index 36e288f9..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueOutputParameterHandler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.queue; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the output parameter handler annotation "@QueueOutput". - */ -public class QueueOutputParameterHandler extends AbstractParameterHandler { - - private SimpleNameReferenceNode var; - - public QueueOutputParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.OUTPUT); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - if (!GeneratorUtil.isAzurePkgType(this.variableSymbol, "StringOutputBinding")) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.typeName().location(), "AZ0007", - "required.type", DiagnosticSeverity.ERROR, "type must be StringOutputBinding")); - } - this.var = GeneratorUtil.addAzurePkgRecordVarDef(this.ctx, "StringOutputBinding", this.ctx.getNextVarName()); - return this.var; - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef("params")); - - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - - PositionalArgumentNode varArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(var.name().text())); - - GeneratorUtil.addAzurePkgFunctionCallStatement(this.ctx, "setStringOutput", true, paramsArg, stringArg, varArg); - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "queue"); - binding.put("queueName", annonMap.get("queueName")); - String connection = (String) annonMap.get("connection"); - if (connection == null) { - connection = Constants.DEFAULT_STORAGE_CONNECTION_NAME; - } - binding.put("connection", connection); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueTriggerHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueTriggerHandler.java deleted file mode 100644 index 71dc24da..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/queue/QueueTriggerHandler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.queue; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@QueueTrigger". - */ -public class QueueTriggerHandler extends AbstractParameterHandler { - - public QueueTriggerHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.TRIGGER); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken(Constants.PARAMS))); - if (GeneratorUtil.isStringType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil - .createAfFunctionInvocationNode("getJsonStringFromInputData", true, paramsArg, stringArg); - } else if (GeneratorUtil.isJsonType(this.variableSymbol)) { - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil - .createAfFunctionInvocationNode("getParsedJsonFromJsonStringFromInputData", true, paramsArg, - stringArg); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "queueTrigger"); - binding.put("queueName", annonMap.get("queueName")); - String connection = (String) annonMap.get("connection"); - if (connection == null) { - connection = Constants.DEFAULT_STORAGE_CONNECTION_NAME; - } - binding.put("connection", connection); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/timer/TimerTriggerHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/timer/TimerTriggerHandler.java deleted file mode 100644 index 98e467ce..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/timer/TimerTriggerHandler.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.timer; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the input parameter handler annotation "@TimerTrigger". - */ -public class TimerTriggerHandler extends AbstractParameterHandler { - - public TimerTriggerHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.TRIGGER); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - if (GeneratorUtil.isJsonType(this.variableSymbol)) { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - NodeFactory.createSimpleNameReferenceNode(NodeFactory.createIdentifierToken("params"))); - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - return GeneratorUtil.createAfFunctionInvocationNode("getJsonFromInputData", true, paramsArg, stringArg); - } else { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(this.param.typeName().location(), "AZ0008", - "unsupported.param.type", DiagnosticSeverity.ERROR, - "type '" + this.param.typeName().toString() + "'" + - " is not supported")); - } - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "timerTrigger"); - binding.put("schedule", annonMap.get("schedule")); - Boolean runOnStartup = (Boolean) annonMap.get("runOnStartup"); - if (runOnStartup == null) { - runOnStartup = Constants.DEFAULT_TIMER_TRIGGER_RUNONSTARTUP; - } - binding.put("runOnStartup", runOnStartup); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/twilio/TwilioSmsOutputParameterHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/twilio/TwilioSmsOutputParameterHandler.java deleted file mode 100644 index 77771b4f..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/handlers/twilio/TwilioSmsOutputParameterHandler.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.handlers.twilio; - -import io.ballerina.compiler.api.symbols.ParameterSymbol; -import io.ballerina.compiler.syntax.tree.AnnotationNode; -import io.ballerina.compiler.syntax.tree.ExpressionNode; -import io.ballerina.compiler.syntax.tree.NodeFactory; -import io.ballerina.compiler.syntax.tree.PositionalArgumentNode; -import io.ballerina.compiler.syntax.tree.RequiredParameterNode; -import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.AzureFunctionsException; -import org.ballerinax.azurefunctions.generator.BindingType; -import org.ballerinax.azurefunctions.generator.Constants; -import org.ballerinax.azurefunctions.generator.GeneratorUtil; -import org.ballerinax.azurefunctions.generator.handlers.AbstractParameterHandler; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Implementation for the output parameter handler annotation "@TwilioSmsOutput". - */ -public class TwilioSmsOutputParameterHandler extends AbstractParameterHandler { - - private SimpleNameReferenceNode var; - - public TwilioSmsOutputParameterHandler(ParameterSymbol variableSymbol, RequiredParameterNode param) { - super(variableSymbol, param, BindingType.OUTPUT); - } - - @Override - public ExpressionNode invocationProcess() throws AzureFunctionsException { - if (!GeneratorUtil.isAzurePkgType(this.variableSymbol, "TwilioSmsOutputBinding")) { - throw new AzureFunctionsException(GeneratorUtil.getAFDiagnostic(param.typeName().location(), "AZ0007", - "required.type", DiagnosticSeverity.ERROR, "Type must be 'TwilioSmsOutputBinding'")); - } - this.var = GeneratorUtil.addAzurePkgRecordVarDef(this.ctx, "TwilioSmsOutputBinding", this.ctx.getNextVarName()); - return this.var; - } - - @Override - public void postInvocationProcess() throws AzureFunctionsException { - PositionalArgumentNode paramsArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef("params")); - - PositionalArgumentNode stringArg = - NodeFactory.createPositionalArgumentNode(GeneratorUtil.createStringLiteral(this.name)); - - PositionalArgumentNode varArg = NodeFactory.createPositionalArgumentNode( - GeneratorUtil.createVariableRef(var.name().text())); - - GeneratorUtil - .addAzurePkgFunctionCallStatement(this.ctx, "setTwilioSmsOutput", true, paramsArg, stringArg, varArg); - } - - @Override - public Map generateBinding() throws AzureFunctionsException { - Map binding = new LinkedHashMap<>(); - Optional annotationNode = GeneratorUtil.extractAzureFunctionAnnotation(param.annotations()); - Map annonMap = GeneratorUtil.extractAnnotationKeyValues(annotationNode.orElseThrow()); - binding.put("type", "twilioSms"); - binding.put("from", annonMap.get("fromNumber")); - String accountSidSetting = (String) annonMap.get("accountSidSetting"); - if (accountSidSetting == null) { - accountSidSetting = Constants.DEFAULT_TWILIO_ACCOUNT_SID_SETTING; - } - binding.put("accountSidSetting", accountSidSetting); - String authTokenSetting = (String) annonMap.get("authTokenSetting"); - if (authTokenSetting == null) { - authTokenSetting = Constants.DEFAULT_TWILIO_AUTH_TOKEN_SETTING; - } - binding.put("authTokenSetting", authTokenSetting); - return binding; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/AzureFunctionsCodeAnalyzerTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/AzureFunctionsCodeAnalyzerTask.java deleted file mode 100644 index 436522a0..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/AzureFunctionsCodeAnalyzerTask.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.validators; - -import io.ballerina.compiler.syntax.tree.Node; -import io.ballerina.compiler.syntax.tree.NodeLocation; -import io.ballerina.projects.Document; -import io.ballerina.projects.DocumentId; -import io.ballerina.projects.Module; -import io.ballerina.projects.ModuleId; -import io.ballerina.projects.Package; -import io.ballerina.projects.ProjectKind; -import io.ballerina.projects.plugins.AnalysisTask; -import io.ballerina.projects.plugins.CompilationAnalysisContext; -import io.ballerina.tools.diagnostics.Diagnostic; -import io.ballerina.tools.diagnostics.DiagnosticFactory; -import io.ballerina.tools.diagnostics.DiagnosticInfo; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.Constants; - -import java.util.ArrayList; -import java.util.List; - -/*** - * Code analyzer for azure function specific validations. - * - * @since 2.0.0 - */ -public class AzureFunctionsCodeAnalyzerTask implements AnalysisTask { - - @Override - public void perform(CompilationAnalysisContext compilationAnalysisContext) { - Package currentPackage = compilationAnalysisContext.currentPackage(); - List diagnostics = new ArrayList<>(); - if (currentPackage.project().kind() != ProjectKind.BUILD_PROJECT) { - DiagnosticInfo diagnosticInfo = new DiagnosticInfo("A000", "azure functions are only allowed in ballerina" + - " projects", DiagnosticSeverity.ERROR); - DocumentId firsDocument = currentPackage.getDefaultModule().documentIds().iterator().next(); - Document document = currentPackage.getDefaultModule().document(firsDocument); - NodeLocation location = document.syntaxTree().rootNode().location(); - diagnostics.add(DiagnosticFactory.createDiagnostic(diagnosticInfo, location)); - } - - - for (ModuleId moduleId : currentPackage.moduleIds()) { - Module module = currentPackage.module(moduleId); - for (DocumentId documentId : module.documentIds()) { - Document document = module.document(documentId); - Node rootNode = document.syntaxTree().rootNode(); - if (document.name().startsWith(Constants.AZ_FUNCTION_PREFIX)) { - continue; - } - diagnostics.addAll(validateMainFunction(rootNode)); - if (module.isDefaultModule()) { - continue; - } - diagnostics.addAll(validateSubmoduleDocument(rootNode)); - } - } - diagnostics.forEach(compilationAnalysisContext::reportDiagnostic); - } - - private List validateMainFunction(Node node) { - List diagnostics = new ArrayList<>(); - node.accept(new MainFunctionValidator(diagnostics)); - return diagnostics; - } - - private List validateSubmoduleDocument(Node node) { - List diagnostics = new ArrayList<>(); - node.accept(new SubmoduleValidator(diagnostics)); - return diagnostics; - } - -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/MainFunctionValidator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/MainFunctionValidator.java deleted file mode 100644 index 8691d512..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/MainFunctionValidator.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.validators; - -import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; -import io.ballerina.compiler.syntax.tree.NodeVisitor; -import io.ballerina.tools.diagnostics.Diagnostic; -import io.ballerina.tools.diagnostics.DiagnosticFactory; -import io.ballerina.tools.diagnostics.DiagnosticInfo; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.Constants; - -import java.util.List; - -/** - * Responsible for disallowing main function in a ballerina document. - * - * @since 2.0.0 - */ -public class MainFunctionValidator extends NodeVisitor { - private final List diagnostics; - private boolean isAzureFunctionsImportExist = false; - - public MainFunctionValidator(List diagnostics) { - this.diagnostics = diagnostics; - } - - @Override - public void visit(ImportDeclarationNode importDeclarationNode) { - if (importDeclarationNode.orgName().isEmpty()) { - return; - } - String orgName = importDeclarationNode.orgName().get().orgName().text(); - if (!Constants.AZURE_FUNCTIONS_PACKAGE_ORG.equals(orgName)) { - return; - } - if (importDeclarationNode.moduleName().size() != 1) { - return; - } - String moduleName = importDeclarationNode.moduleName().get(0).text(); - if (Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(moduleName)) { - isAzureFunctionsImportExist = true; - } - } - - @Override - public void visit(FunctionDefinitionNode functionDefinitionNode) { - if (!isAzureFunctionsImportExist) { - return; - } - String text = functionDefinitionNode.functionName().text(); - if (Constants.MAIN_FUNC_NAME.equals(text)) { - DiagnosticInfo diagnosticInfo = new DiagnosticInfo("AZ010", "main function is not allowed in " + - "azure functions", DiagnosticSeverity.ERROR); - diagnostics.add(DiagnosticFactory.createDiagnostic(diagnosticInfo, - functionDefinitionNode.location())); - } - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/SubmoduleValidator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/SubmoduleValidator.java deleted file mode 100644 index ca8de0b9..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/validators/SubmoduleValidator.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions.generator.validators; - -import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; -import io.ballerina.compiler.syntax.tree.NodeVisitor; -import io.ballerina.tools.diagnostics.Diagnostic; -import io.ballerina.tools.diagnostics.DiagnosticFactory; -import io.ballerina.tools.diagnostics.DiagnosticInfo; -import io.ballerina.tools.diagnostics.DiagnosticSeverity; -import org.ballerinax.azurefunctions.generator.Constants; - -import java.util.List; - -/** - * Responsible for disallowing azure functions in submodule document. - * - * @since 2.0.0 - */ -public class SubmoduleValidator extends NodeVisitor { - private final List diagnostics; - - public SubmoduleValidator(List diagnostics) { - this.diagnostics = diagnostics; - } - - @Override - public void visit(ImportDeclarationNode importDeclarationNode) { - if (importDeclarationNode.orgName().isEmpty()) { - return; - } - String orgName = importDeclarationNode.orgName().get().orgName().text(); - if (!Constants.AZURE_FUNCTIONS_PACKAGE_ORG.equals(orgName)) { - return; - } - if (importDeclarationNode.moduleName().size() != 1) { - return; - } - String moduleName = importDeclarationNode.moduleName().get(0).text(); - if (Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(moduleName)) { - DiagnosticInfo diagnosticInfo = new DiagnosticInfo("AZ011", "azure functions is not allowed inside" + - " sub modules", DiagnosticSeverity.ERROR); - this.diagnostics.add(DiagnosticFactory.createDiagnostic(diagnosticInfo, - importDeclarationNode.location())); - } - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java new file mode 100644 index 00000000..4e3f8431 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java @@ -0,0 +1,45 @@ +package org.ballerinax.azurefunctions.service; + +import com.google.gson.JsonObject; + +/** + * Represents a binding in the functions.json. + * + * @since 2.0.0 + */ +public abstract class Binding { + private String triggerType; + private String varName; + private String direction; + + public Binding(String triggerType, String direction) { + this.triggerType = triggerType; + this.direction = direction; + } + + public String getTriggerType() { + return triggerType; + } + + public String getVarName() { + return varName; + } + + public String getDirection() { + return direction; + } + + public void setTriggerType(String triggerType) { + this.triggerType = triggerType; + } + + public void setVarName(String varName) { + this.varName = varName; + } + + public void setDirection(String direction) { + this.direction = direction; + } + + public abstract JsonObject getJsonObject(); +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java new file mode 100644 index 00000000..95deea23 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java @@ -0,0 +1,15 @@ +package org.ballerinax.azurefunctions.service; + +import org.ballerinax.azurefunctions.Constants; + +/** + * Represents an Input Binding in Azure Functions. + * + * @since 2.0.0 + */ +public abstract class InputBinding extends Binding { + + public InputBinding(String triggerType) { + super(triggerType, Constants.DIRECTION_IN); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java new file mode 100644 index 00000000..d943844c --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java @@ -0,0 +1,37 @@ +package org.ballerinax.azurefunctions.service; + +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.service.blob.BlobInputBinding; +import org.ballerinax.azurefunctions.service.cosmosdb.CosmosDBInputBinding; + +import java.util.Optional; + +/** + * Represents an Input Binding builder for Azure Function services. + * + * @since 2.0.0 + */ +public class InputBindingBuilder { + + public Optional getInputBinding(NodeList annotations, String varName) { + for (AnnotationNode annotation : annotations) { + Node annotRef = annotation.annotReference(); + if (annotRef.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { + QualifiedNameReferenceNode annotationRef = (QualifiedNameReferenceNode) annotRef; + String annotationText = annotationRef.identifier().text(); + if (annotationText.equals("CosmosDBInput")) { + return Optional.of(new CosmosDBInputBinding(annotation, varName)); + } + if (annotationText.equals("BlobInput")) { + return Optional.of(new BlobInputBinding(annotation, varName)); + } + //TODO Add other stuff + } + } + return Optional.empty(); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java new file mode 100644 index 00000000..3ed23770 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java @@ -0,0 +1,14 @@ +package org.ballerinax.azurefunctions.service; + +import org.ballerinax.azurefunctions.Constants; + +/** + * Represents an Output Binding in Azure Functions. + * + * @since 2.0.0 + */ +public abstract class OutputBinding extends Binding { + public OutputBinding(String triggerType) { + super(triggerType, Constants.DIRECTION_OUT); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java new file mode 100644 index 00000000..e12a7fa9 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java @@ -0,0 +1,48 @@ +package org.ballerinax.azurefunctions.service; + +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.service.blob.BlobOutputBinding; +import org.ballerinax.azurefunctions.service.cosmosdb.CosmosDBOutputBinding; +import org.ballerinax.azurefunctions.service.http.HTTPOutputBinding; +import org.ballerinax.azurefunctions.service.queue.QueueOutputBinding; +import org.ballerinax.azurefunctions.service.twilio.TwilioSmsOutputBinding; + +import java.util.Optional; + +/** + * Represents an Output Binding builder for Azure Function services. + * + * @since 2.0.0 + */ +public class OutputBindingBuilder { + + public Optional getOutputBinding(NodeList nodes) { + for (AnnotationNode annotationNode : nodes) { + Node node = annotationNode.annotReference(); + if (node.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { + continue; + } + QualifiedNameReferenceNode qualifiedNameReferenceNode = (QualifiedNameReferenceNode) node; + String annotationName = qualifiedNameReferenceNode.identifier().text(); + switch (annotationName) { + case "QueueOutput": + return Optional.of(new QueueOutputBinding(annotationNode)); + case "HTTPOutput": + return Optional.of(new HTTPOutputBinding(annotationNode)); + case "CosmosDBOutput": + return Optional.of(new CosmosDBOutputBinding(annotationNode)); + case "TwilioSmsOutput": + return Optional.of(new TwilioSmsOutputBinding(annotationNode)); + case "BlobOutput": + return Optional.of(new BlobOutputBinding(annotationNode)); + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + return Optional.empty(); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java new file mode 100644 index 00000000..c734e386 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java @@ -0,0 +1,113 @@ +package org.ballerinax.azurefunctions.service; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.RequiredParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.FunctionContext; +import org.ballerinax.azurefunctions.Util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Represents a queue trigger binding in function.json. + * + * @since 2.0.0 + */ +public abstract class RemoteTriggerBinding extends TriggerBinding { + private String methodName; + private String annotationName; + + public RemoteTriggerBinding(String triggerType, String methodName, + String annotationName, ServiceDeclarationNode serviceDeclarationNode, + SemanticModel semanticModel) { + super(triggerType); + this.serviceDeclarationNode = serviceDeclarationNode; + this.semanticModel = semanticModel; + this.methodName = methodName; + this.annotationName = annotationName; + } + + @Override + public List getBindings() { + Optional queueTrigger = getListenerAnnotation(this.serviceDeclarationNode, this.annotationName); + List functionContexts = new ArrayList<>(); + if (queueTrigger.isEmpty()) { + return functionContexts; + } + getAnnotation(queueTrigger.get()); + String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + NodeList members = this.serviceDeclarationNode.members(); + for (Node node : members) { + List bindings = new ArrayList<>(); + if (node.kind() != SyntaxKind.OBJECT_METHOD_DEFINITION) { + continue; + } + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; + String method = functionDefinitionNode.functionName().text(); + if (!method.equals(this.methodName)) { + continue; + } + + for (ParameterNode parameterNode : functionDefinitionNode.functionSignature().parameters()) { + if (parameterNode.kind() != SyntaxKind.REQUIRED_PARAM) { + continue; + } + RequiredParameterNode reqParam = (RequiredParameterNode) parameterNode; + if (reqParam.paramName().isEmpty()) { + continue; + } + String variableName = reqParam.paramName().get().text(); + if (isPayloadAnnotationExist(reqParam.annotations())) { + this.setVarName(variableName); + continue; + } + + InputBindingBuilder inputBuilder = new InputBindingBuilder(); + Optional inputBinding = inputBuilder.getInputBinding(reqParam.annotations(), variableName); + if (inputBinding.isPresent()) { + bindings.add(inputBinding.get()); + continue; + } + + } + bindings.add(this); +// ParameterNode parameterNode = functionDefinitionNode.functionSignature().parameters().get(0); +// //TODO valid +// if (parameterNode.kind() != SyntaxKind.REQUIRED_PARAM) { +// continue; +// } +// RequiredParameterNode reqParam = (RequiredParameterNode) parameterNode; +// String paramName = reqParam.paramName().orElseThrow().text(); +// bindings.add(new QueueTriggerBinding(paramName, queueName)); + + ReturnTypeDescriptorNode returnTypeDescriptorNode = + functionDefinitionNode.functionSignature().returnTypeDesc().get(); //TODO recheck if return is must + OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); + Optional returnBinding = outputBuilder.getOutputBinding(returnTypeDescriptorNode.annotations()); + bindings.add(returnBinding.orElseThrow()); //TODO handle in code analyzer + functionContexts.add(new FunctionContext(servicePath.replace("/", ""), bindings)); //TODO remove / + } + return functionContexts; + } + + private void getAnnotation(AnnotationNode queueTrigger) { + SeparatedNodeList fields = queueTrigger.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + protected abstract void extractValueFromAnnotation(SpecificFieldNode fieldNode); +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java new file mode 100644 index 00000000..89f4fc9a --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java @@ -0,0 +1,183 @@ +package org.ballerinax.azurefunctions.service; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.symbols.UnionTypeSymbol; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import org.ballerinax.azurefunctions.service.blob.BlobTriggerBinding; +import org.ballerinax.azurefunctions.service.cosmosdb.CosmosDBTriggerBinding; +import org.ballerinax.azurefunctions.service.http.HTTPTriggerBinding; +import org.ballerinax.azurefunctions.service.queue.QueueTriggerBinding; +import org.ballerinax.azurefunctions.service.timer.TimerTriggerBinding; + +import java.util.Optional; + +/** + * Represents the base handler for each azure service. + * + * @since 2.0.0 + */ +public abstract class ServiceHandler { + + public static TriggerBinding getBuilder(ServiceDeclarationNode svcDeclarationNode, SemanticModel semanticModel) { + SeparatedNodeList expressions = svcDeclarationNode.expressions(); + for (ExpressionNode expressionNode : expressions) { + Optional typeSymbol = semanticModel.typeOf(expressionNode); + if (typeSymbol.isEmpty()) { + continue; + } + TypeReferenceTypeSymbol typeSymbol1; + if (typeSymbol.get().typeKind() == TypeDescKind.UNION) { + UnionTypeSymbol union = (UnionTypeSymbol) typeSymbol.get(); + typeSymbol1 = (TypeReferenceTypeSymbol) union.memberTypeDescriptors().get(0); + + } else { + typeSymbol1 = (TypeReferenceTypeSymbol) typeSymbol.get(); + } + Optional name = typeSymbol1.definition().getName(); + if (name.isEmpty()) { + continue; + } + + String serviceTypeName = name.get(); + switch (serviceTypeName) { + case "HTTPListener": + return new HTTPTriggerBinding(svcDeclarationNode, semanticModel); + case "QueueListener": + return new QueueTriggerBinding(svcDeclarationNode, semanticModel); + case "CosmosDBListener": + return new CosmosDBTriggerBinding(svcDeclarationNode, semanticModel); + case "TimerListener": + return new TimerTriggerBinding(svcDeclarationNode, semanticModel); + case "BlobListener": + return new BlobTriggerBinding(svcDeclarationNode, semanticModel); + default: + //TODO Change + throw new RuntimeException("Unsupported Listener type"); + } + } + //TODO Change + throw new RuntimeException("Unsupported Listener type"); + } + +// protected boolean isPayloadAnnotationExist(NodeList nodes) { +// for (AnnotationNode annotation : nodes) { +// Node annotRef = annotation.annotReference(); +// if (annotRef.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { +// QualifiedNameReferenceNode annotationRef = (QualifiedNameReferenceNode) annotRef; +// if (annotationRef.identifier().text().equals("Payload")) { //Add other stuff +// return true; +// } +// } +// } +// return false; +// } + +// public Optional getReturnBinding(ReturnTypeDescriptorNode returnTypeDescriptorNode) { +// NodeList annotations = returnTypeDescriptorNode.annotations(); +// for (AnnotationNode annotationNode : annotations) { +// Node node = annotationNode.annotReference(); +// if (node.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { +// continue; +// } +// QualifiedNameReferenceNode qualifiedNameReferenceNode = (QualifiedNameReferenceNode) node; +// String annotationName = qualifiedNameReferenceNode.identifier().text(); +// switch (annotationName) { +// case "QueueOutput": +// MappingFieldNode mappingFieldNode = annotationNode.annotValue().orElseThrow().fields().get(0); +// if (mappingFieldNode.kind() == SyntaxKind.SPECIFIC_FIELD) { +// SpecificFieldNode specificFieldNode = (SpecificFieldNode) mappingFieldNode; +// Optional value = Util.extractValueFromAnnotationField(specificFieldNode); +// if (value.isPresent()) { +// String text = ((IdentifierToken) specificFieldNode.fieldName()).text(); +// if (text.equals("queueName")) { +// return Optional.of(new QueueOutputBinding(value.get())); +// } +// } +// } +// break; +// case "HTTPOutput": +// return Optional.of(new HTTPOutputBinding()); +// case "CosmosDBOutput": +// CosmosDBOutputBinding cosmosDBOutputBinding = new CosmosDBOutputBinding(); +// SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); +// for (MappingFieldNode mappingField : fields) { +// if (mappingField.kind() == SyntaxKind.SPECIFIC_FIELD) { +// SpecificFieldNode specificFieldNode = (SpecificFieldNode) mappingField; +// Optional value = Util.extractValueFromAnnotationField(specificFieldNode); +// if (value.isPresent()) { +// String text = ((IdentifierToken) specificFieldNode.fieldName()).text(); +// switch (text) { +// case "connectionStringSetting": +// cosmosDBOutputBinding.setConnectionStringSetting(value.get()); +// break; +// case "databaseName": +// cosmosDBOutputBinding.setDatabaseName(value.get()); +// break; +// case "collectionName": +// cosmosDBOutputBinding.setCollectionName(value.get()); +// break; +// default: +// throw new RuntimeException("Unexpected property in the annotation"); +// } +// } +// } +// } +// return Optional.of(cosmosDBOutputBinding); +// } +// } +// return Optional.empty(); +// } + +// public Optional getListenerAnnotation(ServiceDeclarationNode svcDeclNode, String annotationName) { +// //TODO handle inline decl +// for (ExpressionNode expression : svcDeclNode.expressions()) { +// Optional symbol = this.semanticModel.symbol(expression); +// if (symbol.isEmpty()) { +// continue; +// } +// Symbol listenerSymbol = symbol.get(); +// if (listenerSymbol.kind() != SymbolKind.VARIABLE) { +// continue; +// } +// VariableSymbol variableSymbol = (VariableSymbol) listenerSymbol; +// ListenerDeclarationNode listenerDeclarationNode = (ListenerDeclarationNode) findNode(variableSymbol); +// Optional metadata = listenerDeclarationNode.metadata(); +// if (metadata.isEmpty()) { +// continue; +// } +// NodeList annotations = metadata.get().annotations(); +// for (AnnotationNode annotationNode : annotations) { +// Optional typeSymbol = this.semanticModel.symbol(annotationNode); +// if (typeSymbol.isEmpty()) { +// continue; +// } +// Symbol annotationType = typeSymbol.get(); +// Optional name = annotationType.getName(); +// if (name.isEmpty()) { +// continue; +// } +// if (name.get().equals(annotationName)) { +// return Optional.of(annotationNode); +// } +// } +// +//// List annotations = variableSymbol.annotations(); +//// for (AnnotationSymbol annotationSymbol : annotations) { +//// Optional name = annotationSymbol.getName(); +//// if (name.isEmpty()) { +//// continue; +//// } +//// if (name.get().equals(annotationName)) { +//// return Optional.of(annotationSymbol); +//// } +//// } +// } +// +// return Optional.empty(); +// } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java new file mode 100644 index 00000000..4559decb --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java @@ -0,0 +1,99 @@ +package org.ballerinax.azurefunctions.service; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.SymbolKind; +import io.ballerina.compiler.api.symbols.VariableSymbol; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.ListenerDeclarationNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.Constants; +import org.ballerinax.azurefunctions.FunctionContext; +import org.ballerinax.azurefunctions.Util; + +import java.util.List; +import java.util.Optional; + +/** + * Represents an Trigger Binding in Azure Functions. + * + * @since 2.0.0 + */ +public abstract class TriggerBinding extends Binding { + protected ServiceDeclarationNode serviceDeclarationNode; + protected SemanticModel semanticModel; + + public TriggerBinding(String triggerType) { + super(triggerType, Constants.DIRECTION_IN); + } + + public abstract List getBindings(); + + public Optional getListenerAnnotation(ServiceDeclarationNode svcDeclNode, String annotationName) { + //TODO handle inline decl + for (ExpressionNode expression : svcDeclNode.expressions()) { + Optional symbol = this.semanticModel.symbol(expression); + if (symbol.isEmpty()) { + continue; + } + Symbol listenerSymbol = symbol.get(); + if (listenerSymbol.kind() != SymbolKind.VARIABLE) { + continue; + } + VariableSymbol variableSymbol = (VariableSymbol) listenerSymbol; + ListenerDeclarationNode listenerDeclarationNode = + (ListenerDeclarationNode) Util.findNode(svcDeclNode, variableSymbol); + Optional metadata = listenerDeclarationNode.metadata(); + if (metadata.isEmpty()) { + continue; + } + NodeList annotations = metadata.get().annotations(); + for (AnnotationNode annotationNode : annotations) { + Optional typeSymbol = this.semanticModel.symbol(annotationNode); + if (typeSymbol.isEmpty()) { + continue; + } + Symbol annotationType = typeSymbol.get(); + Optional name = annotationType.getName(); + if (name.isEmpty()) { + continue; + } + if (name.get().equals(annotationName)) { + return Optional.of(annotationNode); + } + } + +// List annotations = variableSymbol.annotations(); +// for (AnnotationSymbol annotationSymbol : annotations) { +// Optional name = annotationSymbol.getName(); +// if (name.isEmpty()) { +// continue; +// } +// if (name.get().equals(annotationName)) { +// return Optional.of(annotationSymbol); +// } +// } + } + + return Optional.empty(); + } + + protected boolean isPayloadAnnotationExist(NodeList nodes) { + for (AnnotationNode annotation : nodes) { + Node annotRef = annotation.annotReference(); + if (annotRef.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { + QualifiedNameReferenceNode annotationRef = (QualifiedNameReferenceNode) annotRef; + if (annotationRef.identifier().text().equals("Payload")) { //Add other stuff + return true; + } + } + } + return false; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java new file mode 100644 index 00000000..8d1e234f --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java @@ -0,0 +1,85 @@ +package org.ballerinax.azurefunctions.service.blob; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.InputBinding; + +import java.util.Optional; + +/** + * Represents CosmosDB Binding in functions.json. + */ +public class BlobInputBinding extends InputBinding { + + private String path; + private String connection = "AzureWebJobsStorage"; + private String dataType = "binary"; + + public BlobInputBinding(AnnotationNode queueTrigger, String varName) { + super("blob"); + this.setVarName(varName); + SeparatedNodeList fields = queueTrigger.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + private void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "path": + value.ifPresent(this::setPath); + break; + case "connection": + value.ifPresent(this::setConnection); + break; + case "dataType": + value.ifPresent(this::setDataType); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getConnection() { + return connection; + } + + public void setConnection(String connection) { + this.connection = connection; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("name", this.getVarName()); + inputTrigger.addProperty("path", this.path); + inputTrigger.addProperty("connection", this.connection); + inputTrigger.addProperty("dataType", this.dataType); + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java new file mode 100644 index 00000000..223b726e --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java @@ -0,0 +1,87 @@ +package org.ballerinax.azurefunctions.service.blob; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.OutputBinding; + +import java.util.Optional; + +/** + * Represents Queue output binding in functions.json. + * + * @since 2.0.0 + */ +public class BlobOutputBinding extends OutputBinding { + + private String path; + private String connection = "AzureWebJobsStorage"; + private String dataType = "string"; + + public BlobOutputBinding(AnnotationNode annotationNode) { + super("blob"); + this.setVarName("outMsg"); + SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getConnection() { + return connection; + } + + public void setConnection(String connection) { + this.connection = connection; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + private void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "path": + value.ifPresent(this::setPath); + break; + case "connection": + value.ifPresent(this::setConnection); + break; + case "dataType": + value.ifPresent(this::setDataType); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + @Override + public JsonObject getJsonObject() { + JsonObject output = new JsonObject(); + output.addProperty("type", this.getTriggerType()); + output.addProperty("direction", this.getDirection()); + output.addProperty("name", this.getVarName()); + output.addProperty("path", this.path); + output.addProperty("connection", this.connection); + output.addProperty("dataType", this.dataType); + return output; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java new file mode 100644 index 00000000..b4099e32 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java @@ -0,0 +1,84 @@ +package org.ballerinax.azurefunctions.service.blob; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.RemoteTriggerBinding; + +import java.util.Optional; + +/** + * Represents a HTTP Trigger binding in functions.json. + * + * @since 2.0.0 + */ +public class BlobTriggerBinding extends RemoteTriggerBinding { + + private String path; + private String connection = "AzureWebJobsStorage"; + private String dataType = "binary"; + + public BlobTriggerBinding(ServiceDeclarationNode serviceDeclarationNode, SemanticModel semanticModel) { + super("blobTrigger", "onUpdated", Constants.ANNOTATION_BLOB_TRIGGER, serviceDeclarationNode, + semanticModel); + } + + @Override + protected void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "path": + value.ifPresent(this::setPath); + break; + case "connection": + value.ifPresent(this::setConnection); + break; + case "dataType": + value.ifPresent(this::setDataType); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getConnection() { + return connection; + } + + public void setConnection(String connection) { + this.connection = connection; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("name", this.getVarName()); + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("path", this.path); + inputTrigger.addProperty("dataType", this.dataType); + inputTrigger.addProperty("connection", this.connection); + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java new file mode 100644 index 00000000..e192eec8 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java @@ -0,0 +1,130 @@ +package org.ballerinax.azurefunctions.service.cosmosdb; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.InputBinding; + +import java.util.Optional; + +/** + * Represents CosmosDB Binding in functions.json. + */ +public class CosmosDBInputBinding extends InputBinding { + + private String connectionStringSetting; + private String databaseName; + private String collectionName; + private String id; + private String partitionKey; + private String sqlQuery; + + public CosmosDBInputBinding(AnnotationNode queueTrigger, String varName) { + super("cosmosDB"); + this.setVarName(varName); + SeparatedNodeList fields = queueTrigger.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + private void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "connectionStringSetting": + value.ifPresent(this::setConnectionStringSetting); + break; + case "databaseName": + value.ifPresent(this::setDatabaseName); + break; + case "collectionName": + value.ifPresent(this::setCollectionName); + break; + case "sqlQuery": + value.ifPresent(this::setSqlQuery); + break; + case "id": + value.ifPresent(this::setId); + break; + case "partitionKey": + value.ifPresent(this::setPartitionKey); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getConnectionStringSetting() { + return connectionStringSetting; + } + + public void setConnectionStringSetting(String connectionStringSetting) { + this.connectionStringSetting = connectionStringSetting; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public String getCollectionName() { + return collectionName; + } + + public void setCollectionName(String collectionName) { + this.collectionName = collectionName; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPartitionKey() { + return partitionKey; + } + + public void setPartitionKey(String partitionKey) { + this.partitionKey = partitionKey; + } + + public String getSqlQuery() { + return sqlQuery; + } + + public void setSqlQuery(String sqlQuery) { + this.sqlQuery = sqlQuery; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("name", this.getVarName()); + inputTrigger.addProperty("connectionStringSetting", this.connectionStringSetting); + inputTrigger.addProperty("databaseName", databaseName); + inputTrigger.addProperty("collectionName", this.collectionName); + if (this.partitionKey != null) { + inputTrigger.addProperty("partitionKey", this.partitionKey); + } + if (this.id != null) { + inputTrigger.addProperty("id", this.id); + } + if (this.sqlQuery != null) { + inputTrigger.addProperty("sqlQuery", this.sqlQuery); + } + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java new file mode 100644 index 00000000..4f3c9631 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java @@ -0,0 +1,87 @@ +package org.ballerinax.azurefunctions.service.cosmosdb; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.OutputBinding; + +import java.util.Optional; + +/** + * Represents Queue output binding in functions.json. + * + * @since 2.0.0 + */ +public class CosmosDBOutputBinding extends OutputBinding { + + private String connectionStringSetting; + private String databaseName; + private String collectionName; + + public CosmosDBOutputBinding(AnnotationNode annotationNode) { + super("cosmosDB"); + this.setVarName("outMsg"); + SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + private void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "connectionStringSetting": + value.ifPresent(this::setConnectionStringSetting); + break; + case "databaseName": + value.ifPresent(this::setDatabaseName); + break; + case "collectionName": + value.ifPresent(this::setCollectionName); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getConnectionStringSetting() { + return connectionStringSetting; + } + + public void setConnectionStringSetting(String connectionStringSetting) { + this.connectionStringSetting = connectionStringSetting; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public String getCollectionName() { + return collectionName; + } + + public void setCollectionName(String collectionName) { + this.collectionName = collectionName; + } + + @Override + public JsonObject getJsonObject() { + JsonObject output = new JsonObject(); + output.addProperty("type", this.getTriggerType()); + output.addProperty("connectionStringSetting", this.connectionStringSetting); + output.addProperty("databaseName", this.databaseName); + output.addProperty("collectionName", this.collectionName); + output.addProperty("direction", this.getDirection()); + output.addProperty("name", this.getVarName()); + return output; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java new file mode 100644 index 00000000..2e59273a --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java @@ -0,0 +1,114 @@ +package org.ballerinax.azurefunctions.service.cosmosdb; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.RemoteTriggerBinding; + +import java.util.Optional; + +/** + * Represents a HTTP Trigger binding in functions.json. + * + * @since 2.0.0 + */ +public class CosmosDBTriggerBinding extends RemoteTriggerBinding { + + private String connectionStringSetting; + private String databaseName; + private String collectionName; + private boolean createLeaseCollectionIfNotExists = true; + private int leasesCollectionThroughput = 400; + + public CosmosDBTriggerBinding(ServiceDeclarationNode serviceDeclarationNode, SemanticModel semanticModel) { + super("cosmosDBTrigger", "onUpdated", Constants.ANNOTATION_COSMOS_TRIGGER, serviceDeclarationNode, + semanticModel); + } + + @Override + protected void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "connectionStringSetting": + value.ifPresent(this::setConnectionStringSetting); + break; + case "databaseName": + value.ifPresent(this::setDatabaseName); + break; + case "collectionName": + value.ifPresent(this::setCollectionName); + break; + case "createLeaseCollectionIfNotExists": + value.ifPresent(s -> this.setCreateLeaseCollectionIfNotExists(Boolean.parseBoolean(s))); + break; + case "leasesCollectionThroughput": + value.ifPresent(s -> this.setLeasesCollectionThroughput(Integer.parseInt(s))); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getConnectionStringSetting() { + return connectionStringSetting; + } + + public void setConnectionStringSetting(String connectionStringSetting) { + this.connectionStringSetting = connectionStringSetting; + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public String getCollectionName() { + return collectionName; + } + + public void setCollectionName(String collectionName) { + this.collectionName = collectionName; + } + + public boolean getCreateLeaseCollectionIfNotExists() { + return createLeaseCollectionIfNotExists; + } + + public void setCreateLeaseCollectionIfNotExists(boolean createLeaseCollectionIfNotExists) { + this.createLeaseCollectionIfNotExists = createLeaseCollectionIfNotExists; + } + + public boolean isCreateLeaseCollectionIfNotExists() { + return createLeaseCollectionIfNotExists; + } + + public int getLeasesCollectionThroughput() { + return leasesCollectionThroughput; + } + + public void setLeasesCollectionThroughput(int leasesCollectionThroughput) { + this.leasesCollectionThroughput = leasesCollectionThroughput; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("connectionStringSetting", this.connectionStringSetting); + inputTrigger.addProperty("databaseName", databaseName); + inputTrigger.addProperty("collectionName", this.collectionName); + inputTrigger.addProperty("name", this.getVarName()); + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("createLeaseCollectionIfNotExists", this.createLeaseCollectionIfNotExists); + inputTrigger.addProperty("leasesCollectionThroughput", this.leasesCollectionThroughput); + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java new file mode 100644 index 00000000..113debbe --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java @@ -0,0 +1,27 @@ +package org.ballerinax.azurefunctions.service.http; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import org.ballerinax.azurefunctions.service.OutputBinding; + +/** + * Represents a HTTP Output binding in function.json. + * + * @since 2.0.0 + */ +public class HTTPOutputBinding extends OutputBinding { + + public HTTPOutputBinding(AnnotationNode annotationNode) { + super("http"); + this.setVarName("resp"); + } + + @Override + public JsonObject getJsonObject() { + JsonObject output = new JsonObject(); + output.addProperty("type", this.getTriggerType()); + output.addProperty("direction", this.getDirection()); + output.addProperty("name", this.getVarName()); + return output; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java new file mode 100644 index 00000000..a70a70ca --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -0,0 +1,232 @@ +package org.ballerinax.azurefunctions.service.http; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.BasicLiteralNode; +import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; +import io.ballerina.compiler.syntax.tree.RequiredParameterNode; +import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.Constants; +import org.ballerinax.azurefunctions.FunctionContext; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.Binding; +import org.ballerinax.azurefunctions.service.InputBindingBuilder; +import org.ballerinax.azurefunctions.service.OutputBindingBuilder; +import org.ballerinax.azurefunctions.service.TriggerBinding; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Represents a HTTP Trigger binding in functions.json. + * + * @since 2.0.0 + */ +public class HTTPTriggerBinding extends TriggerBinding { + private String path; + private String authLevel = "anonymous"; + private String methods; + + public HTTPTriggerBinding(ServiceDeclarationNode serviceDeclarationNode, SemanticModel semanticModel) { + super("httpTrigger"); + this.setVarName("httpPayload"); + this.serviceDeclarationNode = serviceDeclarationNode; + this.semanticModel = semanticModel; + } + + @Override + public List getBindings() { + Optional httpTriggerAnnot = + getListenerAnnotation(this.serviceDeclarationNode, Constants.ANNOTATION_HTTP_TRIGGER); + String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + List functionContexts = new ArrayList<>(); + NodeList members = this.serviceDeclarationNode.members(); + for (Node node : members) { +// HTTPTriggerBinding httpTriggerBinding = +// httpTriggerAnnot.map(HTTPTriggerBinding::new).orElseGet(HTTPTriggerBinding::new); + HTTPTriggerBinding httpTriggerBinding = + new HTTPTriggerBinding(this.serviceDeclarationNode, this.semanticModel); + httpTriggerAnnot.ifPresent(queueTrigger -> getAnnotation(httpTriggerBinding, queueTrigger)); + List bindings = new ArrayList<>(); + if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + continue; + } + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; + String method = functionDefinitionNode.functionName().text(); + httpTriggerBinding.setMethods(method); + StringBuilder resourcePath = new StringBuilder(); + resourcePath.append(servicePath); + for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { + if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { + resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); + continue; + } + if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { + ResourcePathParameterNode pathParamNode = (ResourcePathParameterNode) pathBlock; + //TODO Handle optional + resourcePath.append("/" + "{").append(pathParamNode.paramName().text()).append("}"); + continue; + } + //TODO add wildcard + } + httpTriggerBinding.setPath(resourcePath.toString()); + bindings.add(httpTriggerBinding); + String variableName; + SeparatedNodeList parameters = functionDefinitionNode.functionSignature().parameters(); + for (ParameterNode parameterNode : parameters) { + if (parameterNode.kind() != SyntaxKind.REQUIRED_PARAM) { + continue; + } + RequiredParameterNode reqParam = (RequiredParameterNode) parameterNode; + if (reqParam.paramName().isEmpty()) { + continue; + } + variableName = reqParam.paramName().get().text(); + InputBindingBuilder inputBuilder = new InputBindingBuilder(); + Optional inputBinding = inputBuilder.getInputBinding(reqParam.annotations(), variableName); + if (inputBinding.isPresent()) { + bindings.add(inputBinding.get()); + continue; + } + } + + ReturnTypeDescriptorNode returnTypeDescriptorNode = + functionDefinitionNode.functionSignature().returnTypeDesc().get(); //TODO recheck if return is must + OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); + Optional returnBinding = outputBuilder.getOutputBinding(returnTypeDescriptorNode.annotations()); + if (returnBinding.isEmpty()) { + bindings.add(new HTTPOutputBinding(null)); + } else { + bindings.add(returnBinding.get()); //TODO handle in code analyzer + } + Optional functionName = getFunctionNameFromAnnotation(functionDefinitionNode); + //TODO Handle +// if (functionName.isEmpty()) { +// } + functionContexts.add(new FunctionContext(functionName.get(), bindings)); + } + return functionContexts; + } + + private void getAnnotation(HTTPTriggerBinding triggerBinding, AnnotationNode queueTrigger) { + SeparatedNodeList fields = queueTrigger.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation(triggerBinding, (SpecificFieldNode) fieldNode); + } + } + + private void extractValueFromAnnotation(HTTPTriggerBinding triggerBinding, SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "authLevel": + value.ifPresent(triggerBinding::setAuthLevel); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public Optional getFunctionNameFromAnnotation(FunctionDefinitionNode functionDefinitionNode) { + MetadataNode metadataNode = functionDefinitionNode.metadata().orElseThrow(); + NodeList annotations = metadataNode.annotations(); + for (AnnotationNode annotationNode : annotations) { + Node ref = annotationNode.annotReference(); + if (ref.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { + continue; + } + QualifiedNameReferenceNode qualifiedRef = (QualifiedNameReferenceNode) ref; + if (!qualifiedRef.identifier().text().equals("AzureFunction") || + !qualifiedRef.modulePrefix().text().equals("af")) { + continue; + } + Optional val = + annotationNode.annotValue(); + if (val.isEmpty()) { + continue; + } + SeparatedNodeList fields = val.get().fields(); + for (MappingFieldNode field : fields) { + if (field.kind() != SyntaxKind.SPECIFIC_FIELD) { + continue; + } + SpecificFieldNode specificFieldNode = (SpecificFieldNode) field; + Node fieldNameNode = specificFieldNode.fieldName(); + if (fieldNameNode.kind() != SyntaxKind.IDENTIFIER_TOKEN) { + continue; + } + String fieldName = ((IdentifierToken) fieldNameNode).text(); + if (!fieldName.equals("name")) { + continue; + } + Optional expressionNode = specificFieldNode.valueExpr(); + if (expressionNode.isEmpty()) { + continue; + } + ExpressionNode value = expressionNode.get(); + if (value.kind() != SyntaxKind.STRING_LITERAL) { + continue; + } + BasicLiteralNode literalNode = (BasicLiteralNode) value; + String functionName = literalNode.literalToken().text(); + return Optional.of(functionName.substring(1, functionName.length() - 1)); + } + } + return Optional.empty(); + } + + public void setPath(String path) { + this.path = path; + } + + public void setAuthLevel(String authLevel) { + this.authLevel = authLevel; + } + + public void setMethods(String methods) { + this.methods = methods; + } + + public String getPath() { + return path; + } + + public String getAuthLevel() { + return authLevel; + } + + public String getMethods() { + return methods; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("authLevel", this.authLevel); + JsonArray methods = new JsonArray(); + methods.add(this.methods); //TODO add default + inputTrigger.add("methods", methods); + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("name", this.getVarName()); + inputTrigger.addProperty("route", this.getPath()); + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java new file mode 100644 index 00000000..017dcb0a --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java @@ -0,0 +1,74 @@ +package org.ballerinax.azurefunctions.service.queue; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.OutputBinding; + +import java.util.Optional; + +/** + * Represents Queue output binding in functions.json. + * + * @since 2.0.0 + */ +public class QueueOutputBinding extends OutputBinding { + + private String connection = "AzureWebJobsStorage"; + private String queueName; + + public QueueOutputBinding(AnnotationNode annotationNode) { + super("queue"); + this.setVarName("outMsg"); + SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + private void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "queueName": + value.ifPresent(this::setQueueName); + break; + case "connection": + value.ifPresent(this::setConnection); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getConnection() { + return connection; + } + + public void setConnection(String connection) { + this.connection = connection; + } + + public String getQueueName() { + return queueName; + } + + public void setQueueName(String queueName) { + this.queueName = queueName; + } + + @Override + public JsonObject getJsonObject() { + JsonObject output = new JsonObject(); + output.addProperty("type", this.getTriggerType()); + output.addProperty("connection", this.connection); + output.addProperty("queueName", this.queueName); + output.addProperty("direction", this.getDirection()); + output.addProperty("name", this.getVarName()); + return output; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java new file mode 100644 index 00000000..df6f736c --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java @@ -0,0 +1,72 @@ +package org.ballerinax.azurefunctions.service.queue; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.RemoteTriggerBinding; + +import java.util.Optional; + +/** + * Represents a queue trigger binding in function.json. + * + * @since 2.0.0 + */ +public class QueueTriggerBinding extends RemoteTriggerBinding { + + private String connection = "AzureWebJobsStorage"; + private String queueName; + + public QueueTriggerBinding(ServiceDeclarationNode serviceDeclarationNode, SemanticModel semanticModel) { + super("queueTrigger", "onMessage", Constants.ANNOTATION_QUEUE_TRIGGER, serviceDeclarationNode, semanticModel); + } + + @Override + protected void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "queueName": + value.ifPresent(this::setQueueName); + break; + case "connection": + value.ifPresent(this::setConnection); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getConnection() { + return connection; + } + + public void setConnection(String connection) { + this.connection = connection; + } + + public String getQueueName() { + return queueName; + } + + public void setQueueName(String queueName) { + this.queueName = queueName; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("connection", this.connection); + if (this.queueName != null) { + inputTrigger.addProperty("queueName", this.queueName); + } + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("name", this.getVarName()); + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java new file mode 100644 index 00000000..36f64970 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java @@ -0,0 +1,70 @@ +package org.ballerinax.azurefunctions.service.timer; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.RemoteTriggerBinding; + +import java.util.Optional; + +/** + * Represents a timer trigger binding in function.json. + * + * @since 2.0.0 + */ +public class TimerTriggerBinding extends RemoteTriggerBinding { + + private String schedule; + private boolean runOnStartup = true; + + public TimerTriggerBinding(ServiceDeclarationNode serviceDeclarationNode, SemanticModel semanticModel) { + super("timerTrigger", "onTrigger", Constants.ANNOTATION_TIMER_TRIGGER, serviceDeclarationNode, semanticModel); + } + + @Override + protected void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "schedule": + value.ifPresent(this::setSchedule); + break; + case "runOnStartup": + value.ifPresent(runOnStartup1 -> setRunOnStartup(Boolean.parseBoolean(runOnStartup1))); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getSchedule() { + return schedule; + } + + public void setSchedule(String schedule) { + this.schedule = schedule; + } + + public boolean isRunOnStartup() { + return runOnStartup; + } + + public void setRunOnStartup(boolean runOnStartup) { + this.runOnStartup = runOnStartup; + } + + @Override + public JsonObject getJsonObject() { + JsonObject inputTrigger = new JsonObject(); + inputTrigger.addProperty("type", this.getTriggerType()); + inputTrigger.addProperty("schedule", this.schedule); + inputTrigger.addProperty("runOnStartup", this.runOnStartup); + inputTrigger.addProperty("direction", this.getDirection()); + inputTrigger.addProperty("name", this.getVarName()); + return inputTrigger; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java new file mode 100644 index 00000000..be309fa5 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java @@ -0,0 +1,100 @@ +package org.ballerinax.azurefunctions.service.twilio; + +import com.google.gson.JsonObject; +import io.ballerina.compiler.syntax.tree.AnnotationNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.SeparatedNodeList; +import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Util; +import org.ballerinax.azurefunctions.service.OutputBinding; + +import java.util.Optional; + +/** + * Represents Twilio SMS output binding in functions.json. + * + * @since 2.0.0 + */ +public class TwilioSmsOutputBinding extends OutputBinding { + + private String accountSidSetting = "AzureWebJobsTwilioAccountSid"; + private String authTokenSetting = "AzureWebJobsTwilioAuthToken"; + private String from; + private String to; + + public TwilioSmsOutputBinding(AnnotationNode annotationNode) { + super("twilioSms"); + this.setVarName("outMsg"); + SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); + for (MappingFieldNode fieldNode : fields) { + extractValueFromAnnotation((SpecificFieldNode) fieldNode); + } + } + + private void extractValueFromAnnotation(SpecificFieldNode fieldNode) { + String text = ((IdentifierToken) fieldNode.fieldName()).text(); + Optional value = Util.extractValueFromAnnotationField(fieldNode); + switch (text) { + case "accountSidSetting": + value.ifPresent(this::setAccountSidSetting); + break; + case "authTokenSetting": + value.ifPresent(this::setAuthTokenSetting); + break; + case "'from": + value.ifPresent(this::setFrom); + break; + case "to": + value.ifPresent(this::setTo); + break; + default: + throw new RuntimeException("Unexpected property in the annotation"); + } + } + + public String getAccountSidSetting() { + return accountSidSetting; + } + + public void setAccountSidSetting(String accountSidSetting) { + this.accountSidSetting = accountSidSetting; + } + + public String getAuthTokenSetting() { + return authTokenSetting; + } + + public void setAuthTokenSetting(String authTokenSetting) { + this.authTokenSetting = authTokenSetting; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + @Override + public JsonObject getJsonObject() { + JsonObject output = new JsonObject(); + output.addProperty("type", this.getTriggerType()); + output.addProperty("accountSidSetting", this.accountSidSetting); + output.addProperty("authTokenSetting", this.authTokenSetting); + output.addProperty("from", this.from); + output.addProperty("to", this.to); + output.addProperty("direction", this.getDirection()); + output.addProperty("name", this.getVarName()); + return output; + } +} diff --git a/gradle.properties b/gradle.properties index 28469bd4..203022ad 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,25 +1,25 @@ org.gradle.caching=true org.gradle.jvmargs='-Dfile.encoding=UTF-8' group=org.ballerinax.azurefunctions -version=2.1.0-SNAPSHOT +version=3.0.0-SNAPSHOT systemProp.org.gradle.internal.publish.checksums.insecure=true -ballerinaLangVersion=2201.1.0-rc1.13 +ballerinaLangVersion=2201.1.0 ballerinaGradlePluginVersion=0.14.3 -stdlibIoVersion=1.2.2-20220512-114900-0d9af6e -stdlibLogVersion=2.3.0-20220524-082200-b38eda1 -stdlibHttpVersion=2.3.0-20220524-125800-903e1ba -stdlibAuthVersion=2.3.0-20220524-121500-fca79ed -stdlibFileVersion=1.3.0-20220524-121200-ace183b -stdlibRegexVersion=1.3.0-20220511-152100-0dd5f08 -stdlibCacheVersion=3.2.2-20220524-081900-4cd7ba0 -stdlibCryptoVersion=2.2.2-20220520-104000-8893e40 -stdlibTimeVersion=2.2.2-20220512-114900-b6ad9a5 -stdlibMimeVersion=2.3.0-20220524-121500-b5feb0c -stdlibOsVersion=1.3.0-20220520-104100-691a223 -stdlibTaskVersion=2.2.2-20220520-104500-363b385 -stdlibJwtVersion=2.3.0-20220524-121600-d31ab3d -stdlibOAuth2Version=2.3.0-20220524-121700-a777f0d -stdlibUuidVersion=1.5.0-20220520-105500-0a937d5 -stdlibUrlVersion=2.2.2-20220511-152900-e4473e4 +stdlibIoVersion=1.2.2 +stdlibLogVersion=2.3.0 +stdlibHttpVersion=2.3.0 +stdlibAuthVersion=2.3.0 +stdlibFileVersion=1.3.0 +stdlibRegexVersion=1.3.0 +stdlibCacheVersion=3.2.2 +stdlibCryptoVersion=2.2.2 +stdlibTimeVersion=2.2.2 +stdlibMimeVersion=2.3.0 +stdlibOsVersion=1.3.0 +stdlibTaskVersion=2.2.2 +stdlibJwtVersion=2.3.0 +stdlibOAuth2Version=2.3.0 +stdlibUuidVersion=1.3.0 +stdlibUrlVersion=2.2.2 observeVersion=1.0.4 observeInternalVersion=1.0.3 diff --git a/native/build.gradle b/native/build.gradle new file mode 100644 index 00000000..2a869df8 --- /dev/null +++ b/native/build.gradle @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed 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. + * + */ + +plugins { + id 'java' + id 'checkstyle' + id 'com.github.spotbugs' +} + +description = 'Ballerina - Azure Functions Java Utils' + +dependencies { + checkstyle project(':checkstyle') + checkstyle "com.puppycrawl.tools:checkstyle:${puppycrawlCheckstyleVersion}" + implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'value', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'array', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'ballerina-core', version: "${ballerinaLangVersion}" + implementation (group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}") { + transitive = false + } + +// implementation group: 'io.ballerina.stdlib', name: 'http-native', version: "${stdlibHttpVersion}" +// implementation group: 'io.ballerina.stdlib', name: 'mime-native', version: "${stdlibMimeVersion}" +} + +checkstyle { + toolVersion '7.8.2' + configFile rootProject.file("build-config/checkstyle/build/checkstyle.xml") + configProperties = ["suppressionFile" : file("${rootDir}/build-config/checkstyle/build/suppressions.xml")] +} + +checkstyleMain.dependsOn(":checkstyle:downloadCheckstyleRuleFiles") + +spotbugsMain { + effort "max" + reportLevel "low" + reportsDir = file("$project.buildDir/reports/spotbugs") + reports { + html.enabled true + text.enabled = true + } + def excludeFile = file("spotbugs-exclude.xml") + if(excludeFile.exists()) { + excludeFilter = excludeFile + } +} + +def excludePattern = '**/module-info.java' +tasks.withType(Checkstyle) { + exclude excludePattern +} + +compileJava { + doFirst { + options.compilerArgs = [ + '--module-path', classpath.asPath, + ] + classpath = files() + } +} diff --git a/native/spotbugs-exclude.xml b/native/spotbugs-exclude.xml new file mode 100644 index 00000000..e695b8d7 --- /dev/null +++ b/native/spotbugs-exclude.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java new file mode 100644 index 00000000..c5be7eec --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java @@ -0,0 +1,33 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; + +/** + * Represents an parameter of resource/remote function. + * + * @since 2.0.0 + */ +public abstract class AZFParameter implements Comparable { + private int index; + private Parameter parameter; + + public AZFParameter(int index, Parameter parameter) { + this.index = index; + this.parameter = parameter; + } + + public int getIndex() { + return index; + } + + public Parameter getParameter() { + return parameter; + } + + @Override + public int compareTo(AZFParameter o) { + return this.index - o.index; + } + + public abstract Object getValue(); +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java new file mode 100644 index 00000000..d8cf80cc --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; + +/** + * {@code Constants} contains the public constants to be used. + */ +public interface Constants { + String PACKAGE_ORG = "ballerinax"; + String PACKAGE_NAME = "azure_functions"; + + String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java new file mode 100644 index 00000000..a51c31e1 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; + +import io.ballerina.runtime.api.Future; +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.async.Callback; +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.Type; +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.BString; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.runtime.api.utils.StringUtils.fromString; + +/** + * {@code FunctionCallback} used to handle the Azure function service method invocation results. + */ +public class FunctionCallback implements Callback { + + private final Future future; + private final Module module; + private final List annotations; + + public FunctionCallback(Future future, Module module, Object[] annotations) { + this.future = future; + this.module = module; + this.annotations = new ArrayList<>(); + for (Object o : annotations) { + BString annotation = (BString) o; + String[] split = annotation.getValue().split(":"); + this.annotations.add(split[split.length - 1]); + } + } + + @Override + public void notifySuccess(Object result) { + if (result instanceof BError) { + BError error = (BError) result; + if (!isModuleDefinedError(error)) { + error.printStackTrace(); + } + future.complete(result); + return; + } + BMap mapValue = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); +// if (result instanceof BArray) { +// BArray result1 = (BArray) result; +// Object[] values = result1.getValues(); +// for (int i = 0; i < values.length; i++) { +// Object obj = values[i]; +// String identifier = generateUniqueIdentifier(i); +// mapValue.put(StringUtils.fromString(identifier), obj); +// } +// future.complete(mapValue); +// return; +// } + if (this.annotations.get(0).equals("QueueOutput") || this.annotations.get(0).equals("CosmosDBOutput")) { + mapValue.put(StringUtils.fromString("outMsg"), result); + } else { + BMap respMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + respMap.put(StringUtils.fromString("body"), result); + mapValue.put(StringUtils.fromString("resp"), respMap); + } + future.complete(mapValue); + } + + @Override + public void notifyFailure(BError bError) { + bError.printStackTrace(); + BString errorMessage = fromString("service method invocation failed: " + bError.getErrorMessage()); + BError invocationError = ErrorCreator.createError(module, "ServiceExecutionError", + errorMessage, bError, null); + future.complete(invocationError); + } + + private boolean isModuleDefinedError(BError error) { + Type errorType = error.getType(); + Module packageDetails = errorType.getPackage(); + String orgName = packageDetails.getOrg(); + String packageName = packageDetails.getName(); + return Constants.PACKAGE_ORG.equals(orgName) && Constants.PACKAGE_NAME.equals(packageName); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java new file mode 100644 index 00000000..17e45e89 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -0,0 +1,185 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; +import io.ballerina.runtime.api.types.ResourceMethodType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; +import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; +import static io.ballerina.runtime.api.TypeTags.STRING_TAG; + +/** + * Represents a Azure Resource function property. + * + * @since 2.0.0 + */ +public class HttpResource { + + private PathParameter[] pathParams; + private QueryParameter[] queryParameter; + private PayloadParameter payloadParameter; + private InputBindingParameter[] inputBindingParameters; + + public HttpResource(ResourceMethodType resourceMethodType, BMap body) { + this.pathParams = getPathParams(resourceMethodType, body); + this.payloadParameter = processPayloadParam(resourceMethodType, body).orElse(null); + this.queryParameter = getQueryParams(resourceMethodType, body); + this.inputBindingParameters = getInputBindingParams(resourceMethodType, body); + } + + private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) { + Parameter[] parameters = resourceMethod.getParameters(); + List inputBindingParameters = new ArrayList<>(); + for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { + Parameter parameter = parameters[i]; + String name = parameter.name; + Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + if (!ParamHandler.isInputAnnotationParam(annotation)) { + continue; + } + BString bodyValue = body.getStringValue(StringUtils.fromString(name)); + Type type = parameter.type; + JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(type); + Object bValue = jsonPayloadBuilder.getValue(bodyValue, false); +// Object bValue = Utilities.convertJsonToDataBoundParamValue(bodyValue, type); + //TODO handle other types records n stuff + inputBindingParameters.add(new InputBindingParameter(i, parameter, bValue)); + } + + return inputBindingParameters.toArray(InputBindingParameter[]::new); + } + + private QueryParameter[] getQueryParams(ResourceMethodType resourceMethod, BMap body) { + BMap queryParams = body.getMapValue(StringUtils.fromString("httpPayload")) + .getMapValue(StringUtils.fromString("Query")); + Parameter[] parameters = resourceMethod.getParameters(); + List queryParameters = new ArrayList<>(); + for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { + Parameter parameter = parameters[i]; + String name = parameter.name; + Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + if (annotation != null) { //Add other annotations as well + continue; + } + Object arr = queryParams.get(StringUtils.fromString(name)); + //TODO Handle optional null type +// if (arr == null) { +// +// } + int tag = parameter.type.getTag(); + Object bValue = null; + switch (tag) { + case STRING_TAG: + //TODO error +// if (!(arr instanceof BString)) { +// +// } + bValue = arr; + break; + case ARRAY_TAG: + //TODO handle other cases + BArray values = (BArray) arr; +// Type elementType = ((ArrayType) parameter.type).getElementType(); + bValue = values; //TODO check all types +// if (elementType.getTag() == STRING_TAG) { +// BString[] bString = new BString[values.length]; +// for (int j = 0, valuesLength = values.length; j < valuesLength; j++) { +// String value = values[j]; +// bString[j] =StringUtils.fromString(value); +// bValue = ValueCreator.createArrayValue(bString); +// } +// } + break; + default: + //TODO unsupported + } + queryParameters.add(new QueryParameter(i, parameter, bValue)); + } + return queryParameters.toArray(QueryParameter[]::new); + } + + private PathParameter[] getPathParams(ResourceMethodType resourceMethod, BMap body) { + String[] resourcePath = resourceMethod.getResourcePath(); + Parameter[] parameters = resourceMethod.getParameters(); + List pathParams = new ArrayList<>(); + int count = 0; + for (String path : resourcePath) { + if (path.equals("*")) { + Parameter parameter = parameters[count]; + BMap payload = body.getMapValue(StringUtils.fromString("httpPayload")); + BMap params = payload.getMapValue(StringUtils.fromString("Params")); + BString param = params.getStringValue(StringUtils.fromString(parameter.name)); + pathParams.add(new PathParameter(count, parameter, param.getValue())); + count++; + } + } + + return pathParams.toArray(PathParameter[]::new); + } + + private Optional processPayloadParam(ResourceMethodType resourceMethod, BMap body) { + Parameter[] parameters = resourceMethod.getParameters(); + for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { + Parameter parameter = parameters[i]; + String name = parameter.name; + Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + if (annotation == null) { + continue; + } + if (!(annotation instanceof BMap)) { + continue; + } + Boolean booleanValue = ((BMap) annotation).getBooleanValue(StringUtils.fromString( + "ballerinax/azure_functions:3:Payload")); + if (!booleanValue) { + continue; + } + BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); + BMap headers = body.getMapValue(StringUtils.fromString("httpPayload")) + .getMapValue(StringUtils.fromString("Headers")); + BString contentType = headers.getArrayValue(StringUtils.fromString("Content-Type")).getBString(0); + BString bodyValue = httpPayload.getStringValue(StringUtils.fromString("Body")); + Type type = parameter.type; + AbstractPayloadBuilder builder = AbstractPayloadBuilder.getBuilder(contentType.getValue(), type); + Object bValue = builder.getValue(bodyValue, false); +// Object bValue = Utilities.convertJsonToDataBoundParamValue(bodyValue, type); +// str.createValue(type, false); +// Object bValue = Utilities.convertJsonToDataBoundParamValue(bodyValue, type); +// Object bValue = builder.getValue((BObject) body1, false); + //TODO handle other types records n stuff + return Optional.of(new PayloadParameter(i, parameter, bValue)); + } + return Optional.empty(); + } + + public Object[] getArgList() { + List parameters = new ArrayList<>(Arrays.asList(pathParams)); + parameters.addAll(Arrays.asList(queryParameter)); + parameters.addAll(Arrays.asList(inputBindingParameters)); + if (payloadParameter != null) { + parameters.add(payloadParameter); + } + //TODO add more input output binding params + Collections.sort(parameters); + + Object[] args = new Object[parameters.size() * 2]; + int i = 0; + for (AZFParameter parameter : parameters) { + Object bValue = parameter.getValue(); + args[i++] = bValue; + args[i++] = true; + } + return args; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java new file mode 100644 index 00000000..20220e3c --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java @@ -0,0 +1,22 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; + +/** + * Represents an Input Binding Parameter. + * + * @since 2.0.0 + */ +public class InputBindingParameter extends AZFParameter { + private Object value; + + public InputBindingParameter(int index, Parameter parameter, Object value) { + super(index, parameter); + this.value = value; + } + + @Override + public Object getValue() { + return value; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGenerator.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java similarity index 50% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGenerator.java rename to native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java index 26555277..1fcc0b37 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/AzureCodeGenerator.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java @@ -6,29 +6,35 @@ * in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; -import io.ballerina.projects.plugins.CodeGenerator; -import io.ballerina.projects.plugins.CodeGeneratorContext; +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.Environment; +import io.ballerina.runtime.api.Module; /** - * Registers Code generators for azure functions. - * - * @since 2.0.0 + * {@code ModuleUtils} contains the utility methods for the module. */ -public class AzureCodeGenerator extends CodeGenerator { +public class ModuleUtils { + + private static Module module; + + private ModuleUtils() {} + + public static void setModule(Environment environment) { + module = environment.getCurrentModule(); + } - @Override - public void init(CodeGeneratorContext codeGeneratorContext) { - codeGeneratorContext.addSourceGeneratorTask(new AzureCodegenTask()); + public static Module getModule() { + return module; } } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java new file mode 100644 index 00000000..e1606cf5 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; + +import io.ballerina.runtime.api.Environment; +import io.ballerina.runtime.api.Future; +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.async.StrandMetadata; +import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.Parameter; +import io.ballerina.runtime.api.types.RemoteMethodType; +import io.ballerina.runtime.api.types.ResourceMethodType; +import io.ballerina.runtime.api.types.ServiceType; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.stdlib.azure.functions.Constants.SERVICE_OBJECT; + +/** + * {@code NativeHttpToAzureAdaptor} is a wrapper object used for service method execution. + */ +public class NativeHttpToAzureAdaptor { + + public static void externInit(BObject adaptor, BObject serviceObj) { + adaptor.addNativeData(SERVICE_OBJECT, serviceObj); + } + + public static BArray getAzureFunctionNames(Environment env, BObject adaptor) { + BObject bHubService = (BObject) adaptor.getNativeData(SERVICE_OBJECT); + ServiceType svcType = (ServiceType) bHubService.getType(); + List functionNameList = new ArrayList<>(); + for (ResourceMethodType resourceMethod : svcType.getResourceMethods()) { + BString functionName = ((BMap) resourceMethod + .getAnnotation(StringUtils.fromString("ballerinax/azure_functions:3:AzureFunction"))) + .getStringValue(StringUtils.fromString("name")); + functionNameList.add(functionName); + } + return ValueCreator.createArrayValue(functionNameList.toArray(BString[]::new)); + } + + public static Object callNativeMethod(Environment env, BObject adaptor, BMap body, BString functionName) { + BObject bHubService = (BObject) adaptor.getNativeData(SERVICE_OBJECT); + return invokeResourceFunction(env, bHubService, + "callNativeMethod", body, functionName); + } + + public static Object callRemoteFunction(Environment env, BObject adaptor, BMap body, BString remoteFuncName) { + BObject bHubService = (BObject) adaptor.getNativeData(SERVICE_OBJECT); + return invokeRemoteFunction(env, bHubService, "callOnMessage", body, remoteFuncName); + } + + private static Object invokeRemoteFunction(Environment env, BObject bHubService, String parentFunctionName, + BMap body, BString remoteFuncName) { + Future balFuture = env.markAsync(); + Module module = ModuleUtils.getModule(); + StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), + parentFunctionName); + ServiceType serviceType = (ServiceType) bHubService.getType(); + Object[] args = new Object[2]; + RemoteMethodType methodType = getOnMessageMethod(serviceType, remoteFuncName).orElseThrow(); //TODO handle error + Parameter parameter = methodType.getParameters()[0]; //TODO handle other params and payload type + String name = parameter.name; + BString bStr = body.getStringValue(StringUtils.fromString(name)); + JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(parameter.type); + Object bValue = jsonPayloadBuilder.getValue(bStr, false); +// Object bValue = Utilities.convertJsonToDataBoundParamValue(bStr, parameter.type); + args[0] = bValue; + args[1] = true; + BMap annotation = (BMap) methodType.getAnnotation(StringUtils.fromString("$returns$")); + if (serviceType.isIsolated()) { + env.getRuntime().invokeMethodAsyncConcurrently( + bHubService, remoteFuncName.getValue(), null, metadata, + new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, + args); + } else { + env.getRuntime().invokeMethodAsyncSequentially( + bHubService, remoteFuncName.getValue(), null, metadata, + new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, + args); + } + return null; + } + + private static Optional getOnMessageMethod(ServiceType serviceType, BString remoteFuncName) { + RemoteMethodType[] remoteMethods = serviceType.getRemoteMethods(); + for (RemoteMethodType methodType : remoteMethods) { + if (methodType.getName().equals(remoteFuncName.getValue())) { + return Optional.of(methodType); + } + } + return Optional.empty(); + } + + private static Object invokeResourceFunction(Environment env, BObject bHubService, String parentFunctionName, + BMap body, BString functionName) { + Future balFuture = env.markAsync(); + Module module = ModuleUtils.getModule(); + StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), + parentFunctionName); + ServiceType serviceType = (ServiceType) bHubService.getType(); + ResourceMethodType[] resourceMethods = serviceType.getResourceMethods(); + //TODO restrict "httpPayload" from the param names. + ResourceMethodType resourceMethod = + getResourceMethodType(resourceMethods, functionName).orElseThrow(); + HttpResource httpResource = new HttpResource(resourceMethod, body); + Object[] args = httpResource.getArgList(); +// Object[] args = new Object[1]; + BMap annotation = (BMap) resourceMethod.getAnnotation(StringUtils.fromString("$returns$")); + if (serviceType.isIsolated() && resourceMethod.isIsolated()) { + env.getRuntime().invokeMethodAsyncConcurrently( + bHubService, resourceMethod.getName(), null, metadata, + new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, + args); + } else { + env.getRuntime().invokeMethodAsyncSequentially( + bHubService, resourceMethod.getName(), null, metadata, + new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, + args); + } + return null; + } + + private static Optional getResourceMethodType(ResourceMethodType[] types, + BString enteredFunctionName) { + for (ResourceMethodType type : types) { + BString functionName = ((BMap) type + .getAnnotation(StringUtils.fromString("ballerinax/azure_functions:3:AzureFunction"))) + .getStringValue(StringUtils.fromString("name")); + + if (functionName.toString().equals(enteredFunctionName.toString())) { + return Optional.of(type); + } + } + return Optional.empty(); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java new file mode 100644 index 00000000..47b52ddc --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; + +import io.ballerina.runtime.api.Environment; +import io.ballerina.runtime.api.Future; +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.PredefinedTypes; +import io.ballerina.runtime.api.async.StrandMetadata; +import io.ballerina.runtime.api.types.Parameter; +import io.ballerina.runtime.api.types.RemoteMethodType; +import io.ballerina.runtime.api.types.ServiceType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypeId; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.stdlib.azure.functions.Constants.SERVICE_OBJECT; + +/** + * {@code NativeRemoteAdapter} is a wrapper object used for service method execution. + */ +public class NativeRemoteAdapter { + + public static void externRemoteInit(BObject adaptor, BObject serviceObj) { + adaptor.addNativeData(SERVICE_OBJECT, serviceObj); + } + + public static Object callRemoteFunction(Environment env, BObject adaptor, BMap body, BString remoteFuncName) { + BObject bHubService = (BObject) adaptor.getNativeData(SERVICE_OBJECT); + return invokeRemoteFunction(env, bHubService, "callRemoteFunction", body, remoteFuncName); + } + + private static Object invokeRemoteFunction(Environment env, BObject bHubService, String parentFunctionName, + BMap body, BString remoteFuncName) { + Future balFuture = env.markAsync(); + Module module = ModuleUtils.getModule(); + StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), + parentFunctionName); + ServiceType serviceType = (ServiceType) bHubService.getType(); + List argList = new ArrayList<>(); + RemoteMethodType methodType = getRemoteMethod(serviceType, remoteFuncName).orElseThrow(); //TODO handle error + Parameter[] parameters = methodType.getParameters(); + for (Parameter parameter : parameters) { + String name = parameter.name; + Object annotation = methodType.getAnnotation(StringUtils.fromString("$param$." + name)); + //TODO check and process Payload variable + if (ParamHandler.isPayloadAnnotationParam(annotation)) { + Object bValue = getDataboundValue(body, parameter, serviceType); + argList.add(bValue); + argList.add(true); + } + + //TODO check and process input binding variable + if (ParamHandler.isInputAnnotationParam(annotation)) { + BString bodyValue = body.getStringValue(StringUtils.fromString(name)); + Type type = parameter.type; + JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(type); + Object bValue = jsonPayloadBuilder.getValue(bodyValue, false); + argList.add(bValue); + argList.add(true); + } + } + Object[] args = argList.toArray(); + BMap annotation = (BMap) methodType.getAnnotation(StringUtils.fromString("$returns$")); + if (serviceType.isIsolated()) { + env.getRuntime().invokeMethodAsyncConcurrently( + bHubService, remoteFuncName.getValue(), null, metadata, + new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, + args); + } else { + env.getRuntime().invokeMethodAsyncSequentially( + bHubService, remoteFuncName.getValue(), null, metadata, + new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, + args); + } + return null; + } + + private static Object getDataboundValue(BMap body, Parameter parameter, ServiceType serviceType) { + List ids = serviceType.getTypeIdSet().getIds(); + BString paramBString = StringUtils.fromString(parameter.name); + if (ids.size() >= 1) { + TypeId typeId = ids.get(0); + String name = typeId.getName(); + if (name.equals("TimerService")) { + return body.getMapValue(paramBString); + } + } + BString bStr = body.getStringValue(paramBString); + JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(parameter.type); + return jsonPayloadBuilder.getValue(bStr, false); + } + + private static Optional getRemoteMethod(ServiceType serviceType, BString remoteFuncName) { + RemoteMethodType[] remoteMethods = serviceType.getRemoteMethods(); + for (RemoteMethodType methodType : remoteMethods) { + if (methodType.getName().equals(remoteFuncName.getValue())) { + return Optional.of(methodType); + } + } + return Optional.empty(); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java new file mode 100644 index 00000000..7a3b43f9 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -0,0 +1,59 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import java.util.Arrays; + +enum InputBindings { + COSMOS("CosmosDBInput"); + + private String annotation; + InputBindings (String annotation) { + this.annotation = annotation; + } + + public String getAnnotation() { + return annotation; + } +} + +/** + * Represents the input binding handler. + * + * @since 2.0.0 + */ +public class ParamHandler { + + public static boolean isPayloadAnnotationParam(Object annotation) { + if (annotation == null) { + return false; + } + if (!(annotation instanceof BMap)) { + return false; + } + + Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:Payload")); + return value instanceof Boolean; + } + + public static boolean isInputAnnotationParam(Object annotation) { + if (annotation == null) { + return false; + } + if (!(annotation instanceof BMap)) { + return false; + } + for (Object key : ((BMap) annotation).getKeys()) { + if (key instanceof BString) { + String annotationKey = ((BString) key).getValue(); + String annotationName = annotationKey.substring(annotationKey.lastIndexOf(':') + 1); + return Arrays.stream(InputBindings.values()) + .anyMatch(name -> name.getAnnotation().equals(annotationName)); + } + } + return false; + } + +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java new file mode 100644 index 00000000..5b531a2a --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java @@ -0,0 +1,22 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; +import io.ballerina.runtime.api.utils.StringUtils; + +/** + * Represents a path paramter in azure resource function. + * + * @since 2.0.0 + */ +public class PathParameter extends AZFParameter { + private String value; + public PathParameter(int index, Parameter parameter, String value) { + super(index, parameter); + this.value = value; + } + + @Override + public Object getValue() { + return StringUtils.fromString(this.value); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java new file mode 100644 index 00000000..903d5c99 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java @@ -0,0 +1,23 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; + +/** + * Represents the payload parameter in azure functions. + * + * @since 2.0.0 + */ +public class PayloadParameter extends AZFParameter { + + private Object value; + + public PayloadParameter(int index, Parameter parameter, Object value) { + super(index, parameter); + this.value = value; + } + + @Override + public Object getValue() { + return value; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java new file mode 100644 index 00000000..8825757f --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java @@ -0,0 +1,23 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; + +/** + * Represents a query parameter in a resource function. + * + * @since 2.0.0 + */ +public class QueryParameter extends AZFParameter { + + private Object value; + + public QueryParameter(int index, Parameter parameter, Object value) { + super(index, parameter); + this.value = value; + } + + @Override + public Object getValue() { + return this.value; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java new file mode 100644 index 00000000..141f2a10 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java @@ -0,0 +1,76 @@ +package io.ballerina.stdlib.azure.functions; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.utils.JsonUtils; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BString; +import org.ballerinalang.langlib.value.CloneWithType; +import org.ballerinalang.langlib.value.FromJsonString; + +import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; +import static io.ballerina.runtime.api.TypeTags.JSON_TAG; +import static io.ballerina.runtime.api.TypeTags.RECORD_TYPE_TAG; +import static io.ballerina.runtime.api.TypeTags.STRING_TAG; +/** + * Contains the utilities required for azure functions runtime. + * + * @since 2.0.0 + */ +public class Utilities { + +// public static final String ENTITY = "Entity"; + +// public static BObject createEntityObject() { +// return createObjectValue(MimeUtil.getMimePackage(), ENTITY); +// } + +// private static BObject createObjectValue(Module module, String objectTypeName, +// Object... fieldValues) { +// +// Object[] fields = new Object[fieldValues.length * 2]; +// +// // Adding boolean values for each arg +// for (int i = 0, j = 0; i < fieldValues.length; i++) { +// fields[j++] = fieldValues[i]; +// fields[j++] = true; +// } +// +// // passing scheduler, strand and properties as null for the moment, but better to expose them via this method +// return ValueCreator.createObjectValue(module, objectTypeName, null, null, null, fields); +// } + + public static Object convertJsonToDataBoundParamValue(BString bodyValue, Type type) { + Object bValue = bodyValue; + if (type.getTag() == JSON_TAG) { + bValue = FromJsonString.fromJsonString((BString) JsonUtils.parse(bodyValue)); + } else if (type.getTag() == RECORD_TYPE_TAG) { + Object jsonValue = FromJsonString.fromJsonString((BString) JsonUtils.parse(bodyValue)); + bValue = CloneWithType.convert(type, jsonValue); + } else if (type.getTag() == ARRAY_TAG) { + bValue = CloneWithType.convert(type, JsonUtils.parse(bodyValue)); + } else if (type.getTag() == STRING_TAG) { + try { + bValue = CloneWithType.convert(type, JsonUtils.parse(bodyValue)); + } catch (BError error) { + return bValue; + } + } +// switch (type.getTag()) { +// case TypeTags.STRING_TAG: +// bValue = StringUtils.fromString(stringAggregator.getAggregateString()); +// break; +// case TypeTags.XML_TAG: +// bValue = XmlUtils.parse(stringAggregator.getAggregateString()); +// break; +// case TypeTags.RECORD_TYPE_TAG: +// bValue = CloneWithType.convert(targetType, JsonUtils.parse( +// stringAggregator.getAggregateString())); +// break; +// default: +// bValue = FromJsonStringWithType.fromJsonStringWithType(bodyValue), +// ValueCreator.createTypedescValue(targetType)); +// break; +// } + return bValue; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/AbstractPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/AbstractPayloadBuilder.java new file mode 100644 index 00000000..bcb5a8c7 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/AbstractPayloadBuilder.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.builder; + +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.TypedescType; +import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BTypedesc; + +import java.util.List; +import java.util.Locale; + +import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; +import static io.ballerina.runtime.api.TypeTags.STRING_TAG; +import static io.ballerina.runtime.api.TypeTags.XML_TAG; + +/** + * The abstract class to build and convert the payload based on the content-type header. If the content type is not + * standard, the parameter type is used to infer the builder. + * + * @since SwanLake update 1 + */ +public abstract class AbstractPayloadBuilder { + + private static final String JSON_PATTERN = "^.*json.*$"; + private static final String XML_PATTERN = "^.*xml.*$"; + private static final String TEXT_PATTERN = "^.*text.*$"; + private static final String OCTET_STREAM_PATTERN = "^.*octet-stream.*$"; + private static final String URL_ENCODED_PATTERN = "^.*x-www-form-urlencoded.*$"; + + /** + * Get the built inbound payload after binding it to the respective type. + * + * @param dataSource inbound request entity + * @param readonly readonly status of parameter + * @return the payload + */ + public abstract Object getValue(BString dataSource, boolean readonly); + + public static AbstractPayloadBuilder getBuilder(String contentType, Type payloadType) { + if (contentType == null || contentType.isEmpty()) { + return getBuilderFromType(payloadType); + } + contentType = contentType.toLowerCase(Locale.getDefault()); + if (contentType.matches(XML_PATTERN)) { + return new XmlPayloadBuilder(payloadType); + } else if (contentType.matches(TEXT_PATTERN)) { + return new StringPayloadBuilder(payloadType); + } else if (contentType.matches(URL_ENCODED_PATTERN)) { + return new StringPayloadBuilder(payloadType); + } else if (contentType.matches(OCTET_STREAM_PATTERN)) { + return new BinaryPayloadBuilder(payloadType); + } else if (contentType.matches(JSON_PATTERN)) { + return new JsonPayloadBuilder(payloadType); + } else { + return getBuilderFromType(payloadType); + } + } + + private static AbstractPayloadBuilder getBuilderFromType(Type payloadType) { + switch (payloadType.getTag()) { + case STRING_TAG: + return new StringPayloadBuilder(payloadType); + case XML_TAG: + return new XmlPayloadBuilder(payloadType); + case ARRAY_TAG: + return new ArrayBuilder(payloadType); + default: + return new JsonPayloadBuilder(payloadType); + } + } + + public static boolean isSubtypeOfAllowedType(Type payloadType, int targetTypeTag) { + if (payloadType.getTag() == targetTypeTag) { + return true; + } else if (payloadType.getTag() == TypeTags.UNION_TAG) { + assert payloadType instanceof UnionType : payloadType.getClass(); + List memberTypes = ((UnionType) payloadType).getMemberTypes(); + return memberTypes.stream().anyMatch(memberType -> isSubtypeOfAllowedType(memberType, targetTypeTag)); + } + return false; + } + + public static boolean typeIncludedInUnion(BTypedesc unionType, BTypedesc targetType) { + int targetTypeTag = ((TypedescType) targetType.getDescribingType()).getConstraint().getTag(); + Type unionTypeDescribingType = unionType.getDescribingType(); + if (unionTypeDescribingType.getTag() == TypeTags.UNION_TAG) { + List memberTypes = ((UnionType) unionTypeDescribingType).getMemberTypes(); + return memberTypes.stream().anyMatch(memberType -> memberType.getTag() == targetTypeTag); + } + return false; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/ArrayBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/ArrayBuilder.java new file mode 100644 index 00000000..6052bed3 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/ArrayBuilder.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.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.values.BString; + +/** + * The array type payload builder. + * + * @since SwanLake update 1 + */ +public class ArrayBuilder extends AbstractPayloadBuilder { + private final Type payloadType; + + public ArrayBuilder(Type payloadType) { + this.payloadType = payloadType; + } + + @Override + public Object getValue(BString entity, boolean readonly) { + Type elementType = ((ArrayType) payloadType).getElementType(); + if (elementType.getTag() == TypeTags.BYTE_TAG) { + return new BinaryPayloadBuilder(payloadType).getValue(entity, readonly); + } + return new JsonPayloadBuilder(payloadType).getValue(entity, readonly); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/BinaryPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/BinaryPayloadBuilder.java new file mode 100644 index 00000000..a8875f84 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/BinaryPayloadBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.builder; + +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BString; +import org.ballerinalang.langlib.array.FromBase64; + +import java.util.List; + +/** + * The blob type payload builder. + * + * @since SwanLake update 1 + */ +public class BinaryPayloadBuilder extends AbstractPayloadBuilder { + private final Type payloadType; + + public BinaryPayloadBuilder(Type payloadType) { + this.payloadType = payloadType; + } + + @Override + public Object getValue(BString entity, boolean readonly) { + if (payloadType.getTag() == TypeTags.ARRAY_TAG) { + Type elementType = ((ArrayType) payloadType).getElementType(); + if (elementType.getTag() == TypeTags.BYTE_TAG) { + return FromBase64.fromBase64(entity); + } + } else if (payloadType.getTag() == TypeTags.UNION_TAG) { + List memberTypes = ((UnionType) payloadType).getMemberTypes(); + for (Type memberType : memberTypes) { + if (memberType.getTag() == TypeTags.ARRAY_TAG) { + Type elementType = ((ArrayType) memberType).getElementType(); + if (elementType.getTag() == TypeTags.BYTE_TAG) { + return FromBase64.fromBase64(entity); + } + } + } + } + throw ErrorCreator.createError(StringUtils.fromString("incompatible type found: '" + payloadType.toString())); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java new file mode 100644 index 00000000..666a6e1f --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.builder; + +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BRefValue; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.converter.JsonToRecordConverter; +import org.ballerinalang.langlib.value.FromJsonString; +import org.ballerinalang.langlib.value.FromJsonWithType; + +/** + * The json type payload builder. + * + * @since SwanLake update 1 + */ +public class JsonPayloadBuilder extends AbstractPayloadBuilder { + private final Type payloadType; + + public JsonPayloadBuilder(Type payloadType) { + this.payloadType = payloadType; + } + + @Override + public Object getValue(BString dataSource, boolean readonly) { + // Following can be removed based on the solution of + // https://github.com/ballerina-platform/ballerina-lang/issues/35780 + Object obj = FromJsonString.fromJsonString(dataSource); + if (isSubtypeOfAllowedType(payloadType, TypeTags.RECORD_TYPE_TAG)) { + return JsonToRecordConverter.convert(payloadType, obj, readonly); + } +// Object bjson = EntityBodyHandler.constructJsonDataSource(null); +// EntityBodyHandler.addJsonMessageDataSource(null, bjson); +// var result = FromJsonWithType.fromJsonWithType(bjson, ValueCreator.createTypedescValue(payloadType)); + var result = FromJsonWithType.fromJsonWithType(obj, ValueCreator.createTypedescValue(payloadType)); + if (result instanceof BError) { + throw (BError) result; + } + if (readonly && result instanceof BRefValue) { + ((BRefValue) result).freezeDirect(); + } + return result; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/StringPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/StringPayloadBuilder.java new file mode 100644 index 00000000..2783ee60 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/StringPayloadBuilder.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.builder; + +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.creators.ErrorCreator; +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.UnionType; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.converter.StringToByteArrayConverter; +import io.ballerina.stdlib.azure.functions.converter.UrlEncodedStringToMapConverter; + +import java.util.List; + +/** + * The string type payload builder. + * + * @since SwanLake update 1 + */ +public class StringPayloadBuilder extends AbstractPayloadBuilder { + private final Type payloadType; + + public StringPayloadBuilder(Type payloadType) { + this.payloadType = payloadType; + } + + @Override + public Object getValue(BString dataSource, boolean readonly) { + return createValue(payloadType, readonly, dataSource); + } + + private Object createValue(Type payloadType, boolean readonly, BString dataSource) { + if (payloadType.getTag() == TypeTags.STRING_TAG) { + return dataSource; + } else if (payloadType.getTag() == TypeTags.ARRAY_TAG) { + return StringToByteArrayConverter.convert((ArrayType) payloadType, dataSource, readonly); + } else if (payloadType.getTag() == TypeTags.MAP_TAG) { + return UrlEncodedStringToMapConverter.convert((MapType) payloadType, dataSource, readonly); + } else if (payloadType.getTag() == TypeTags.UNION_TAG) { + List memberTypes = ((UnionType) payloadType).getMemberTypes(); + for (Type memberType : memberTypes) { + try { + return createValue(memberType, readonly, dataSource); + } catch (BError ignored) { + // thrown errors are ignored until all the types are iterated + } + } + } + throw ErrorCreator.createError(StringUtils.fromString("incompatible type found: '" + payloadType.toString())); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java new file mode 100644 index 00000000..a18c3fd7 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.builder; + +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.utils.XmlUtils; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BXml; +/** + * The xml type payload builder. + * + * @since SwanLake update 1 + */ +public class XmlPayloadBuilder extends AbstractPayloadBuilder { + private final Type payloadType; + + public XmlPayloadBuilder(Type payloadType) { + this.payloadType = payloadType; + } + + @Override + public Object getValue(BString entity, boolean readonly) { + if (isSubtypeOfAllowedType(payloadType, TypeTags.XML_TAG)) { +// BXml bxml = EntityBodyHandler.constructXmlDataSource(entity); +// EntityBodyHandler.addMessageDataSource(entity, bxml); + BXml bxml = XmlUtils.parse(entity); + if (readonly) { + bxml.freezeDirect(); + } + return bxml; + } + throw ErrorCreator.createError(StringUtils.fromString("incompatible type found: '" + payloadType.toString())); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java new file mode 100644 index 00000000..9ce0f84b --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.converter; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BRefValue; +import org.ballerinalang.langlib.value.CloneWithType; + +/** + * The converter binds the JSON payload to a record. + * + * @since SwanLake update 1 + */ +public class JsonToRecordConverter { + + public static Object convert(Type type, Object entity, boolean readonly) { + Object recordEntity = getRecordEntity(entity, type); + if (readonly && recordEntity instanceof BRefValue) { + ((BRefValue) recordEntity).freezeDirect(); + } + return recordEntity; + } + + private static Object getRecordEntity(Object entity, Type entityBodyType) { +// Object bjson = EntityBodyHandler.getMessageDataSource(entity) == null ? getBJsonValue(entity) +// : EntityBodyHandler.getMessageDataSource(entity); + Object result = getRecord(entityBodyType, entity); + if (result instanceof BError) { + throw (BError) result; + } + return result; + } + + /** + * Convert a json to the relevant record type. + * + * @param entityBodyType Represents entity body type + * @param bjson Represents the json value that needs to be converted + * @return the relevant ballerina record or object + */ + private static Object getRecord(Type entityBodyType, Object bjson) { + try { + return CloneWithType.convert(entityBodyType, bjson); + } catch (NullPointerException ex) { + throw new RuntimeException("cannot convert payload to record type: " + + entityBodyType.getName()); + } + } + +// /** +// * Given an inbound request entity construct the ballerina json. +// * +// * @param entity Represents inbound request entity +// * @return a ballerina json value +// */ +// private static Object getBJsonValue(BObject entity) { +// Object bjson = EntityBodyHandler.constructJsonDataSource(entity); +// EntityBodyHandler.addJsonMessageDataSource(entity, bjson); +// return bjson; +// } + + private JsonToRecordConverter() { + + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/StringToByteArrayConverter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/StringToByteArrayConverter.java new file mode 100644 index 00000000..5e4a9b08 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/StringToByteArrayConverter.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.converter; + +import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BString; + +import java.nio.charset.StandardCharsets; + +import static io.ballerina.runtime.api.creators.ValueCreator.createArrayValue; +import static io.ballerina.runtime.api.creators.ValueCreator.createReadonlyArrayValue; + +/** + * The converter binds the String payload to a Byte array. + * + * @since SwanLake update 1 + */ +public class StringToByteArrayConverter { + + public static Object convert(ArrayType type, BString dataSource, boolean readonly) { + Type elementType = type.getElementType(); + if (elementType.getTag() == TypeTags.BYTE_TAG) { + byte[] values = dataSource.getValue().getBytes(StandardCharsets.UTF_8); + return readonly ? createReadonlyArrayValue(values) : createArrayValue(values); + } + return ErrorCreator.createError(StringUtils.fromString("incompatible array element type found: '" + + elementType.toString())); + } + + private StringToByteArrayConverter() { + + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/UrlEncodedStringToMapConverter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/UrlEncodedStringToMapConverter.java new file mode 100644 index 00000000..a786a12b --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/UrlEncodedStringToMapConverter.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.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.Type; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import static io.ballerina.runtime.api.TypeTags.STRING_TAG; + +/** + * The converter binds the URL encoded string payload to a Map. + * + * @since SwanLake update 1 + */ +public class UrlEncodedStringToMapConverter { + + private static final MapType STRING_MAP = TypeCreator.createMapType(PredefinedTypes.TYPE_STRING); + + public static Object convert(MapType type, BString dataSource, boolean readonly) { + Type constrainedType = type.getConstrainedType(); + if (constrainedType.getTag() == STRING_TAG) { + BMap formParamMap = getFormParamMap(dataSource); + if (readonly) { + formParamMap.freezeDirect(); + } + return formParamMap; + } + throw ErrorCreator.createError(StringUtils.fromString("incompatible type found: '" + type.toString())); + } + + private static BMap getFormParamMap(Object stringDataSource) { + try { + String formData = ((BString) stringDataSource).getValue(); + BMap formParamsMap = ValueCreator.createMapValue(STRING_MAP); + if (formData.isEmpty()) { + return formParamsMap; + } + Map tempParamMap = new HashMap<>(); + String decodedValue = URLDecoder.decode(formData, StandardCharsets.UTF_8); + + if (!decodedValue.contains("=")) { + throw new RuntimeException("Datasource does not contain form data"); + } + String[] formParamValues = decodedValue.split("&"); + for (String formParam : formParamValues) { + int index = formParam.indexOf('='); + if (index == -1) { + if (!tempParamMap.containsKey(formParam)) { + tempParamMap.put(formParam, null); + } + continue; + } + String formParamName = formParam.substring(0, index).trim(); + String formParamValue = formParam.substring(index + 1).trim(); + tempParamMap.put(formParamName, formParamValue); + } + + for (Map.Entry entry : tempParamMap.entrySet()) { + String entryValue = entry.getValue(); + if (entryValue != null) { + formParamsMap.put(StringUtils.fromString(entry.getKey()), StringUtils.fromString(entryValue)); + } else { + formParamsMap.put(StringUtils.fromString(entry.getKey()), null); + } + } + return formParamsMap; + } catch (Exception ex) { + throw ErrorCreator.createError( + StringUtils.fromString("Could not convert payload to map: " + ex.getMessage())); + } + } + + private UrlEncodedStringToMapConverter() { + + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/BindingType.java b/native/src/main/java/module-info.java similarity index 62% rename from compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/BindingType.java rename to native/src/main/java/module-info.java index af227a42..144ca3e8 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/generator/BindingType.java +++ b/native/src/main/java/module-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -11,19 +11,14 @@ * 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 + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package org.ballerinax.azurefunctions.generator; -/** - * Represents the binding type. - */ -public enum BindingType { - TRIGGER, - INPUT, - OUTPUT, - CONTEXT, - METADATA +module io.ballerina.stdlib.azure.functions { + requires io.ballerina.lang; + requires io.ballerina.runtime; + requires io.ballerina.lang.value; + requires io.ballerina.lang.array; } diff --git a/settings.gradle b/settings.gradle index 1e528fb4..6d91ff41 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,13 +21,16 @@ plugins { rootProject.name = 'azure_functions' +include(':native') include ':checkstyle' +include ':azure_functions-native' include ':azure_functions-ballerina' include ':azure_functions-compiler-plugin' include ':azure_functions-ballerina-tests' include ':azure_functions-compiler-plugin-tests' project(':checkstyle').projectDir = file("build-config${File.separator}checkstyle") +project(':azure_functions-native').projectDir = file('native') project(':azure_functions-ballerina').projectDir = file('ballerina') project(':azure_functions-compiler-plugin').projectDir = file('compiler-plugin') project(':azure_functions-ballerina-tests').projectDir = file('ballerina-tests') diff --git a/spec/spec.md b/spec/spec.md new file mode 100644 index 00000000..638d3835 --- /dev/null +++ b/spec/spec.md @@ -0,0 +1,606 @@ +### Supported Triggers And Bindings + +Supported From Ballerina - :heavy_check_mark: Supported From Azure - :white_check_mark: Not supported - :x: + +| Type | Trigger | Input | Output | +|--------------------- |-------------------- |-------------------- |-------------------- | +| Blob storage | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Azure Cosmos DB | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | +| Azure SQL (preview) | | :white_check_mark: | :white_check_mark: | +| Dapr | | :white_check_mark: | :white_check_mark: | +| Event Grid | :white_check_mark: | | :white_check_mark: | +| Event Hubs | :white_check_mark: | | :white_check_mark: | +| HTTP & webhooks | :heavy_check_mark: | | :heavy_check_mark: | +| IoT Hub | :white_check_mark: | | | +| Kafka | :white_check_mark: | | :white_check_mark: | +| Mobile Apps | | :white_check_mark: | :white_check_mark: | +| Notification Hubs | | | :white_check_mark: | +| Queue storage | :heavy_check_mark: | | :heavy_check_mark: | +| RabbitMQ | :white_check_mark: | | :white_check_mark: | +| SendGrid | | | :white_check_mark: | +| Service Bus | :white_check_mark: | | :white_check_mark: | +| SignalR | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Table storage | | :white_check_mark: | :white_check_mark: | +| Timer | :heavy_check_mark: | | | +| Twilio | | | :heavy_check_mark: | + +## HTTP + +### 2.1 Listener +Azure functions HTTP trigger is mapped to HttpListener defined in the module. You can define the listener in two ways. + +Separate listener declaration +```ballerina +import ballerinax/azure_functions as af; + +listener af:HTTPListener ep = new (); + +service "hello" on ep { +} +``` + +Inline listener declaration //TODO impl compiler ext +```ballerina +import ballerinax/azure_functions as af; + +service "hello" on new af:HTTPListener() { +} +``` + + +### 2.2 Service +Service is a collection of resources functions, which are the network entry points of a ballerina program. +In addition to that a service can contain public and private functions which can be accessed by calling with `self`. + +#### 2.2.1. Service type +```ballerina +public type HttpService distinct service object { + +}; +``` +#### 2.2.2.Annotation +Each Listener can be attached with an optional annotation which contains the configurations required for the azure platform. +```ballerina +public type AUTH_LEVEL "anonymous"|"function"|"admin"; + +public type HTTPTriggerConfiguration record {| + AUTH_LEVEL authLevel = "anonymous"; +|}; +``` + +If an output Binding annotation is specified, HTTPOutput will be taken implicitly. + +#### 2.2.2. Service base path + +The base path is considered during the request dispatching to discover the service. Identifiers and string literals +are allowed to be stated as base path and it should be started with `/`. The base path is optional and it will be +defaulted to `/` when not defined. If the base path contains any special characters, those should be escaped or defined +as string literals + +```ballerina +service hello\-world on new af:HTTPListener() { //TODO compiler ext + resource function get foo() { + + } +} + +service http:Service "hello-world" on new af:HTTPListener() { + resource function get foo() { + + } +} +``` + +A service can be declared in three ways upon the requirement. + +#### 2.2.3. Service declaration +The [Service declaration](https://ballerina.io/spec/lang/2021R1/#section_8.3.2) is a syntactic sugar for creating a +service and it is the mostly used approach for creating a service. The declaration gets desugared into creating a +listener object, creating a service object, attaching the service object to the listener object. + +```ballerina +service af:HttpService /foo/bar on new af:HTTPListener() { + resource function get greeting() returns string { + return "hello world"; + } +} +``` + +### 2.3. Resource + +A method of a service can be declared as a [resource function](https://ballerina.io/spec/lang/2021R1/#resources) +which is associated with configuration data that is invoked by a network message by a Listener. Users write the +business logic inside a resource and expose it over the network. + +#### 2.3.1. Accessor +The accessor-name of the resource represents the HTTP method and it can be get, post, put, delete, head, patch, options +and default. If the accessor is unmatched, 405 Method Not Allowed response is returned. When the accessor name is +stated as default, any HTTP method can be matched to it in the absence of an exact match. Users can define custom +methods such as copy, move based on their requirement. A resource which can handle any method would look like as +follows. This is useful when handling unmatched verbs. + +```ballerina +resource function 'default NAME_TEMPLATE () { + +} +``` +#### 2.3.2. Resource name +The resource-name represents the path of the resource which is considered during the request dispatching. The name can +be hierarchical(foo/bar/baz). Each path identifier should be separated by `/` and first path identifier should not +contain a prefixing `/`. If the paths are unmatched, 404 NOT FOUND response is returned. +```ballerina +resource function post hello() { + +} +``` +Only the identifiers can be used as resource path not string literals. Dot identifier is +used to denote the `/` only if the path contains a single identifier. +```ballerina +resource function post .() { + +} +``` +Any special characters can be used in the path by escaping. +```ballerina +resource function post hello\-world() { + +} +``` + +#### 2.3.3. Path parameter +The path parameter segment is also a part of the resource name which is declared within brackets along with the type. +As per the following resource name, baz is the path param segment and it’s type is string. Like wise users can define +string, int, boolean, float, and decimal typed path parameters. If the paths are unmatched, 404 NOT FOUND response +is returned. If the segment failed to parse into the expected type, 500 Internal Server Error response is returned. + +```ballerina +resource function post foo/bar/[string baz]/qux() { + // baz is the path param +} + +resource function get data/[int age]/[string name]/[boolean status]/[float weight]() returns json { + int balAge = age + 1; + float balWeight = weight + 2.95; + string balName = name + " lang"; + if (status) { + balName = name; + } + json responseJson = { Name:name, Age:balAge, Weight:balWeight, Status:status, Lang: balName}; + return responseJson; +} +``` + +If multiple path segments needs to be matched after the last identifier, Rest param should be used at the end of the +resource name as the last identifier. string, int, boolean, float, and decimal types are supported as rest parameters. +```ballerina +resource function get foo/[string... bar]() returns json { + json responseJson = {"echo": bar[0]}; + return responseJson; +} +``` + +Using both `'default` accessor and the rest parameters, a default resource can be defined to a service. This +default resource can act as a common destination where the unmatched requests (either HTTP method or resource path) may +get dispatched. + +```ballerina +resource function 'default [string... s]() { + +} +``` + +##### 2.3.4.3. Query parameter + +The query param is a URL parameter which is available as a resource function parameter and it's not associated +with any annotation or additional detail. This parameter is not compulsory and not ordered. The type of query param +are as follows + +```ballerina +type BasicType boolean|int|float|decimal|string|map; +``` + +The same query param can have multiple values. In the presence of multiple such values, If the user has specified +the param as an array type, then all values will return. If not the first param values will be returned. As per the +following resource function, the request may contain at least two query params with the key of bar and id. +Eg : “/hello?bar=hi&id=56” + +```ballerina +resource function get hello(string bar, int id) { + +} +``` + +If the query parameter is not defined in the function signature, then the query param binding does not happen. If a +query param of the request URL has no corresponding parameter in the resource function, then that param is ignored. +If the parameter is defined in the function, but there is no such query param in the URL, that request will lead +to a 400 BAD REQUEST error response unless the type is nilable (string?) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Case Resource argument Query Mapping
1 string foo foo=bar bar
foo= ""
foo Error : no query param value found for 'foo'
No query Error : no query param value found for 'foo'
2 string? foo foo=bar bar
foo= ""
foo nil
No query nil
+ +### Payload parameter + +The payload parameter is used to access the request payload during the resource invocation. When the payload param is +defined with @http:Payload annotation, the listener deserialize the inbound request payload based on the media type +which retrieved by the `Content-type` header of the request. The data binding happens thereafter considering the +parameter type. The type of payload parameter can be one of the `anytype`. If the header is not present or not a +standard header, the binding type is inferred by the parameter type. + +Following table explains the compatible `anydata` types with each common media type. In the absence of a standard media +type, the binding type is inferred by the payload parameter type itself. If the type is not compatible with the media +type, error is returned. + +|Ballerina Type | Structure|"text" | "xml" | "json" | "x-www-form-urlencoded" | "octet-stream"| +|---------------|----------|-------|-------|--------|-------------------------|---------------| +|boolean| | ❌ | ❌ | ✅|❌|❌ +| |boolean[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +|int| | ❌ | ❌ | ✅|❌|❌ +| |int[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +float| | ❌ | ❌ | ✅|❌|❌ +| |float[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +decimal| | ❌ | ❌ | ✅|❌|❌ +| |decimal[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +byte[]| | ✅ | ❌ | ✅|❌|✅ +| |byte[][]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +string| |✅|❌|✅|✅|❌ +| |string[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|✅|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +xml| | ❌ | ✅ | ❌|❌|❌ +json| | ❌ | ❌ | ✅|❌|❌ +| |json[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +map| | ❌ | ❌ | ✅|❌|❌ +| |map[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\\>| ❌ | ❌ | ✅|❌|❌ +record| |❌|❌|✅|❌|❌ +| |record[]| ❌ | ❌ | ✅|❌|❌ +| |map\| ❌ | ❌ | ✅|❌|❌ +| |table\| ❌ | ❌ | ✅|❌|❌ + ```bal + resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { + return "Hello from the query " + greeting + " " + name; + } +``` + + +##### 2.3.4.5. Header parameter + +The header parameter is to access the inbound request headers The header param is defined with `@http:Header` annotation +The type of header param can be defined as follows; + +```ballerina +type BasicType string|int|float|decimal|boolean; +public type HeaderParamType ()|BasicType|BasicType[]|record {| BasicType...; |}; +``` + +When multiple header values are present for the given header, the first header value is returned when the param +type is `string` or any of the basic types. To retrieve all the values, use `string[]` type or any array of the +basic types. This parameter is not compulsory and not ordered. + +The header param name is considered as the header name during the value retrieval. However, the header annotation name +field can be used to define the header name whenever user needs some different variable name for the header. + +User cannot denote the type as a union of pure type, array type, or record type together, that way the resource +cannot infer a single type to proceed. Hence, returns a compiler error. + +In the absence of a header when the param is defined in the resource signature, listener returns 400 BAD REQUEST unless +the type is nilable. + +```ballerina +//Single header value extraction +resource function post hello1(@http:Header string referer) { + +} + +//Multiple header value extraction +resource function post hello2(@http:Header {name: "Accept"} string[] accept) { + +} + +public type RateLimitHeaders record {| + string x\-rate\-limit\-id; + int x\-rate\-limit\-remaining; + string[] x\-rate\-limit\-types; +|}; + +//Populate selected headers to a record +resource function get hello3(@http:Header RateLimitHeaders rateLimitHeaders) { +} +``` + +If the requirement is to access all the header of the inbound request, it can be achieved through the `http:Headers` +typed param in the signature. It does not need the annotation and not ordered. + +```ballerina +resource function get hello3(http:Headers headers) { + String|error referer = headers.getHeader("Referer"); + String[]|error accept = headers.getHeaders("Accept"); + String[] keys = headers.getHeaderNames(); +} +``` + +The header consists of header name and values. Sometimes user may send header without value(`foo:`). In such +situations, when the header param type is nilable, the values returns nil and same happened when the complete header is +not present in the request. In order to avoid the missing detail, a service level configuration has introduced naming +`treatNilableAsOptional` + +```ballerina +@http:ServiceConfig { + treatNilableAsOptional : false +} +service /headerparamservice on HeaderBindingIdealEP { + + resource function get test1(@http:Header string? foo) returns json { + + } +} +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Case Resource argument Header Current Mapping (treatNilableAsOptional=true - Default) Ideal Mapping (treatNilableAsOptional=false)
1 string foo foo:bar bar bar
foo: Error : no header value found for 'foo' Error : no header value found for 'foo'
No header Error : no header value found for 'foo' Error : no header value found for 'foo'
2 string? foo foo:bar bar bar
foo: nil nil
No header nil Error : no header value found for 'foo'
+ +#### 2.3.5. Return types +The resource function supports anydata, error?, http:Response and http:StatusCodeResponse as return types. + +```ballerina +resource function XXX NAME_TEMPLATE () returns @http:Payload anydata|http:Response|http:StatusCodeResponse|http:Error? { +} +``` + +In addition to that the `@http:Payload` annotation can be specified along with anydata return type +mentioning the content type of the outbound payload. + +```ballerina +resource function get test() returns @http:Payload {mediaType:"text/id+plain"} string { + return "world"; +} +``` + +Based on the return types respective header value is added as the `Content-type` of the `http:Response`. + +| Type | Content Type | +|-----------------------------------------------------------------------|--------------------------| +| () | - | +| string | text/plain | +| xml | application/xml | +| byte[] | application/octet-stream | +| int, float, decimal, boolean | application/json | +| map\, table>, map\[], table>)[] | application/json | +| http:StatusCodeResponse | application/json | + +##### 2.3.5.1. Status Code Response + +The status code response records are defined in the HTTP module for every HTTP status code. + +```ballerina +type Person record { + string name; +}; +resource function put person(string name) returns record {|*http:Created; Person body;|} { + Person person = {name:name}; + return { + mediaType: "application/person+json", + headers: { + "X-Server": "myServer" + }, + body: person + }; +} +``` + +Following is the `http:Ok` definition. Likewise, all the status codes are provided. + +```ballerina +public type Ok record { + readonly StatusOk status; + string mediaType; + map headers?; + anydata body?; +}; + +resource function get greeting() returns http:Ok|http:InternalServerError { + http:Ok ok = { body: "hello world", headers: { xtest: "foo"} }; + return ok; +} +``` + +##### 2.3.5.2. Return nil + + +The return nil from the resource will return 202 ACCEPTED response. +```ballerina +resource function post person(@http:Payload Person p) { + int age = p.age; + io:println(string `Age is: ${age}`); +} +``` + +##### 2.3.5.3. Default response status codes + +To improve the developer experience for RESTful API development, following default status codes will be used in outbound +response when returning `anydata` directly from a resource function. + +| Resource Accessor | Semantics | Status Code | +|-------------------|---------------------------------------------------------------|-------------------------| +| GET | Retrieve the resource | 200 OK | +| POST | Create a new resource | 201 Created | +| PUT | Create a new resource or update an existing resource | 200 OK | +| PATCH | Partially update an existing resource | 200 OK | +| DELETE | Delete an existing resource | 200 OK | +| HEAD | Retrieve headers | 200 OK | +| OPTIONS | Retrieve permitted communication options | 200 OK | + +### Artifact generation +//TODO Fill +#### Function name Generation +//TODO Fill + +## Queue + + ```bal +@af:QueueTrigger { + queueName: "queue2" +} +listener af:QueueListener queueListener = new af:QueueListener(); +service "queue" on queueListener { + remote function onMessage (@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg; + } +} +``` + + +## CosmosDB + + ```bal +@af:CosmosDBTrigger {connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2"} +listener af:CosmosDBListener cosmosEp = new (); + +service "cosmos" on cosmosEp { + remote function onUpdated (@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } +} +``` + + +## Blob + + ```bal +@af:BlobTrigger { path: "bpath1/{name}" } +listener af:BlobListener blobEp = new (); + +service "blob" on blobEp { + remote function onUpdated (@af:Payload byte[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } +} +``` + +## Timer + + ```bal +@af:TimerTrigger { schedule: "*/10 * * * * *" } +listener af:TimerListener timerEp = new (); + +service "timer" on timerEp { + remote function onTriggered (@af:Payload json inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } +} +``` + + +## Twilio + + + ```bal +@af:TimerTrigger { schedule: "*/10 * * * * *" } +listener af:TimerListener timerEp = new (); + +service "timer" on timerEp { + remote function onTriggered (@af:Payload json inMsg) returns @af:QueueOutput @af:TwilioSmsOutput { fromNumber: "+12069845840" } string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } +} +``` From 46a221007307de583c2910e841246cd6150adb4b Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Thu, 21 Jul 2022 15:17:11 +0530 Subject: [PATCH 03/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index d5301fe7..3f0fc2f0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -9,7 +9,7 @@ dependencies-toml-version = "2" [[package]] org = "ballerina" name = "auth" -version = "2.3.0" +version = "2.3.1" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "jballerina.java"}, @@ -104,7 +104,7 @@ modules = [ [[package]] org = "ballerina" name = "jwt" -version = "2.3.0" +version = "2.3.1" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -223,7 +223,7 @@ dependencies = [ [[package]] org = "ballerina" name = "oauth2" -version = "2.3.0" +version = "2.3.1" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -235,7 +235,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.0.4" +version = "1.0.5" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From a1e143e229d6b0342966f23a03e7c231138d1b18 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Mon, 1 Aug 2022 12:16:35 +0530 Subject: [PATCH 04/59] Add support for HTTP response --- ballerina/http_status_code_types.bal | 932 ++++++++++++++++++ ballerina/http_status_codes.bal | 108 ++ .../azure/functions/FunctionCallback.java | 50 + 3 files changed, 1090 insertions(+) create mode 100644 ballerina/http_status_code_types.bal create mode 100644 ballerina/http_status_codes.bal diff --git a/ballerina/http_status_code_types.bal b/ballerina/http_status_code_types.bal new file mode 100644 index 00000000..f51fce51 --- /dev/null +++ b/ballerina/http_status_code_types.bal @@ -0,0 +1,932 @@ +// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. + +// Remove the union once https://github.com/ballerina-platform/ballerina-lang/issues/30490 is fixed. +# Defines the possible status code response record types. +public type StatusCodeResponse Continue|SwitchingProtocols|Ok|Created|Accepted|NonAuthoritativeInformation|NoContent| + ResetContent|PartialContent|MultipleChoices|MovedPermanently|Found|SeeOther|NotModified|UseProxy|TemporaryRedirect| + PermanentRedirect|BadRequest|Unauthorized|PaymentRequired|Forbidden|NotFound|MethodNotAllowed|NotAcceptable| + ProxyAuthenticationRequired|RequestTimeout|Conflict|Gone|LengthRequired|PreconditionFailed|PayloadTooLarge| + UriTooLong|UnsupportedMediaType|RangeNotSatisfiable|ExpectationFailed|UpgradeRequired|TooManyRequests| + RequestHeaderFieldsTooLarge|InternalServerError|NotImplemented|BadGateway|ServiceUnavailable|GatewayTimeout| + HttpVersionNotSupported; + +# Defines the possible success status code response record types. +type SuccessStatusCodeResponse Ok|Created|Accepted|NonAuthoritativeInformation|NoContent|ResetContent| + PartialContent; + +# The `Status` object creates the distinction for the different response status code types. +# +# + code - The response status code +public type Status distinct object { + public int code; +}; + +# The common attributed of response status code record type. +# +# + mediaType - The value of response `Content-type` header +# + headers - The response headers +# + body - The response payload +public type CommonResponse record {| + string mediaType?; + map headers?; + anydata body?; +|}; + +// Status code class declarations +# Represents the status code of `STATUS_CONTINUE`. +# +# + code - The response status code +public readonly class StatusContinue { + *Status; + public STATUS_CONTINUE code = STATUS_CONTINUE; +} + +# Represents the status code of `STATUS_SWITCHING_PROTOCOLS`. +# +# + code - The response status code +public readonly class StatusSwitchingProtocols { + *Status; + public STATUS_SWITCHING_PROTOCOLS code = STATUS_SWITCHING_PROTOCOLS; +} + +# Represents the status code of `STATUS_OK`. +# +# + code - The response status code +public readonly class StatusOK { + *Status; + public STATUS_OK code = STATUS_OK; +} + +# Represents the status code of `STATUS_CREATED`. +# +# + code - The response status code +public readonly class StatusCreated { + *Status; + public STATUS_CREATED code = STATUS_CREATED; +} + +# Represents the status code of `STATUS_ACCEPTED`. +# +# + code - The response status code +public readonly class StatusAccepted { + *Status; + public STATUS_ACCEPTED code = STATUS_ACCEPTED; +} + +# Represents the status code of `STATUS_NON_AUTHORITATIVE_INFORMATION`. +# +# + code - The response status code +public readonly class StatusNonAuthoritativeInformation { + *Status; + public STATUS_NON_AUTHORITATIVE_INFORMATION code = STATUS_NON_AUTHORITATIVE_INFORMATION; +} + +# Represents the status code of `STATUS_NO_CONTENT`. +# +# + code - The response status code +public readonly class StatusNoContent { + *Status; + public STATUS_NO_CONTENT code = STATUS_NO_CONTENT; +} + +# Represents the status code of `STATUS_RESET_CONTENT`. +# +# + code - The response status code +public readonly class StatusResetContent { + *Status; + public STATUS_RESET_CONTENT code = STATUS_RESET_CONTENT; +} + +# Represents the status code of `STATUS_PARTIAL_CONTENT`. +# +# + code - The response status code +public readonly class StatusPartialContent { + *Status; + public STATUS_PARTIAL_CONTENT code = STATUS_PARTIAL_CONTENT; +} + +# Represents the status code of `STATUS_MULTIPLE_CHOICES`. +# +# + code - The response status code +public readonly class StatusMultipleChoices { + *Status; + public STATUS_MULTIPLE_CHOICES code = STATUS_MULTIPLE_CHOICES; +} + +# Represents the status code of `STATUS_MOVED_PERMANENTLY`. +# +# + code - The response status code +public readonly class StatusMovedPermanently { + *Status; + public STATUS_MOVED_PERMANENTLY code = STATUS_MOVED_PERMANENTLY; +} + +# Represents the status code of `STATUS_FOUND`. +# +# + code - The response status code +public readonly class StatusFound { + *Status; + public STATUS_FOUND code = STATUS_FOUND; +} + +# Represents the status code of `STATUS_SEE_OTHER`. +# +# + code - The response status code +public readonly class StatusSeeOther { + *Status; + public STATUS_SEE_OTHER code = STATUS_SEE_OTHER; +} + +# Represents the status code of `STATUS_NOT_MODIFIED`. +# +# + code - The response status code +public readonly class StatusNotModified { + *Status; + public STATUS_NOT_MODIFIED code = STATUS_NOT_MODIFIED; +} + +# Represents the status code of `STATUS_USE_PROXY`. +# +# + code - The response status code +public readonly class StatusUseProxy { + *Status; + public STATUS_USE_PROXY code = STATUS_USE_PROXY; +} + +# Represents the status code of `STATUS_TEMPORARY_REDIRECT`. +# +# + code - The response status code +public readonly class StatusTemporaryRedirect { + *Status; + public STATUS_TEMPORARY_REDIRECT code = STATUS_TEMPORARY_REDIRECT; +} + +# Represents the status code of `STATUS_PERMANENT_REDIRECT`. +# +# + code - The response status code +public readonly class StatusPermanentRedirect { + *Status; + public STATUS_PERMANENT_REDIRECT code = STATUS_PERMANENT_REDIRECT; +} + +# Represents the status code of `STATUS_BAD_REQUEST`. +# +# + code - The response status code +public readonly class StatusBadRequest { + *Status; + public STATUS_BAD_REQUEST code = STATUS_BAD_REQUEST; +} + +# Represents the status code of `STATUS_UNAUTHORIZED`. +# +# + code - The response status code +public readonly class StatusUnauthorized { + *Status; + public STATUS_UNAUTHORIZED code = STATUS_UNAUTHORIZED; +} + +# Represents the status code of `STATUS_PAYMENT_REQUIRED`. +# +# + code - The response status code +public readonly class StatusPaymentRequired { + *Status; + public STATUS_PAYMENT_REQUIRED code = STATUS_PAYMENT_REQUIRED; +} + +# Represents the status code of `STATUS_FORBIDDEN`. +# +# + code - The response status code +public readonly class StatusForbidden { + *Status; + public STATUS_FORBIDDEN code = STATUS_FORBIDDEN; +} + +# Represents the status code of `STATUS_NOT_FOUND`. +# +# + code - The response status code +public readonly class StatusNotFound { + *Status; + public STATUS_NOT_FOUND code = STATUS_NOT_FOUND; +} + +# Represents the status code of `STATUS_METHOD_NOT_ALLOWED`. +# +# + code - The response status code +public readonly class StatusMethodNotAllowed { + *Status; + public STATUS_METHOD_NOT_ALLOWED code = STATUS_METHOD_NOT_ALLOWED; +} + +# Represents the status code of `STATUS_NOT_ACCEPTABLE`. +# +# + code - The response status code +public readonly class StatusNotAcceptable { + *Status; + public STATUS_NOT_ACCEPTABLE code = STATUS_NOT_ACCEPTABLE; +} + +# Represents the status code of `STATUS_PROXY_AUTHENTICATION_REQUIRED`. +# +# + code - The response status code +public readonly class StatusProxyAuthenticationRequired { + *Status; + public STATUS_PROXY_AUTHENTICATION_REQUIRED code = STATUS_PROXY_AUTHENTICATION_REQUIRED; +} + +# Represents the status code of `STATUS_REQUEST_TIMEOUT`. +# +# + code - The response status code +public readonly class StatusRequestTimeout { + *Status; + public STATUS_REQUEST_TIMEOUT code = STATUS_REQUEST_TIMEOUT; +} + +# Represents the status code of `STATUS_CONFLICT`. +# +# + code - The response status code +public readonly class StatusConflict { + *Status; + public STATUS_CONFLICT code = STATUS_CONFLICT; +} + +# Represents the status code of `STATUS_GONE`. +# +# + code - The response status code +public readonly class StatusGone { + *Status; + public STATUS_GONE code = STATUS_GONE; +} + +# Represents the status code of `STATUS_LENGTH_REQUIRED`. +# +# + code - The response status code +public readonly class StatusLengthRequired { + *Status; + public STATUS_LENGTH_REQUIRED code = STATUS_LENGTH_REQUIRED; +} + +# Represents the status code of `STATUS_PRECONDITION_FAILED`. +# +# + code - The response status code +public readonly class StatusPreconditionFailed { + *Status; + public STATUS_PRECONDITION_FAILED code = STATUS_PRECONDITION_FAILED; +} + +# Represents the status code of `STATUS_PAYLOAD_TOO_LARGE`. +# +# + code - The response status code +public readonly class StatusPayloadTooLarge { + *Status; + public STATUS_PAYLOAD_TOO_LARGE code = STATUS_PAYLOAD_TOO_LARGE; +} + +# Represents the status code of `STATUS_URI_TOO_LONG`. +# +# + code - The response status code +public readonly class StatusUriTooLong { + *Status; + public STATUS_URI_TOO_LONG code = STATUS_URI_TOO_LONG; +} + +# Represents the status code of `STATUS_UNSUPPORTED_MEDIA_TYPE`. +# +# + code - The response status code +public readonly class StatusUnsupportedMediaType { + *Status; + public STATUS_UNSUPPORTED_MEDIA_TYPE code = STATUS_UNSUPPORTED_MEDIA_TYPE; +} + +# Represents the status code of `STATUS_RANGE_NOT_SATISFIABLE`. +# +# + code - The response status code +public readonly class StatusRangeNotSatisfiable { + *Status; + public STATUS_RANGE_NOT_SATISFIABLE code = STATUS_RANGE_NOT_SATISFIABLE; +} + +# Represents the status code of `STATUS_EXPECTATION_FAILED`. +# +# + code - The response status code +public readonly class StatusExpectationFailed { + *Status; + public STATUS_EXPECTATION_FAILED code = STATUS_EXPECTATION_FAILED; +} + +# Represents the status code of `STATUS_UPGRADE_REQUIRED`. +# +# + code - The response status code +public readonly class StatusUpgradeRequired { + *Status; + public STATUS_UPGRADE_REQUIRED code = STATUS_UPGRADE_REQUIRED; +} + +# Represents the status code of `STATUS_TOO_MANY_REQUESTS`. +# +# + code - The response status code +public readonly class StatusTooManyRequests { + *Status; + public STATUS_TOO_MANY_REQUESTS code = STATUS_TOO_MANY_REQUESTS; +} + +# Represents the status code of `STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE`. +# +# + code - The response status code +public readonly class StatusRequestHeaderFieldsTooLarge { + *Status; + public STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE code = STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; +} + +# Represents the status code of `STATUS_INTERNAL_SERVER_ERROR`. +# +# + code - The response status code +public readonly class StatusInternalServerError { + *Status; + public STATUS_INTERNAL_SERVER_ERROR code = STATUS_INTERNAL_SERVER_ERROR; +} + +# Represents the status code of `STATUS_NOT_IMPLEMENTED`. +# +# + code - The response status code +public readonly class StatusNotImplemented { + *Status; + public STATUS_NOT_IMPLEMENTED code = STATUS_NOT_IMPLEMENTED; +} + +# Represents the status code of `STATUS_BAD_GATEWAY`. +# +# + code - The response status code +public readonly class StatusBadGateway { + *Status; + public STATUS_BAD_GATEWAY code = STATUS_BAD_GATEWAY; +} + +# Represents the status code of `STATUS_SERVICE_UNAVAILABLE`. +# +# + code - The response status code +public readonly class StatusServiceUnavailable { + *Status; + public STATUS_SERVICE_UNAVAILABLE code = STATUS_SERVICE_UNAVAILABLE; +} + +# Represents the status code of `STATUS_GATEWAY_TIMEOUT`. +# +# + code - The response status code +public readonly class StatusGatewayTimeout { + *Status; + public STATUS_GATEWAY_TIMEOUT code = STATUS_GATEWAY_TIMEOUT; +} + +# Represents the status code of `STATUS_HTTP_VERSION_NOT_SUPPORTED`. +# +# + code - The response status code +public readonly class StatusHttpVersionNotSupported { + *Status; + public STATUS_HTTP_VERSION_NOT_SUPPORTED code = STATUS_HTTP_VERSION_NOT_SUPPORTED; +} + +// Status code object initialization +final StatusContinue STATUS_CONTINUE_OBJ = new; +final StatusSwitchingProtocols STATUS_SWITCHING_PROTOCOLS_OBJ = new; +final StatusOK STATUS_OK_OBJ = new; +final StatusCreated STATUS_CREATED_OBJ = new; +final StatusAccepted STATUS_ACCEPTED_OBJ = new; +final StatusNonAuthoritativeInformation STATUS_NON_AUTHORITATIVE_INFORMATION_OBJ = new; +final StatusNoContent STATUS_NO_CONTENT_OBJ = new; +final StatusResetContent STATUS_RESET_CONTENT_OBJ = new; +final StatusPartialContent STATUS_PARTIAL_CONTENT_OBJ = new; +final StatusMultipleChoices STATUS_MULTIPLE_CHOICES_OBJ = new; +final StatusMovedPermanently STATUS_MOVED_PERMANENTLY_OBJ = new; +final StatusFound STATUS_FOUND_OBJ = new; +final StatusSeeOther STATUS_SEE_OTHER_OBJ = new; +final StatusNotModified STATUS_NOT_MODIFIED_OBJ = new; +final StatusUseProxy STATUS_USE_PROXY_OBJ = new; +final StatusTemporaryRedirect STATUS_TEMPORARY_REDIRECT_OBJ = new; +final StatusPermanentRedirect STATUS_PERMANENT_REDIRECT_OBJ = new; +final StatusBadRequest STATUS_BAD_REQUEST_OBJ = new; +final StatusUnauthorized STATUS_UNAUTHORIZED_OBJ = new; +final StatusPaymentRequired STATUS_PAYMENT_REQUIRED_OBJ = new; +final StatusForbidden STATUS_FORBIDDEN_OBJ = new; +final StatusNotFound STATUS_NOT_FOUND_OBJ = new; +final StatusMethodNotAllowed STATUS_METHOD_NOT_ALLOWED_OBJ = new; +final StatusNotAcceptable STATUS_NOT_ACCEPTABLE_OBJ = new; +final StatusProxyAuthenticationRequired STATUS_PROXY_AUTHENTICATION_REQUIRED_OBJ = new; +final StatusRequestTimeout STATUS_REQUEST_TIMEOUT_OBJ = new; +final StatusConflict STATUS_CONFLICT_OBJ = new; +final StatusGone STATUS_GONE_OBJ = new; +final StatusLengthRequired STATUS_LENGTH_REQUIRED_OBJ = new; +final StatusPreconditionFailed STATUS_PRECONDITION_FAILED_OBJ = new; +final StatusPayloadTooLarge STATUS_PAYLOAD_TOO_LARGE_OBJ = new; +final StatusUriTooLong STATUS_URI_TOO_LONG_OBJ = new; +final StatusUnsupportedMediaType STATUS_UNSUPPORTED_MEDIA_TYPE_OBJ = new; +final StatusRangeNotSatisfiable STATUS_RANGE_NOT_SATISFIABLE_OBJ = new; +final StatusExpectationFailed STATUS_EXPECTATION_FAILED_OBJ = new; +final StatusUpgradeRequired STATUS_UPGRADE_REQUIRED_OBJ = new; +final StatusTooManyRequests STATUS_TOO_MANY_REQUESTS_OBJ = new; +final StatusRequestHeaderFieldsTooLarge STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_OBJ = new; +final StatusInternalServerError STATUS_INTERNAL_SERVER_ERROR_OBJ = new; +final StatusNotImplemented STATUS_NOT_IMPLEMENTED_OBJ = new; +final StatusBadGateway STATUS_BAD_GATEWAY_OBJ = new; +final StatusServiceUnavailable STATUS_SERVICE_UNAVAILABLE_OBJ = new; +final StatusGatewayTimeout STATUS_GATEWAY_TIMEOUT_OBJ = new; +final StatusHttpVersionNotSupported STATUS_HTTP_VERSION_NOT_SUPPORTED_OBJ = new; + +// Status code record types +# The status code response record of `Continue`. +# +# + status - The response status code obj +public type Continue record {| + *CommonResponse; + readonly StatusContinue status = STATUS_CONTINUE_OBJ; +|}; + +# The status code response record of `SwitchingProtocols`. +# +# + status - The response status code obj +public type SwitchingProtocols record {| + *CommonResponse; + readonly StatusSwitchingProtocols status = STATUS_SWITCHING_PROTOCOLS_OBJ; +|}; + +# The status code response record of `Ok`. +# +# + status - The response status code obj +public type Ok record {| + *CommonResponse; + readonly StatusOK status = STATUS_OK_OBJ; +|}; + +# The status code response record of `Created`. +# +# + status - The response status code obj +public type Created record {| + *CommonResponse; + readonly StatusCreated status = STATUS_CREATED_OBJ; +|}; + +# The status code response record of `Accepted`. +# +# + status - The response status code obj +public type Accepted record {| + *CommonResponse; + readonly StatusAccepted status = STATUS_ACCEPTED_OBJ; +|}; + +# The status code response record of `NonAuthoritativeInformation`. +# +# + status - The response status code obj +public type NonAuthoritativeInformation record {| + *CommonResponse; + readonly StatusNonAuthoritativeInformation status = STATUS_NON_AUTHORITATIVE_INFORMATION_OBJ; +|}; + +# The status code response record of `NoContent`. +# +# + headers - The response headers +# + status - The response status code obj +public type NoContent record {| + map headers?; + readonly StatusNoContent status = STATUS_NO_CONTENT_OBJ; +|}; + +# The status code response record of `ResetContent`. +# +# + status - The response status code obj +public type ResetContent record {| + *CommonResponse; + readonly StatusResetContent status = STATUS_RESET_CONTENT_OBJ; +|}; + +# The status code response record of `PartialContent`. +# +# + status - The response status code obj +public type PartialContent record {| + *CommonResponse; + readonly StatusPartialContent status = STATUS_PARTIAL_CONTENT_OBJ; +|}; + +# The status code response record of `MultipleChoices`. +# +# + status - The response status code obj +public type MultipleChoices record {| + *CommonResponse; + readonly StatusMultipleChoices status = STATUS_MULTIPLE_CHOICES_OBJ; +|}; + +# The status code response record of `MovedPermanently`. +# +# + status - The response status code obj +public type MovedPermanently record {| + *CommonResponse; + readonly StatusMovedPermanently status = STATUS_MOVED_PERMANENTLY_OBJ; +|}; + +# The status code response record of `Found`. +# +# + status - The response status code obj +public type Found record {| + *CommonResponse; + readonly StatusFound status = STATUS_FOUND_OBJ; +|}; + +# The status code response record of `SeeOther`. +# +# + status - The response status code obj +public type SeeOther record {| + *CommonResponse; + readonly StatusSeeOther status = STATUS_SEE_OTHER_OBJ; +|}; + +# The status code response record of `NotModified`. +# +# + status - The response status code obj +public type NotModified record {| + *CommonResponse; + readonly StatusNotModified status = STATUS_NOT_MODIFIED_OBJ; +|}; + +# The status code response record of `UseProxy`. +# +# + status - The response status code obj +public type UseProxy record {| + *CommonResponse; + readonly StatusUseProxy status = STATUS_USE_PROXY_OBJ; +|}; + +# The status code response record of `TemporaryRedirect`. +# +# + status - The response status code obj +public type TemporaryRedirect record {| + *CommonResponse; + readonly StatusTemporaryRedirect status = STATUS_TEMPORARY_REDIRECT_OBJ; +|}; + +# The status code response record of `PermanentRedirect`. +# +# + status - The response status code obj +public type PermanentRedirect record {| + *CommonResponse; + readonly StatusPermanentRedirect status = STATUS_PERMANENT_REDIRECT_OBJ; +|}; + +# The status code response record of `BadRequest`. +# +# + status - The response status code obj +public type BadRequest record {| + *CommonResponse; + readonly StatusBadRequest status = STATUS_BAD_REQUEST_OBJ; +|}; + +# The status code response record of `Unauthorized`. +# +# + status - The response status code obj +public type Unauthorized record {| + *CommonResponse; + readonly StatusUnauthorized status = STATUS_UNAUTHORIZED_OBJ; +|}; + +# The status code response record of `PaymentRequired`. +# +# + status - The response status code obj +public type PaymentRequired record {| + *CommonResponse; + readonly StatusPaymentRequired status = STATUS_PAYMENT_REQUIRED_OBJ; +|}; + +# The status code response record of `Forbidden`. +# +# + status - The response status code obj +public type Forbidden record {| + *CommonResponse; + readonly StatusForbidden status = STATUS_FORBIDDEN_OBJ; +|}; + +# The status code response record of `NotFound`. +# +# + status - The response status code obj +public type NotFound record {| + *CommonResponse; + readonly StatusNotFound status = STATUS_NOT_FOUND_OBJ; +|}; + +# The status code response record of `MethodNotAllowed`. +# +# + status - The response status code obj +public type MethodNotAllowed record {| + *CommonResponse; + readonly StatusMethodNotAllowed status = STATUS_METHOD_NOT_ALLOWED_OBJ; +|}; + +# The status code response record of `NotAcceptable`. +# +# + status - The response status code obj +public type NotAcceptable record {| + *CommonResponse; + readonly StatusNotAcceptable status = STATUS_NOT_ACCEPTABLE_OBJ; +|}; + +# The status code response record of `ProxyAuthenticationRequired`. +# +# + status - The response status code obj +public type ProxyAuthenticationRequired record {| + *CommonResponse; + readonly StatusProxyAuthenticationRequired status = STATUS_PROXY_AUTHENTICATION_REQUIRED_OBJ; +|}; + +# The status code response record of `RequestTimeout`. +# +# + status - The response status code obj +public type RequestTimeout record {| + *CommonResponse; + readonly StatusRequestTimeout status = STATUS_REQUEST_TIMEOUT_OBJ; +|}; + +# The status code response record of `Conflict`. +# +# + status - The response status code obj +public type Conflict record {| + *CommonResponse; + readonly StatusConflict status = STATUS_CONFLICT_OBJ; +|}; + +# The status code response record of `Gone`. +# +# + status - The response status code obj +public type Gone record {| + *CommonResponse; + readonly StatusGone status = STATUS_GONE_OBJ; +|}; + +# The status code response record of `LengthRequired`. +# +# + status - The response status code obj +public type LengthRequired record {| + *CommonResponse; + readonly StatusLengthRequired status = STATUS_LENGTH_REQUIRED_OBJ; +|}; + +# The status code response record of `PreconditionFailed`. +# +# + status - The response status code obj +public type PreconditionFailed record {| + *CommonResponse; + readonly StatusPreconditionFailed status = STATUS_PRECONDITION_FAILED_OBJ; +|}; + +# The status code response record of `PayloadTooLarge`. +# +# + status - The response status code obj +public type PayloadTooLarge record {| + *CommonResponse; + readonly StatusPayloadTooLarge status = STATUS_PAYLOAD_TOO_LARGE_OBJ; +|}; + +# The status code response record of `UriTooLong`. +# +# + status - The response status code obj +public type UriTooLong record {| + *CommonResponse; + readonly StatusUriTooLong status = STATUS_URI_TOO_LONG_OBJ; +|}; + +# The status code response record of `UnsupportedMediaType`. +# +# + status - The response status code obj +public type UnsupportedMediaType record {| + *CommonResponse; + readonly StatusUnsupportedMediaType status = STATUS_UNSUPPORTED_MEDIA_TYPE_OBJ; +|}; + +# The status code response record of `RangeNotSatisfiable`. +# +# + status - The response status code obj +public type RangeNotSatisfiable record {| + *CommonResponse; + readonly StatusRangeNotSatisfiable status = STATUS_RANGE_NOT_SATISFIABLE_OBJ; +|}; + +# The status code response record of `ExpectationFailed`. +# +# + status - The response status code obj +public type ExpectationFailed record {| + *CommonResponse; + readonly StatusExpectationFailed status = STATUS_EXPECTATION_FAILED_OBJ; +|}; + +# The status code response record of `UpgradeRequired`. +# +# + status - The response status code obj +public type UpgradeRequired record {| + *CommonResponse; + readonly StatusUpgradeRequired status = STATUS_UPGRADE_REQUIRED_OBJ; +|}; + +# The status code response record of `TooManyRequests`. +# +# + status - The response status code obj +public type TooManyRequests record {| + *CommonResponse; + readonly StatusTooManyRequests status = STATUS_TOO_MANY_REQUESTS_OBJ; +|}; + +# The status code response record of `RequestHeaderFieldsTooLarge`. +# +# + status - The response status code obj +public type RequestHeaderFieldsTooLarge record {| + *CommonResponse; + readonly StatusRequestHeaderFieldsTooLarge status = STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_OBJ; +|}; + +# The status code response record of `InternalServerError`. +# +# + status - The response status code obj +public type InternalServerError record {| + *CommonResponse; + readonly StatusInternalServerError status = STATUS_INTERNAL_SERVER_ERROR_OBJ; +|}; + +# The status code response record of `NotImplemented`. +# +# + status - The response status code obj +public type NotImplemented record {| + *CommonResponse; + readonly StatusNotImplemented status = STATUS_NOT_IMPLEMENTED_OBJ; +|}; + +# The status code response record of `BadGateway`. +# +# + status - The response status code obj +public type BadGateway record {| + *CommonResponse; + readonly StatusBadGateway status = STATUS_BAD_GATEWAY_OBJ; +|}; + +# The status code response record of `ServiceUnavailable`. +# +# + status - The response status code obj +public type ServiceUnavailable record {| + *CommonResponse; + readonly StatusServiceUnavailable status = STATUS_SERVICE_UNAVAILABLE_OBJ; +|}; + +# The status code response record of `GatewayTimeout`. +# +# + status - The response status code obj +public type GatewayTimeout record {| + *CommonResponse; + readonly StatusGatewayTimeout status = STATUS_GATEWAY_TIMEOUT_OBJ; +|}; + +# The status code response record of `HttpVersionNotSupported`. +# +# + status - The response status code obj +public type HttpVersionNotSupported record {| + *CommonResponse; + readonly StatusHttpVersionNotSupported status = STATUS_HTTP_VERSION_NOT_SUPPORTED_OBJ; +|}; + +# The common status code response constant of `Continue`. +public final readonly & Continue CONTINUE = {}; + +# The common status code response constant of `SwitchingProtocols`. +public final readonly & SwitchingProtocols SWITCHING_PROTOCOLS = {}; + +# The common status code response constant of `Ok`. +public final readonly & Ok OK = {}; + +# The common status code response constant of `Created`. +public final readonly & Created CREATED = {}; + +# The common status code response constant of `Accepted`. +public final readonly & Accepted ACCEPTED = {}; + +# The common status code response constant of `NonAuthoritativeInformation`. +public final readonly & NonAuthoritativeInformation NON_AUTHORITATIVE_INFORMATION = {}; + +# The common status code response constant of `NoContent`. +public final readonly & NoContent NO_CONTENT = {}; + +# The common status code response constant of `ResetContent`. +public final readonly & ResetContent RESET_CONTENT = {}; + +# The common status code response constant of `PartialContent`. +public final readonly & PartialContent PARTIAL_CONTENT = {}; + +# The common status code response constant of `MultipleChoices`. +public final readonly & MultipleChoices MULTIPLE_CHOICES = {}; + +# The common status code response constant of `MovedPermanently`. +public final readonly & MovedPermanently MOVED_PERMANENTLY = {}; + +# The common status code response constant of `Found`. +public final readonly & Found FOUND = {}; + +# The common status code response constant of `SeeOther`. +public final readonly & SeeOther SEE_OTHER = {}; + +# The common status code response constant of `NotModified`. +public final readonly & NotModified NOT_MODIFIED = {}; + +# The common status code response constant of `UseProxy`. +public final readonly & UseProxy USE_PROXY = {}; + +# The common status code response constant of `TemporaryRedirect`. +public final readonly & TemporaryRedirect TEMPORARY_REDIRECT = {}; + +# The common status code response constant of `PermanentRedirect`. +public final readonly & PermanentRedirect PERMANENT_REDIRECT = {}; + +# The common status code response constant of `BadRequest`. +public final readonly & BadRequest BAD_REQUEST = {}; + +# The common status code response constant of `Unauthorized`. +public final readonly & Unauthorized UNAUTHORIZED = {}; + +# The common status code response constant of `PaymentRequired`. +public final readonly & PaymentRequired PAYMENT_REQUIRED = {}; + +# The common status code response constant of `Forbidden`. +public final readonly & Forbidden FORBIDDEN = {}; + +# The common status code response constant of `NotFound`. +public final readonly & NotFound NOT_FOUND = {}; + +# The common status code response constant of `MethodNotAllowed`. +public final readonly & MethodNotAllowed METHOD_NOT_ALLOWED = {}; + +# The common status code response constant of `NotAcceptable`. +public final readonly & NotAcceptable NOT_ACCEPTABLE = {}; + +# The common status code response constant of `ProxyAuthenticationRequired`. +public final readonly & ProxyAuthenticationRequired PROXY_AUTHENTICATION_REQUIRED = {}; + +# The common status code response constant of `RequestTimeout`. +public final readonly & RequestTimeout REQUEST_TIMEOUT = {}; + +# The common status code response constant of `Conflict`. +public final readonly & Conflict CONFLICT = {}; + +# The common status code response constant of `Gone`. +public final readonly & Gone GONE = {}; + +# The common status code response constant of `LengthRequired`. +public final readonly & LengthRequired LENGTH_REQUIRED = {}; + +# The common status code response constant of `PreconditionFailed`. +public final readonly & PreconditionFailed PRECONDITION_FAILED = {}; + +# The common status code response constant of `PayloadTooLarge`. +public final readonly & PayloadTooLarge PAYLOAD_TOO_LARGE = {}; + +# The common status code response constant of `UriTooLong`. +public final readonly & UriTooLong URI_TOO_LONG = {}; + +# The common status code response constant of `UnsupportedMediaType`. +public final readonly & UnsupportedMediaType UNSUPPORTED_MEDIA_TYPE = {}; + +# The common status code response constant of `RangeNotSatisfiable`. +public final readonly & RangeNotSatisfiable RANGE_NOT_SATISFIABLE = {}; + +# The common status code response constant of `ExpectationFailed`. +public final readonly & ExpectationFailed EXPECTATION_FAILED = {}; + +# The common status code response constant of `UpgradeRequired`. +public final readonly & UpgradeRequired UPGRADE_REQUIRED = {}; + +# The common status code response constant of `TooManyRequests`. +public final readonly & TooManyRequests TOO_MANY_REQUESTS = {}; + +# The common status code response constant of `RequestHeaderFieldsTooLarge`. +public final readonly & RequestHeaderFieldsTooLarge REQUEST_HEADER_FIELDS_TOO_LARGE = {}; + +# The common status code response constant of `InternalServerError`. +public final readonly & InternalServerError INTERNAL_SERVER_ERROR = {}; + +# The common status code response constant of `NotImplemented`. +public final readonly & NotImplemented NOT_IMPLEMENTED = {}; + +# The common status code response constant of `BadGateway`. +public final readonly & BadGateway BAD_GATEWAY = {}; + +# The common status code response constant of `ServiceUnavailable`. +public final readonly & ServiceUnavailable SERVICE_UNAVAILABLE = {}; + +# The common status code response constant of `GatewayTimeout`. +public final readonly & GatewayTimeout GATEWAY_TIMEOUT = {}; + +# The common status code response constant of `HttpVersionNotSupported`. +public final readonly & HttpVersionNotSupported HTTP_VERSION_NOT_SUPPORTED = {}; diff --git a/ballerina/http_status_codes.bal b/ballerina/http_status_codes.bal new file mode 100644 index 00000000..47686701 --- /dev/null +++ b/ballerina/http_status_codes.bal @@ -0,0 +1,108 @@ +// Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. + +# The HTTP response status code: 100 Continue +public const int STATUS_CONTINUE = 100; +# The HTTP response status code: 101 Switching Protocols +public const int STATUS_SWITCHING_PROTOCOLS = 101; + +# The HTTP response status code: 200 OK +public const int STATUS_OK = 200; +# The HTTP response status code: 201 Created +public const int STATUS_CREATED = 201; +# The HTTP response status code: 202 Accepted +public const int STATUS_ACCEPTED = 202; +# The HTTP response status code: 203 Non Authoritative Information +public const int STATUS_NON_AUTHORITATIVE_INFORMATION = 203; +# The HTTP response status code: 204 No Content +public const int STATUS_NO_CONTENT = 204; +# The HTTP response status code: 205 Reset Content +public const int STATUS_RESET_CONTENT = 205; +# The HTTP response status code: 206 Partial Content +public const int STATUS_PARTIAL_CONTENT = 206; + +# The HTTP response status code: 300 Multiple Choices +public const int STATUS_MULTIPLE_CHOICES = 300; +# The HTTP response status code: 301 Moved Permanently +public const int STATUS_MOVED_PERMANENTLY = 301; +# The HTTP response status code: 302 Found +public const int STATUS_FOUND = 302; +# The HTTP response status code: 303 See Other +public const int STATUS_SEE_OTHER = 303; +# The HTTP response status code: 304 Not Modified +public const int STATUS_NOT_MODIFIED = 304; +# The HTTP response status code: 305 Use Proxy +public const int STATUS_USE_PROXY = 305; +# The HTTP response status code: 307 Temporary Redirect +public const int STATUS_TEMPORARY_REDIRECT = 307; +# The HTTP response status code: 308 Permanent Redirect +public const int STATUS_PERMANENT_REDIRECT = 308; + +# The HTTP response status code: 400 Bad Request +public const int STATUS_BAD_REQUEST = 400; +# The HTTP response status code: 401 Unauthorized +public const int STATUS_UNAUTHORIZED = 401; +# The HTTP response status code: 402 Payment Required +public const int STATUS_PAYMENT_REQUIRED = 402; +# The HTTP response status code: 403 Forbidden +public const int STATUS_FORBIDDEN = 403; +# The HTTP response status code: 404 Not Found +public const int STATUS_NOT_FOUND = 404; +# The HTTP response status code: 405 Method Not Allowed +public const int STATUS_METHOD_NOT_ALLOWED = 405; +# The HTTP response status code: 406 Not Acceptable +public const int STATUS_NOT_ACCEPTABLE = 406; +# The HTTP response status code: 407 Proxy Authentication Required +public const int STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; +# The HTTP response status code: 408 Request Timeout +public const int STATUS_REQUEST_TIMEOUT = 408; +# The HTTP response status code: 409 Conflict +public const int STATUS_CONFLICT = 409; +# The HTTP response status code: 410 Gone +public const int STATUS_GONE = 410; +# The HTTP response status code: 411 Length Required +public const int STATUS_LENGTH_REQUIRED = 411; +# The HTTP response status code: 412 Precondition Failed +public const int STATUS_PRECONDITION_FAILED = 412; +# The HTTP response status code: 413 Payload Too Large +public const int STATUS_PAYLOAD_TOO_LARGE = 413; +# The HTTP response status code: 414 URI Too Long +public const int STATUS_URI_TOO_LONG = 414; +# The HTTP response status code: 415 Unsupported Media Type +public const int STATUS_UNSUPPORTED_MEDIA_TYPE = 415; +# The HTTP response status code: 416 Range Not Satisfiable +public const int STATUS_RANGE_NOT_SATISFIABLE = 416; +# The HTTP response status code: 417 Expectation Failed +public const int STATUS_EXPECTATION_FAILED = 417; +# The HTTP response status code: 426 Upgrade Required +public const int STATUS_UPGRADE_REQUIRED = 426; +# The HTTP response status code: 429 Too Many Requests +public const int STATUS_TOO_MANY_REQUESTS = 429; +# The HTTP response status code: 431 Request Header Fields Too Large +public const int STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + +# The HTTP response status code: 500 Internal Server Error +public const int STATUS_INTERNAL_SERVER_ERROR = 500; +# The HTTP response status code: 501 Not Implemented +public const int STATUS_NOT_IMPLEMENTED = 501; +# The HTTP response status code: 502 Bad Gateway +public const int STATUS_BAD_GATEWAY = 502; +# The HTTP response status code: 503 Service Unavailable +public const int STATUS_SERVICE_UNAVAILABLE = 503; +# The HTTP response status code: 504 Gateway Timeout +public const int STATUS_GATEWAY_TIMEOUT = 504; +# The HTTP response status code: 505 HTTP Version Not Supported +public const int STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index a51c31e1..9be0d455 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -29,6 +29,7 @@ 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.api.values.BString; import java.util.ArrayList; @@ -81,6 +82,50 @@ public void notifySuccess(Object result) { // } if (this.annotations.get(0).equals("QueueOutput") || this.annotations.get(0).equals("CosmosDBOutput")) { mapValue.put(StringUtils.fromString("outMsg"), result); + } else if (this.annotations.get(0).equals("HTTPOutput")) { + if (isHTTPResponse(result)) { + BObject status = (BObject) (((BMap) result).get(StringUtils.fromString("status"))); + Object statusCode = Long.toString(status.getIntValue(StringUtils.fromString("code"))); + statusCode = StringUtils.fromString((String) statusCode); + + BMap bodyMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + bodyMap.put(StringUtils.fromString("statusCode"), statusCode); + + if (((BMap) result).containsKey(StringUtils.fromString("body"))) { + Object body = ((BMap) result).get(StringUtils.fromString("body")); + bodyMap.put(StringUtils.fromString("body"), body); + } + + if (((BMap) result).containsKey(StringUtils.fromString("headers"))) { + Object headers = ((BMap) result).get(StringUtils.fromString("headers")); + if (!((BMap) headers).containsKey(StringUtils.fromString("content-type"))) { + ((BMap) headers).put(StringUtils.fromString("content-type"), + StringUtils.fromString("text/plain")); + } + bodyMap.put(StringUtils.fromString("headers"), headers); + } else { + Object headers = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + ((BMap) headers).put(StringUtils.fromString("content-type"), + StringUtils.fromString("text/plain")); + bodyMap.put(StringUtils.fromString("headers"), headers); + + } + + if (((BMap) result).containsKey(StringUtils.fromString("mediaType"))) { + Object headers = ((BMap) result).get(StringUtils.fromString("headers")); + Object mediaType = ((BMap) result).get(StringUtils.fromString("mediaType")); + ((BMap) headers).put(StringUtils.fromString("content-type"), mediaType); + } + mapValue.put(StringUtils.fromString("resp"), bodyMap); + + } else { + BMap respMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + respMap.put(StringUtils.fromString("body"), result); + mapValue.put(StringUtils.fromString("resp"), respMap); + } } else { BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); @@ -106,4 +151,9 @@ private boolean isModuleDefinedError(BError error) { String packageName = packageDetails.getName(); return Constants.PACKAGE_ORG.equals(orgName) && Constants.PACKAGE_NAME.equals(packageName); } + + private boolean isHTTPResponse(Object result) { + return (result instanceof BMap) && (((BMap) result).containsKey(fromString("status"))) && + ((result.getClass()).getPackageName()).contains("ballerinax.azure_functions"); + } } From 28a36229736dd08d4ece2a598dbd7a7105691ce2 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Mon, 1 Aug 2022 15:45:29 +0530 Subject: [PATCH 05/59] Add tests for HTTP response support --- ballerina-tests/main.bal | 25 ++++ .../tests/resources/httpResTest1.json | 108 ++++++++++++++++++ .../tests/resources/httpResTest2.json | 108 ++++++++++++++++++ .../tests/resources/httpResTest3.json | 108 ++++++++++++++++++ .../tests/resources/httpResTest4.json | 108 ++++++++++++++++++ ballerina-tests/tests/test.bal | 46 ++++++++ 6 files changed, 503 insertions(+) create mode 100644 ballerina-tests/tests/resources/httpResTest1.json create mode 100644 ballerina-tests/tests/resources/httpResTest2.json create mode 100644 ballerina-tests/tests/resources/httpResTest3.json create mode 100644 ballerina-tests/tests/resources/httpResTest4.json diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index a794f4b5..edefddb3 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -16,6 +16,31 @@ service "hello" on ep { resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from . path "; } + resource function post httpResTest1(@af:Payload string greeting) returns @af:HTTPOutput af:Unauthorized { + af:Unauthorized unauth = {body: "Helloworld.....", + mediaType: "application/account+json", + headers: { + "Location": "/myServer/084230" + }}; + return unauth; + } + + resource function post httpResTest2(@af:Payload string greeting) returns @af:HTTPOutput af:Ok { + af:Ok ok = {body: "Helloworld....."}; + return ok; + } + resource function post httpResTest3(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { + af:InternalServerError err = {body: "Helloworld.....", + headers: { + "content-type": "application/json+id", + "Location": "/myServer/084230" + }}; + return err; + } + resource function post httpResTest4(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { + af:InternalServerError err = {}; + return err; + } resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from foo path " + greeting; diff --git a/ballerina-tests/tests/resources/httpResTest1.json b/ballerina-tests/tests/resources/httpResTest1.json new file mode 100644 index 00000000..5134bc83 --- /dev/null +++ b/ballerina-tests/tests/resources/httpResTest1.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest1", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/httpResTest1" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/httpResTest1" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/httpResTest1", + "X-WAWS-Unencoded-URL": "/api/hello/httpResTest1" + }, + "sys": { + "MethodName": "post-hello-httpResTest1", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/httpResTest2.json b/ballerina-tests/tests/resources/httpResTest2.json new file mode 100644 index 00000000..a39a835e --- /dev/null +++ b/ballerina-tests/tests/resources/httpResTest2.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest2", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/httpResTest2" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/httpResTest2" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/httpResTest2", + "X-WAWS-Unencoded-URL": "/api/hello/httpResTest2" + }, + "sys": { + "MethodName": "post-hello-httpResTest2", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/httpResTest3.json b/ballerina-tests/tests/resources/httpResTest3.json new file mode 100644 index 00000000..5f998c76 --- /dev/null +++ b/ballerina-tests/tests/resources/httpResTest3.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest3", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/httpResTest3" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/httpResTest3" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/httpResTest3", + "X-WAWS-Unencoded-URL": "/api/hello/httpResTest3" + }, + "sys": { + "MethodName": "post-hello-httpResTest3", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/httpResTest4.json b/ballerina-tests/tests/resources/httpResTest4.json new file mode 100644 index 00000000..5c59be97 --- /dev/null +++ b/ballerina-tests/tests/resources/httpResTest4.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest4", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/httpResTest4" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/httpResTest4" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/httpResTest4", + "X-WAWS-Unencoded-URL": "/api/hello/httpResTest4" + }, + "sys": { + "MethodName": "post-hello-httpResTest4", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 256bf876..253f3e70 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -12,6 +12,52 @@ function testBaseDot() returns error? { test:assertEquals(resp, expectedResp); } +@test:Config { } +function httpResTest1() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest1.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest1", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"401", "body":"Helloworld.....", + "headers":{"Location":"/myServer/084230", "content-type":"application/account+json"}}}, + "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function httpResTest2() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest2.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest2", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"200", "body":"Helloworld.....", + "headers":{"content-type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function httpResTest3() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest3.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest3", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"500", "body":"Helloworld.....", + "headers":{"content-type":"application/json+id", "Location":"/myServer/084230"}}}, + "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function httpResTest4() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest4.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest4", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"500", + "headers":{"content-type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + @test:Config { } function testSimpleResourcePath() returns error? { From 637432678a04913a2b5e558d30713fc95085d119 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 2 Aug 2022 13:12:57 +0530 Subject: [PATCH 06/59] Modify based on review --- ballerina-tests/main.bal | 96 +++++----- ballerina-tests/tests/test.bal | 177 ++++++++++-------- ballerina/http_status_code_types.bal | 2 +- ballerina/http_status_codes.bal | 2 +- .../stdlib/azure/functions/Constants.java | 13 ++ .../azure/functions/FunctionCallback.java | 100 ++++++---- 6 files changed, 233 insertions(+), 157 deletions(-) diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index edefddb3..4a75cfcf 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -17,75 +17,82 @@ service "hello" on ep { return "Hello from . path "; } resource function post httpResTest1(@af:Payload string greeting) returns @af:HTTPOutput af:Unauthorized { - af:Unauthorized unauth = {body: "Helloworld.....", + af:Unauthorized unauth = { + body: "Helloworld.....", mediaType: "application/account+json", headers: { "Location": "/myServer/084230" - }}; + } + }; return unauth; } - resource function post httpResTest2(@af:Payload string greeting) returns @af:HTTPOutput af:Ok { - af:Ok ok = {body: "Helloworld....."}; - return ok; - } - resource function post httpResTest3(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { - af:InternalServerError err = {body: "Helloworld.....", - headers: { - "content-type": "application/json+id", - "Location": "/myServer/084230" - }}; - return err; - } - resource function post httpResTest4(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { - af:InternalServerError err = {}; - return err; - } + resource function post httpResTest2(@af:Payload string greeting) returns @af:HTTPOutput af:Ok { + af:Ok ok = {body: "Helloworld....."}; + return ok; + } + resource function post httpResTest3(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { + af:InternalServerError err = { + body: "Helloworld.....", + headers: { + "content-type": "application/json+id", + "Location": "/myServer/084230" + } + }; + return err; + } + resource function post httpResTest4(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { + af:InternalServerError err = {}; + return err; + } resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from foo path " + greeting; } - resource function post foo/[string bar](@af:Payload string greeting) returns @af:HTTPOutput string { + resource function post foo/[string bar](@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from foo param " + bar; } - resource function post foo/bar(@af:Payload string greeting) returns @af:HTTPOutput string { + resource function post foo/bar(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from foo bar res"; } resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { - return "Hello from the query " + greeting + " " + name; + return "Hello from the query " + greeting + " " + name; } resource function post db(@af:Payload string greeting, @af:CosmosDBInput { - connectionStringSetting: "CosmosDBConnection",databaseName: "db1", - collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:HTTPOutput string|error { - return "Hello " + greeting + input1[0].id; + connectionStringSetting: "CosmosDBConnection", + databaseName: "db1", + collectionName: "c2", + sqlQuery: "SELECT * FROM Items" + } DBEntry[] input1) returns @af:HTTPOutput string|error { + return "Hello " + greeting + input1[0].id; } - resource function post payload/jsonToRecord (@af:Payload Person greeting) returns @af:HTTPOutput string|error { + resource function post payload/jsonToRecord(@af:Payload Person greeting) returns @af:HTTPOutput string|error { return "Hello from json to record " + greeting.name; } - resource function post payload/jsonToJson (@af:Payload json greeting) returns @af:HTTPOutput string|error { + resource function post payload/jsonToJson(@af:Payload json greeting) returns @af:HTTPOutput string|error { string name = check greeting.name; - return "Hello from json to json "+ name; + return "Hello from json to json " + name; } - resource function post payload/xmlToXml (@af:Payload xml greeting) returns @af:HTTPOutput string|error { + resource function post payload/xmlToXml(@af:Payload xml greeting) returns @af:HTTPOutput string|error { return greeting.toJsonString(); } - resource function post payload/textToString (@af:Payload string greeting) returns @af:HTTPOutput string|error { + resource function post payload/textToString(@af:Payload string greeting) returns @af:HTTPOutput string|error { return greeting; } - resource function post payload/textToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + resource function post payload/textToByte(@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { return string:fromBytes(greeting); } - resource function post payload/octaToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + resource function post payload/octaToByte(@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { return string:fromBytes(greeting); } } @@ -94,8 +101,8 @@ service "hello" on ep { queueName: "queue2" } service "queue" on new af:QueueListener() { - remote function onMessage (@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { - return "helloo "+ inMsg; + remote function onMessage(@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo " + inMsg; } } @@ -103,17 +110,18 @@ service "queue" on new af:QueueListener() { listener af:CosmosDBListener cosmosEp = new (); service "cosmos" on cosmosEp { - remote function onUpdated (@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated(@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; - return "helloo "+ id; + return "helloo " + id; } } -@af:TimerTrigger { schedule: "*/10 * * * * *" } +@af:TimerTrigger {schedule: "*/10 * * * * *"} listener af:TimerListener timerListener = new af:TimerListener(); + service "timer" on timerListener { - remote function onTrigger (@af:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { - return "helloo "+ inMsg.IsPastDue.toString(); + remote function onTrigger(@af:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo " + inMsg.IsPastDue.toString(); } } @@ -122,11 +130,15 @@ service "timer" on timerListener { } listener af:QueueListener queueListener1 = new af:QueueListener(); + service "queue-input" on queueListener1 { - remote function onMessage (@af:Payload string inMsg, @af:CosmosDBInput { - connectionStringSetting: "CosmosDBConnection",databaseName: "db1", - collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:QueueOutput {queueName: "queue3"} string|error { - return "helloo "+ inMsg + " " +input1[0].id; + remote function onMessage(@af:Payload string inMsg, @af:CosmosDBInput { + connectionStringSetting: "CosmosDBConnection", + databaseName: "db1", + collectionName: "c2", + sqlQuery: "SELECT * FROM Items" + } DBEntry[] input1) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo " + inMsg + " " + input1[0].id; } } diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 253f3e70..f66a3620 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -2,203 +2,232 @@ import ballerina/test; import ballerina/io; import ballerina/http; -@test:Config { } +@test:Config {} function testBaseDot() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/base-dot.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from . path "}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/base-dot.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello", readJson); + json expectedResp = {"Outputs": {"resp": {"body": "Hello from . path "}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function httpResTest1() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/httpResTest1.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-httpResTest1", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"401", "body":"Helloworld.....", - "headers":{"Location":"/myServer/084230", "content-type":"application/account+json"}}}, - "Logs":[], "ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest1.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest1", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "401", + "body": "Helloworld.....", + "headers": {"Location": "/myServer/084230", "content-type": "application/account+json"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function httpResTest2() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/httpResTest2.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-httpResTest2", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"200", "body":"Helloworld.....", - "headers":{"content-type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest2.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest2", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "body": "Helloworld.....", + "headers": {"content-type": "text/plain"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function httpResTest3() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/httpResTest3.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-httpResTest3", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"500", "body":"Helloworld.....", - "headers":{"content-type":"application/json+id", "Location":"/myServer/084230"}}}, - "Logs":[], "ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest3.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest3", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "500", + "body": "Helloworld.....", + "headers": {"content-type": "application/json+id", "Location": "/myServer/084230"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function httpResTest4() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/httpResTest4.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-httpResTest4", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"500", - "headers":{"content-type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpResTest4.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-httpResTest4", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "500", + "headers": {"content-type": "text/plain"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } - -@test:Config { } +@test:Config {} function testSimpleResourcePath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/res-path.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo path Jack"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "Hello from foo path Jack"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testSimpleMultiResourcePath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/res-path-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo-bar", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo bar res"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "Hello from foo bar res"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } - -@test:Config { } +@test:Config {} function testSimpleMultiQueryPath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/query-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-query", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from the query Jack test1"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "Hello from the query Jack test1"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } - -@test:Config { } +@test:Config {} function testSimpleQueue() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/queue-string.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/queue", readJson); - json expectedResp = {"Outputs":{"outMsg":"helloo aaaaa"},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"outMsg": "helloo aaaaa"}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testCosmosInputArr() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/cosmos-db-arr.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-db", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello Jackhello1"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "Hello Jackhello1"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testJsonJsonPayload() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/payload-json-json.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-jsonToJson", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from json to json Anjana"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "Hello from json to json Anjana"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } + +@test:Config {} function testJsonRecordPayload() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/payload-json-record.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-jsonToRecord", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from json to record Anjana"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "Hello from json to record Anjana"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testXmlPayload() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/payload-xml-xml.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-xmlToXml", readJson); string xmlPayload = "\"\\n Anjana<\\/name>\\n 12<\\/age>\\n<\\/root>\""; - json expectedResp = {"Outputs":{"resp":{"body":xmlPayload}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": xmlPayload}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testTextStringPayload() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/payload-text-string.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-textToString", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"hello from byte\n"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testTextBytePayload() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/payload-text-byte.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-textToByte", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"hello from byte\n"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testOctaBytePayload() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/payload-octa-byte.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-octaToByte", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"hello from byte arr\n"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"body": "hello from byte arr\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } - -@test:Config { } +@test:Config {} function testCosmosTrigger() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/trigger-cosmos-base.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/cosmos", readJson); - json expectedResp = {"Outputs":{"outMsg":"helloo ehee"},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"outMsg": "helloo ehee"}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } - -@test:Config { } +@test:Config {} function testTimerTrigger() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/timer.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/timer", readJson); - json expectedResp = {"Outputs":{"outMsg":"helloo false"},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"outMsg": "helloo false"}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testQueueInput() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/queue-input.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/queue-input", readJson); - json expectedResp = {"Outputs":{"outMsg":"helloo qqeeewwww hello1"},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"outMsg": "helloo qqeeewwww hello1"}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } diff --git a/ballerina/http_status_code_types.bal b/ballerina/http_status_code_types.bal index f51fce51..821a5cbf 100644 --- a/ballerina/http_status_code_types.bal +++ b/ballerina/http_status_code_types.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina/http_status_codes.bal b/ballerina/http_status_codes.bal index 47686701..1c999d60 100644 --- a/ballerina/http_status_codes.bal +++ b/ballerina/http_status_codes.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2018 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index d8cf80cc..b47c6e2d 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -26,4 +26,17 @@ public interface Constants { String PACKAGE_NAME = "azure_functions"; String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; + + String QUEUE_OUTPUT = "QueueOutput"; + String COSMOS_DBOUTPUT = "CosmosDBOutput"; + String OUT_MSG = "outMsg"; + String HTTP_OUTPUT = "HTTPOutput"; + String STATUS = "status"; + String CODE = "code"; + String STATUS_CODE = "statusCode"; + String BODY = "body"; + String HEADERS = "headers"; + String CONTENT_TYPE = "content-type"; + String MEDIA_TYPE = "mediaType"; + String RESP = "resp"; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 9be0d455..db768689 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -27,6 +27,7 @@ import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; @@ -34,6 +35,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import static io.ballerina.runtime.api.utils.StringUtils.fromString; @@ -42,10 +44,12 @@ */ public class FunctionCallback implements Callback { + private final Future future; private final Module module; private final List annotations; + public FunctionCallback(Future future, Module module, Object[] annotations) { this.future = future; this.module = module; @@ -69,68 +73,73 @@ public void notifySuccess(Object result) { } BMap mapValue = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); -// if (result instanceof BArray) { -// BArray result1 = (BArray) result; -// Object[] values = result1.getValues(); -// for (int i = 0; i < values.length; i++) { -// Object obj = values[i]; -// String identifier = generateUniqueIdentifier(i); -// mapValue.put(StringUtils.fromString(identifier), obj); -// } -// future.complete(mapValue); -// return; -// } - if (this.annotations.get(0).equals("QueueOutput") || this.annotations.get(0).equals("CosmosDBOutput")) { - mapValue.put(StringUtils.fromString("outMsg"), result); - } else if (this.annotations.get(0).equals("HTTPOutput")) { + if (Constants.QUEUE_OUTPUT.equals(this.annotations.get(0)) || + Constants.COSMOS_DBOUTPUT.equals(this.annotations.get(0))) { + + mapValue.put(StringUtils.fromString(Constants.OUT_MSG), result); + // Check HTTPOutput with annotations + } else if (Constants.HTTP_OUTPUT.equals(this.annotations.get(0))) { + //Check HTTPResponse if (isHTTPResponse(result)) { - BObject status = (BObject) (((BMap) result).get(StringUtils.fromString("status"))); - Object statusCode = Long.toString(status.getIntValue(StringUtils.fromString("code"))); + BMap resultMap = (BMap) result; + + // Extract status code + BObject status = (BObject) (resultMap.get(StringUtils.fromString(Constants.STATUS))); + Object statusCode = Long.toString(status.getIntValue(StringUtils.fromString(Constants.CODE))); statusCode = StringUtils.fromString((String) statusCode); - BMap bodyMap = + // Create a BMap for response field + BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - bodyMap.put(StringUtils.fromString("statusCode"), statusCode); + respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); - if (((BMap) result).containsKey(StringUtils.fromString("body"))) { - Object body = ((BMap) result).get(StringUtils.fromString("body")); - bodyMap.put(StringUtils.fromString("body"), body); + // Create body field in the response Map + if (resultMap.containsKey(StringUtils.fromString(Constants.BODY))) { + Object body = resultMap.get(StringUtils.fromString(Constants.BODY)); + respMap.put(StringUtils.fromString(Constants.BODY), body); } - if (((BMap) result).containsKey(StringUtils.fromString("headers"))) { - Object headers = ((BMap) result).get(StringUtils.fromString("headers")); - if (!((BMap) headers).containsKey(StringUtils.fromString("content-type"))) { - ((BMap) headers).put(StringUtils.fromString("content-type"), + // Create header field in the response Map + if (resultMap.containsKey(StringUtils.fromString(Constants.HEADERS))) { + Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); + BMap headersMap = (BMap) headers; + // Add Content-type field in headers if there is not + if (!isContentTypeExist(headersMap)) { + headersMap.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("text/plain")); } - bodyMap.put(StringUtils.fromString("headers"), headers); + respMap.put(StringUtils.fromString(Constants.HEADERS), headers); } else { + // If there is no headers add one with default content-type Object headers = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - ((BMap) headers).put(StringUtils.fromString("content-type"), + ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("text/plain")); - bodyMap.put(StringUtils.fromString("headers"), headers); + respMap.put(StringUtils.fromString(Constants.HEADERS), headers); } - if (((BMap) result).containsKey(StringUtils.fromString("mediaType"))) { - Object headers = ((BMap) result).get(StringUtils.fromString("headers")); - Object mediaType = ((BMap) result).get(StringUtils.fromString("mediaType")); - ((BMap) headers).put(StringUtils.fromString("content-type"), mediaType); + // If there is mediaType replace content-type in headers + if (resultMap.containsKey(StringUtils.fromString(Constants.MEDIA_TYPE))) { + Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); + Object mediaType = resultMap.get(StringUtils.fromString(Constants.MEDIA_TYPE)); + ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), mediaType); } - mapValue.put(StringUtils.fromString("resp"), bodyMap); + mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } else { + //Handle result except HTTPResponse cases BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - respMap.put(StringUtils.fromString("body"), result); - mapValue.put(StringUtils.fromString("resp"), respMap); + respMap.put(StringUtils.fromString(Constants.BODY), result); + mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } } else { + // Handle other output bindings BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - respMap.put(StringUtils.fromString("body"), result); - mapValue.put(StringUtils.fromString("resp"), respMap); + respMap.put(StringUtils.fromString(Constants.BODY), result); + mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } future.complete(mapValue); } @@ -153,7 +162,20 @@ private boolean isModuleDefinedError(BError error) { } private boolean isHTTPResponse(Object result) { - return (result instanceof BMap) && (((BMap) result).containsKey(fromString("status"))) && - ((result.getClass()).getPackageName()).contains("ballerinax.azure_functions"); + Module resultPkg = TypeUtils.getType(result).getPackage(); + return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))) && + Constants.PACKAGE_ORG.equals(resultPkg.getOrg()) && + Constants.PACKAGE_NAME.equals(resultPkg.getName()); + //TODO : Check inheritance + //(https://github.com/ballerina-platform/module-ballerinax-azure.functions/issues/490) + } + + private boolean isContentTypeExist(BMap headersMap) { + for (BString headerKey : headersMap.getKeys()) { + if (headerKey.getValue().toLowerCase(Locale.ROOT).equals(Constants.CONTENT_TYPE)) { + return true; + } + } + return false; } } From 240ba33092b05d34e369862869d745b8dec619cd Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 26 Jul 2022 19:36:50 +0530 Subject: [PATCH 07/59] Add blob implementation --- ballerina-tests/main.bal | 11 +++++ .../tests/resources/blob-trigger.json | 43 +++++++++++++++++ ballerina/dispatcher_service.bal | 4 +- .../azurefunctions/test/HandlerTest.java | 22 ++++++++- .../src/test/resources/handlers/main.bal | 20 ++++++++ .../service/blob/BlobOutputBinding.java | 2 +- .../stdlib/azure/functions/Constants.java | 1 + .../azure/functions/FunctionCallback.java | 19 ++++++-- .../functions/NativeHttpToAzureAdaptor.java | 48 +------------------ .../azure/functions/NativeRemoteAdapter.java | 25 +++++++++- .../stdlib/azure/functions/ParamHandler.java | 12 +++++ publish.sh | 6 +++ spec/spec.md | 4 +- 13 files changed, 156 insertions(+), 61 deletions(-) create mode 100644 ballerina-tests/tests/resources/blob-trigger.json create mode 100755 publish.sh diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 4a75cfcf..ef3f01bc 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -142,3 +142,14 @@ service "queue-input" on queueListener1 { } } +@af:BlobTrigger { + path: "bpath1/{name}" +} +listener af:BlobListener blobListener = new af:BlobListener(); + +service "blob" on blobListener { + remote function onUpdated (@af:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { + path: "bpath1/newBlob" } byte[]|error { + return blobIn; + } +} diff --git a/ballerina-tests/tests/resources/blob-trigger.json b/ballerina-tests/tests/resources/blob-trigger.json new file mode 100644 index 00000000..75a124f3 --- /dev/null +++ b/ballerina-tests/tests/resources/blob-trigger.json @@ -0,0 +1,43 @@ +{ + "Data": { + "blobIn": "aGVsbG8gZnJvbSBieXRlCg==" + }, + "Metadata": { + "Uri": "\"https:\/\/baldevgroup84df.blob.core.windows.net\/bpath1\/hello.txt\"", + "Properties": { + "CacheControl": null, + "ContentDisposition": null, + "ContentEncoding": null, + "ContentLanguage": null, + "Length": 16, + "ContentMD5": "vNUQNie6YzklPqNxnmy97g==", + "ContentType": "text/plain", + "ETag": "\"0x8DA6E28262505DD\"", + "Created": "2022-07-25T10:26:34+00:00", + "LastModified": "2022-07-25T10:26:34+00:00", + "BlobType": 2, + "LeaseStatus": 2, + "LeaseState": 1, + "LeaseDuration": 0, + "PageBlobSequenceNumber": null, + "AppendBlobCommittedBlockCount": null, + "IsServerEncrypted": true, + "EncryptionScope": null, + "IsIncrementalCopy": false, + "StandardBlobTier": 0, + "RehydrationStatus": null, + "PremiumPageBlobTier": null, + "BlobTierInferred": false, + "BlobTierLastModifiedTime": null, + "DeletedTime": null, + "RemainingDaysBeforePermanentDelete": null + }, + "Metadata": {}, + "name": "\"hello.txt\"", + "sys": { + "MethodName": "blob", + "UtcNow": "2022-07-25T10:26:43.0352723Z", + "RandGuid": "30d01e72-94e7-4d4f-906a-2ce51939bfcc" + } + } +} diff --git a/ballerina/dispatcher_service.bal b/ballerina/dispatcher_service.bal index f764cf25..04df6e6f 100644 --- a/ballerina/dispatcher_service.bal +++ b/ballerina/dispatcher_service.bal @@ -32,9 +32,9 @@ isolated service class DispatcherService { http:Response response = new; json message = check request.getJsonPayload(); io:println(message.toJsonString()); - map body = >check message.Data; + //map body = >check message.Data; - map callRegisterMethod = check self.adaptor.callRemoteFunction(body, self.remoteMethodName); + map callRegisterMethod = check self.adaptor.callRemoteFunction(>message, self.remoteMethodName); json result = {Outputs: callRegisterMethod.toJson(), Logs: []}; result = check result.mergeJson({ReturnValue: null}); io:println(result); diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java index 89ae3294..dc5bb2a2 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -45,7 +45,7 @@ public class HandlerTest { public static final Path RESOURCE_DIRECTORY = Paths.get("src/test/resources/handlers/"); - + private JsonParser jsonParser = new JsonParser(); private Map generatedFunctions = new HashMap<>(); @@ -73,7 +73,7 @@ public void compileSample() { DiagnosticResult diagnosticResult = compilation.diagnosticResult(); Assert.assertFalse(diagnosticResult.hasErrors()); - Assert.assertEquals(generatedFunctions.size(), 14); + Assert.assertEquals(generatedFunctions.size(), 16); } @Test @@ -133,4 +133,22 @@ public void testCosmosTrigger() { JsonElement parse = jsonParser.parse(str); Assert.assertEquals(actual, parse); } + + @Test + public void testTimerTrigger() { + JsonObject actual = generatedFunctions.get("timer"); + String str = "{\"bindings\":[{\"type\":\"timerTrigger\",\"schedule\":\"*/10 * * * * *\"," + + "\"runOnStartup\":true,\"direction\":\"in\",\"name\":\"inMsg\"},{\"type\":\"queue\",\"connection\":\"AzureWebJobsStorage\",\"queueName\":\"queue3\",\"direction\":\"out\",\"name\":\"outMsg\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + + @Test + public void testBlobTrigger() { + JsonObject actual = generatedFunctions.get("blob"); + String str = + "{\"bindings\":[{\"type\":\"blobTrigger\",\"name\":\"blobIn\",\"direction\":\"in\",\"path\":\"bpath1/{name}\",\"dataType\":\"binary\",\"connection\":\"AzureWebJobsStorage\"},{\"type\":\"blob\",\"direction\":\"out\",\"name\":\"outMsg\",\"path\":\"bpath1/newBlob\",\"connection\":\"AzureWebJobsStorage\",\"dataType\":\"binary\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } } diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index daf1e6ff..754af451 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -85,3 +85,23 @@ service "cosmos" on cosmosEp { return "helloo "+ id; } } + +@af:TimerTrigger { schedule: "*/10 * * * * *" } +listener af:TimerListener timerListener = new af:TimerListener(); +service "timer" on timerListener { + remote function onTrigger (@af:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg.IsPastDue.toString(); + } +} + +@af:BlobTrigger { + path: "bpath1/{name}" +} +listener af:BlobListener blobListener = new af:BlobListener(); + +service "blob" on blobListener { + remote function onUpdated (@af:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { + path: "bpath1/newBlob" } byte[]|error { + return blobIn; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java index 223b726e..73aaefeb 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java @@ -20,7 +20,7 @@ public class BlobOutputBinding extends OutputBinding { private String path; private String connection = "AzureWebJobsStorage"; - private String dataType = "string"; + private String dataType = "binary"; public BlobOutputBinding(AnnotationNode annotationNode) { super("blob"); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index b47c6e2d..155b920b 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -31,6 +31,7 @@ public interface Constants { String COSMOS_DBOUTPUT = "CosmosDBOutput"; String OUT_MSG = "outMsg"; String HTTP_OUTPUT = "HTTPOutput"; + String BLOB_OUTPUT = "BlobOutput"; String STATUS = "status"; String CODE = "code"; String STATUS_CODE = "statusCode"; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index db768689..1fbe53ab 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -28,10 +28,12 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; 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.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; +import org.ballerinalang.langlib.array.ToBase64; import java.util.ArrayList; import java.util.List; @@ -73,12 +75,19 @@ public void notifySuccess(Object result) { } BMap mapValue = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - if (Constants.QUEUE_OUTPUT.equals(this.annotations.get(0)) || - Constants.COSMOS_DBOUTPUT.equals(this.annotations.get(0))) { + String outputBinding = this.annotations.get(0); + if (Constants.QUEUE_OUTPUT.equals(outputBinding) || + Constants.COSMOS_DBOUTPUT.equals(outputBinding)) { mapValue.put(StringUtils.fromString(Constants.OUT_MSG), result); // Check HTTPOutput with annotations - } else if (Constants.HTTP_OUTPUT.equals(this.annotations.get(0))) { + } else if (Constants.BLOB_OUTPUT.equals(outputBinding)) { + if (result instanceof BArray) { + BArray arrayValue = (BArray) result; + BString encodedString = ToBase64.toBase64(arrayValue); + mapValue.put(StringUtils.fromString("outMsg"), encodedString); + } + }else if (Constants.HTTP_OUTPUT.equals(outputBinding)) { //Check HTTPResponse if (isHTTPResponse(result)) { BMap resultMap = (BMap) result; @@ -166,8 +175,8 @@ private boolean isHTTPResponse(Object result) { return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))) && Constants.PACKAGE_ORG.equals(resultPkg.getOrg()) && Constants.PACKAGE_NAME.equals(resultPkg.getName()); - //TODO : Check inheritance - //(https://github.com/ballerina-platform/module-ballerinax-azure.functions/issues/490) + //TODO : Check inheritance + //(https://github.com/ballerina-platform/module-ballerinax-azure.functions/issues/490) } private boolean isContentTypeExist(BMap headersMap) { diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index e1606cf5..519212f8 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -68,53 +68,7 @@ public static Object callNativeMethod(Environment env, BObject adaptor, BMap bod return invokeResourceFunction(env, bHubService, "callNativeMethod", body, functionName); } - - public static Object callRemoteFunction(Environment env, BObject adaptor, BMap body, BString remoteFuncName) { - BObject bHubService = (BObject) adaptor.getNativeData(SERVICE_OBJECT); - return invokeRemoteFunction(env, bHubService, "callOnMessage", body, remoteFuncName); - } - - private static Object invokeRemoteFunction(Environment env, BObject bHubService, String parentFunctionName, - BMap body, BString remoteFuncName) { - Future balFuture = env.markAsync(); - Module module = ModuleUtils.getModule(); - StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), - parentFunctionName); - ServiceType serviceType = (ServiceType) bHubService.getType(); - Object[] args = new Object[2]; - RemoteMethodType methodType = getOnMessageMethod(serviceType, remoteFuncName).orElseThrow(); //TODO handle error - Parameter parameter = methodType.getParameters()[0]; //TODO handle other params and payload type - String name = parameter.name; - BString bStr = body.getStringValue(StringUtils.fromString(name)); - JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(parameter.type); - Object bValue = jsonPayloadBuilder.getValue(bStr, false); -// Object bValue = Utilities.convertJsonToDataBoundParamValue(bStr, parameter.type); - args[0] = bValue; - args[1] = true; - BMap annotation = (BMap) methodType.getAnnotation(StringUtils.fromString("$returns$")); - if (serviceType.isIsolated()) { - env.getRuntime().invokeMethodAsyncConcurrently( - bHubService, remoteFuncName.getValue(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, - args); - } else { - env.getRuntime().invokeMethodAsyncSequentially( - bHubService, remoteFuncName.getValue(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, - args); - } - return null; - } - - private static Optional getOnMessageMethod(ServiceType serviceType, BString remoteFuncName) { - RemoteMethodType[] remoteMethods = serviceType.getRemoteMethods(); - for (RemoteMethodType methodType : remoteMethods) { - if (methodType.getName().equals(remoteFuncName.getValue())) { - return Optional.of(methodType); - } - } - return Optional.empty(); - } + private static Object invokeResourceFunction(Environment env, BObject bHubService, String parentFunctionName, BMap body, BString functionName) { diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index 47b52ddc..4ea10fb9 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -32,6 +32,7 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.builder.BinaryPayloadBuilder; import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; import java.util.ArrayList; @@ -56,6 +57,7 @@ public static Object callRemoteFunction(Environment env, BObject adaptor, BMap body, BString remoteFuncName) { + BMap data = body.getMapValue(StringUtils.fromString("Data")); Future balFuture = env.markAsync(); Module module = ModuleUtils.getModule(); StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), @@ -69,19 +71,34 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, Object annotation = methodType.getAnnotation(StringUtils.fromString("$param$." + name)); //TODO check and process Payload variable if (ParamHandler.isPayloadAnnotationParam(annotation)) { - Object bValue = getDataboundValue(body, parameter, serviceType); + Object bValue = getDataboundValue(data, parameter, serviceType); argList.add(bValue); argList.add(true); + continue; } + + if (ParamHandler.isBindingNameParam(annotation)) { + BString nameParam = + body.getMapValue(StringUtils.fromString("Metadata")).getStringValue(StringUtils.fromString( + "name")); + Type type = parameter.type; + JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(type); + Object bValue = jsonPayloadBuilder.getValue(nameParam, false); + argList.add(bValue); + argList.add(true); + continue; + } + //TODO check and process input binding variable if (ParamHandler.isInputAnnotationParam(annotation)) { - BString bodyValue = body.getStringValue(StringUtils.fromString(name)); + BString bodyValue = data.getStringValue(StringUtils.fromString(name)); Type type = parameter.type; JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(type); Object bValue = jsonPayloadBuilder.getValue(bodyValue, false); argList.add(bValue); argList.add(true); + continue; } } Object[] args = argList.toArray(); @@ -108,6 +125,10 @@ private static Object getDataboundValue(BMap body, Parameter parameter, Se String name = typeId.getName(); if (name.equals("TimerService")) { return body.getMapValue(paramBString); + } else if (name.equals("BlobService")) { + BString bStr = body.getStringValue(paramBString); + BinaryPayloadBuilder binaryPayloadBuilder = new BinaryPayloadBuilder(parameter.type); + return binaryPayloadBuilder.getValue(bStr, false); } } BString bStr = body.getStringValue(paramBString); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 7a3b43f9..3e076026 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -56,4 +56,16 @@ public static boolean isInputAnnotationParam(Object annotation) { return false; } + public static boolean isBindingNameParam(Object annotation) { + if (annotation == null) { + return false; + } + if (!(annotation instanceof BMap)) { + return false; + } + + Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:BindingName")); + return value != null; + } + } diff --git a/publish.sh b/publish.sh new file mode 100755 index 00000000..f7a59e26 --- /dev/null +++ b/publish.sh @@ -0,0 +1,6 @@ +./gradlew clean build -x check -x test +rm -rf ~/.ballerina/repositories/local/ +cd ballerina +#bal test +/home/anjana/repos/module-ballerinax-azure.functions/target/ballerina-runtime/bin/bal pack +/home/anjana/repos/module-ballerinax-azure.functions/target/ballerina-runtime/bin/bal push --repository local diff --git a/spec/spec.md b/spec/spec.md index 638d3835..57767a3a 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -39,7 +39,7 @@ service "hello" on ep { } ``` -Inline listener declaration //TODO impl compiler ext +Inline listener declaration ```ballerina import ballerinax/azure_functions as af; @@ -78,7 +78,7 @@ defaulted to `/` when not defined. If the base path contains any special charact as string literals ```ballerina -service hello\-world on new af:HTTPListener() { //TODO compiler ext +service hello\-world on new af:HTTPListener() { resource function get foo() { } From fbb5d21f0b1bbc81525ecba962e6b753d9472515 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 1 Aug 2022 21:38:02 +0530 Subject: [PATCH 08/59] Add azure local dev env implementation --- .../tests/resources/local-res-path.json | 99 +++++++++++++++++++ ballerina/code.bal | 2 +- .../AzureCodeGeneratedTask.java | 3 + .../AzureFunctionNameGenerator.java | 66 +++++++++++++ .../ballerinax/azurefunctions/Constants.java | 2 + .../azurefunctions/FunctionsArtifact.java | 1 + .../org/ballerinax/azurefunctions/Util.java | 86 ++++++++++++++++ .../stdlib/azure/functions/HttpResource.java | 1 + .../functions/NativeHttpToAzureAdaptor.java | 3 - 9 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 ballerina-tests/tests/resources/local-res-path.json create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java diff --git a/ballerina-tests/tests/resources/local-res-path.json b/ballerina-tests/tests/resources/local-res-path.json new file mode 100644 index 00000000..9ed41218 --- /dev/null +++ b/ballerina-tests/tests/resources/local-res-path.json @@ -0,0 +1,99 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/foo", + "Method": "GET", + "Query": {}, + "Headers": { + "Cache-Control": [ + "max-age=0" + ], + "Connection": [ + "keep-alive" + ], + "Accept": [ + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Accept-Language": [ + "en-US,en;q=0.9" + ], + "Cookie": [ + "_ga=GA1.1.720190479.1641539348; _hjSessionUser_865786=eyJpZCI6IjhjNjRiYjkwLTA0NzYtNTc5MS1hZWNiLTRjN2VhZTM4OWM5NCIsImNyZWF0ZWQiOjE2NDE1MzkzNDg4NTAsImV4aXN0aW5nIjp0cnVlfQ==; Idea-2474b141=7a691445-10e4-435d-b306-2b36a77b1d2e; Webstorm-fe5cf5e6=379eafe5-e706-45c1-92e8-6e424ab655f0; cookie_accepted=1" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" + ], + "Upgrade-Insecure-Requests": [ + "1" + ], + "sec-ch-ua": [ + "\".Not\/A)Brand\";v=\"99\", \"Google Chrome\";v=\"103\", \"Chromium\";v=\"103\"" + ], + "sec-ch-ua-mobile": [ + "?0" + ], + "sec-ch-ua-platform": [ + "\"Linux\"" + ], + "Sec-Fetch-Site": [ + "none" + ], + "Sec-Fetch-Mode": [ + "navigate" + ], + "Sec-Fetch-User": [ + "?1" + ], + "Sec-Fetch-Dest": [ + "document" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Cache-Control": "max-age=0", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "en-US,en;q=0.9", + "Cookie": "_ga=GA1.1.720190479.1641539348; _hjSessionUser_865786=eyJpZCI6IjhjNjRiYjkwLTA0NzYtNTc5MS1hZWNiLTRjN2VhZTM4OWM5NCIsImNyZWF0ZWQiOjE2NDE1MzkzNDg4NTAsImV4aXN0aW5nIjp0cnVlfQ==; Idea-2474b141=7a691445-10e4-435d-b306-2b36a77b1d2e; Webstorm-fe5cf5e6=379eafe5-e706-45c1-92e8-6e424ab655f0; cookie_accepted=1", + "Host": "localhost:7071", + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", + "Upgrade-Insecure-Requests": "1", + "sec-ch-ua": "\".Not\/A)Brand\";v=\"99\", \"Google Chrome\";v=\"103\", \"Chromium\";v=\"103\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Linux\"", + "Sec-Fetch-Site": "none", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-User": "?1", + "Sec-Fetch-Dest": "document" + }, + "sys": { + "MethodName": "get-hello-foo", + "UtcNow": "2022-08-01T06:13:05.6335269Z", + "RandGuid": "eba6cb26-507a-40b1-a800-781964722f00" + } + } +} diff --git a/ballerina/code.bal b/ballerina/code.bal index 6db9213c..900e97a1 100644 --- a/ballerina/code.bal +++ b/ballerina/code.bal @@ -57,7 +57,7 @@ type HttpPayload record { map Headers; map Params; Identity[] Identities; - anydata? Body; + anydata? Body?; }; type Identity record { diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java index 447213d2..2ddaba71 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java @@ -70,6 +70,9 @@ public void perform(CompilerLifecycleEventContext compilerLifecycleEventContext) OUT.println(msg); throw new RuntimeException(msg, e); } + OUT.println("\n\tExecute the below command to deploy the function locally:"); + OUT.println( + "\tfunc start --script-root target/bin/azf-local --java"); OUT.println("\n\tExecute the below command to deploy Ballerina Azure Functions:"); Path parent = path.getParent(); if (parent != null) { diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java new file mode 100644 index 00000000..60c1e88e --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -0,0 +1,66 @@ +package org.ballerinax.azurefunctions; + +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.ballerinax.azurefunctions.Util.resourcePathToString; + +public class AzureFunctionNameGenerator { + private ServiceDeclarationNode serviceDeclarationNode; + private List functionNames = new ArrayList<>(); + + public AzureFunctionNameGenerator(ServiceDeclarationNode serviceDeclarationNode) { + this.serviceDeclarationNode = serviceDeclarationNode; + NodeList members = serviceDeclarationNode.members(); + String servicePath = resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + for (Node node : members) { + if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + continue; + } + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; + String method = functionDefinitionNode.functionName().text(); + StringBuilder resourcePath = new StringBuilder(); + resourcePath.append(servicePath); + for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { + if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { + resourcePath.append("/").append(((IdentifierToken) pathBlock).text()); + continue; + } + } + String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + this.functionNames.add(functionName); + } + } + + + + public String getUniqueFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { + int index = 0; + String method = functionDefinitionNode.functionName().text(); + StringBuilder resourcePath = new StringBuilder(); + resourcePath.append(servicePath); + for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { + if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { + resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); + continue; + } + if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { + resourcePath.append("/" + index); +// resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); + continue; + } + } + String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + } + +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index 12faa849..d8f6d9a3 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -53,4 +53,6 @@ public class Constants { public static final String DIRECTION_IN = "in"; public static final String DIRECTION_OUT = "out"; + + public static final String AF_LOCAL_DIR = "azf-local"; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index ba7e0268..7673691c 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -146,6 +146,7 @@ public void generate(String outputFileName) throws IOException { StandardCopyOption.REPLACE_EXISTING); } } + Util.unzipFolder(parent.resolve(outputFileName), parent.resolve(Constants.AF_LOCAL_DIR)); } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index eca62c22..39c2d707 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -15,7 +15,17 @@ import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextRange; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.util.Optional; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; /** * Contains the utilities required for the compiler extension. @@ -65,4 +75,80 @@ public static String resourcePathToString(NodeList nodes) { } return out.substring(1, out.toString().length() - 1); } + + public static void unzipFolder(Path source, Path target) throws IOException { + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile()))) { + + // list files in zip + ZipEntry zipEntry = zis.getNextEntry(); + + while (zipEntry != null) { + + boolean isDirectory = false; + // example 1.1 + // some zip stored files and folders separately + // e.g data/ + // data/folder/ + // data/folder/file.txt + if (zipEntry.getName().endsWith(File.separator)) { + isDirectory = true; + } + + Path newPath = zipSlipProtect(zipEntry, target); + + if (isDirectory) { + Files.createDirectories(newPath); + } else { + + // example 1.2 + // some zip stored file path only, need create parent directories + // e.g data/folder/file.txt + if (newPath.getParent() != null) { + if (Files.notExists(newPath.getParent())) { + Files.createDirectories(newPath.getParent()); + } + } + + // copy files, nio + Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING); + + // copy files, classic + /*try (FileOutputStream fos = new FileOutputStream(newPath.toFile())) { + byte[] buffer = new byte[1024]; + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + }*/ + } + + zipEntry = zis.getNextEntry(); + + } + zis.closeEntry(); + + } + + } + + // protect zip slip attack + public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) + throws IOException { + + // test zip slip vulnerability + // Path targetDirResolved = targetDir.resolve("../../" + zipEntry.getName()); + + Path targetDirResolved = targetDir.resolve(zipEntry.getName()); + + // make sure normalized file still has targetDir as its prefix + // else throws exception + Path normalizePath = targetDirResolved.normalize(); + if (!normalizePath.startsWith(targetDir)) { + throw new IOException("Bad zip entry: " + zipEntry.getName()); + } + + return normalizePath; + } + } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index 17e45e89..a0de0dbf 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -150,6 +150,7 @@ private Optional processPayloadParam(ResourceMethodType resour .getMapValue(StringUtils.fromString("Headers")); BString contentType = headers.getArrayValue(StringUtils.fromString("Content-Type")).getBString(0); BString bodyValue = httpPayload.getStringValue(StringUtils.fromString("Body")); + //TODO handle payload 400 Type type = parameter.type; AbstractPayloadBuilder builder = AbstractPayloadBuilder.getBuilder(contentType.getValue(), type); Object bValue = builder.getValue(bodyValue, false); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index 519212f8..ce7c777d 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -24,8 +24,6 @@ import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.async.StrandMetadata; import io.ballerina.runtime.api.creators.ValueCreator; -import io.ballerina.runtime.api.types.Parameter; -import io.ballerina.runtime.api.types.RemoteMethodType; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.ServiceType; import io.ballerina.runtime.api.utils.StringUtils; @@ -33,7 +31,6 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; -import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; import java.util.ArrayList; import java.util.List; From 20177c8708343c5d8160f41988635ab84de11cf1 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Wed, 3 Aug 2022 09:00:39 +0530 Subject: [PATCH 09/59] Improve function name generation --- .../resources/res-path-conflict-param.json | 2 +- .../tests/resources/res-path-param.json | 2 +- ballerina-tests/tests/test.bal | 17 ++++- .../azurefunctions/test/HandlerTest.java | 21 ++++- .../azurefunctions/AzureFunctionModifier.java | 42 +++++----- .../AzureFunctionNameGenerator.java | 57 ++++++++++---- .../azurefunctions/UniqueIDHolder.java | 15 ---- .../org/ballerinax/azurefunctions/Util.java | 44 ++--------- .../stdlib/azure/functions/AZFParameter.java | 17 +++++ .../stdlib/azure/functions/Constants.java | 2 +- .../azure/functions/FunctionCallback.java | 2 +- .../stdlib/azure/functions/HttpResource.java | 17 +++++ .../functions/InputBindingParameter.java | 17 +++++ .../stdlib/azure/functions/ModuleUtils.java | 2 +- .../stdlib/azure/functions/ParamHandler.java | 27 ++++++- .../stdlib/azure/functions/PathParameter.java | 17 +++++ .../azure/functions/PayloadParameter.java | 17 +++++ .../azure/functions/QueryParameter.java | 18 +++++ .../stdlib/azure/functions/Utilities.java | 76 ------------------- 19 files changed, 233 insertions(+), 179 deletions(-) delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java delete mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java diff --git a/ballerina-tests/tests/resources/res-path-conflict-param.json b/ballerina-tests/tests/resources/res-path-conflict-param.json index 99d48ef7..615a94b3 100644 --- a/ballerina-tests/tests/resources/res-path-conflict-param.json +++ b/ballerina-tests/tests/resources/res-path-conflict-param.json @@ -103,7 +103,7 @@ "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { - "MethodName": "post-hello-foo-0", + "MethodName": "post-hello-foo-bar-1", "UtcNow": "2022-06-10T08:23:28.1520104Z", "RandGuid": "6d6d581c-3dce-4907-8638-c1f18a6e0d64" } diff --git a/ballerina-tests/tests/resources/res-path-param.json b/ballerina-tests/tests/resources/res-path-param.json index b6f69e77..230ac700 100644 --- a/ballerina-tests/tests/resources/res-path-param.json +++ b/ballerina-tests/tests/resources/res-path-param.json @@ -100,7 +100,7 @@ "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { - "MethodName": "post-hello-foo-bar", + "MethodName": "post-hello-foo-bar-2", "UtcNow": "2022-06-10T07:57:46.8453883Z", "RandGuid": "0f4737ff-5b49-4e1d-9639-e51aaca81bc6" } diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index f66a3620..9259493f 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -101,16 +101,27 @@ function testSimpleResourcePath() returns error? { test:assertEquals(resp, expectedResp); } -@test:Config {} +@test:Config { } function testSimpleMultiResourcePath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/res-path-param.json"; json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-foo-bar", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello from foo bar res"}}, "Logs": [], "ReturnValue": null}; + json resp = check clientEndpoint->post("/post-hello-foo-bar-2", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo bar res"}},"Logs":[],"ReturnValue":null}; test:assertEquals(resp, expectedResp); } +@test:Config { } +function testSimpleConflictingPathParam() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/res-path-conflict-param.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-foo-bar-1", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo param meow"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + + @test:Config {} function testSimpleMultiQueryPath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java index dc5bb2a2..ecab40e3 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -100,7 +100,7 @@ public void testHttpHelloFoo() { @Test public void testHttpHelloFooParam() { - JsonObject actual = generatedFunctions.get("post-hello-foo-0"); + JsonObject actual = generatedFunctions.get("post-hello-foo-bar-1"); String str = "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello/foo/{bar}\"}," + @@ -109,6 +109,17 @@ public void testHttpHelloFooParam() { Assert.assertEquals(actual, parse); } + @Test + public void testHttpHelloFooConflictPathParam() { + JsonObject actual = generatedFunctions.get("post-hello-foo-bar-2"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello/foo/bar\"},{\"type\":" + + "\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + @Test public void testQueueTrigger() { JsonObject actual = generatedFunctions.get("queue"); @@ -138,7 +149,8 @@ public void testCosmosTrigger() { public void testTimerTrigger() { JsonObject actual = generatedFunctions.get("timer"); String str = "{\"bindings\":[{\"type\":\"timerTrigger\",\"schedule\":\"*/10 * * * * *\"," + - "\"runOnStartup\":true,\"direction\":\"in\",\"name\":\"inMsg\"},{\"type\":\"queue\",\"connection\":\"AzureWebJobsStorage\",\"queueName\":\"queue3\",\"direction\":\"out\",\"name\":\"outMsg\"}]}"; + "\"runOnStartup\":true,\"direction\":\"in\",\"name\":\"inMsg\"},{\"type\":\"queue\",\"connection\":" + + "\"AzureWebJobsStorage\",\"queueName\":\"queue3\",\"direction\":\"out\",\"name\":\"outMsg\"}]}"; JsonElement parse = jsonParser.parse(str); Assert.assertEquals(actual, parse); } @@ -147,7 +159,10 @@ public void testTimerTrigger() { public void testBlobTrigger() { JsonObject actual = generatedFunctions.get("blob"); String str = - "{\"bindings\":[{\"type\":\"blobTrigger\",\"name\":\"blobIn\",\"direction\":\"in\",\"path\":\"bpath1/{name}\",\"dataType\":\"binary\",\"connection\":\"AzureWebJobsStorage\"},{\"type\":\"blob\",\"direction\":\"out\",\"name\":\"outMsg\",\"path\":\"bpath1/newBlob\",\"connection\":\"AzureWebJobsStorage\",\"dataType\":\"binary\"}]}"; + "{\"bindings\":[{\"type\":\"blobTrigger\",\"name\":\"blobIn\",\"direction\":\"in\",\"path\":" + + "\"bpath1/{name}\",\"dataType\":\"binary\",\"connection\":\"AzureWebJobsStorage\"}," + + "{\"type\":\"blob\",\"direction\":\"out\",\"name\":\"outMsg\",\"path\":\"bpath1/newBlob\"," + + "\"connection\":\"AzureWebJobsStorage\",\"dataType\":\"binary\"}]}"; JsonElement parse = jsonParser.parse(str); Assert.assertEquals(actual, parse); } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index 7b03eeec..f923e75d 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -10,7 +10,6 @@ import io.ballerina.compiler.syntax.tree.BasicLiteralNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; -import io.ballerina.compiler.syntax.tree.IdentifierToken; import io.ballerina.compiler.syntax.tree.LiteralValueToken; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.MappingFieldNode; @@ -35,13 +34,11 @@ public class AzureFunctionModifier extends TreeModifier { private SemanticModel semanticModel; - private UniqueIDHolder holder; private String modulePrefix; public AzureFunctionModifier(SemanticModel semanticModel) { super(); this.semanticModel = semanticModel; - this.holder = new UniqueIDHolder(); this.modulePrefix = "af"; //TODO fixme } @@ -69,9 +66,10 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio if (!name.get().equals("HTTPListener")) { return super.transform(serviceDeclarationNode); } + AzureFunctionNameGenerator nameGen = new AzureFunctionNameGenerator(serviceDeclarationNode); NodeList newMembersList = NodeFactory.createNodeList(); for (Node node : members) { - Optional modifiedMember = getModifiedMember(node, servicePath); + Optional modifiedMember = getModifiedMember(node, servicePath, nameGen); if (modifiedMember.isEmpty()) { newMembersList = newMembersList.add(node); } else { @@ -82,26 +80,27 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio .withMembers(newMembersList).apply(); } - public Optional getModifiedMember(Node node, String servicePath) { + public Optional getModifiedMember(Node node, String servicePath, AzureFunctionNameGenerator nameGen) { if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { return Optional.empty(); } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; - String method = functionDefinitionNode.functionName().text(); - StringBuilder resourcePath = new StringBuilder(); - resourcePath.append(servicePath); - for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { - if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { - resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); - continue; - } - if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { - resourcePath.append("/" + holder.getNextValue()); -// resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); - continue; - } - } - String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + String uniqueFunctionName = nameGen.getUniqueFunctionName(servicePath, functionDefinitionNode); +// String method = functionDefinitionNode.functionName().text(); +// StringBuilder resourcePath = new StringBuilder(); +// resourcePath.append(servicePath); +// for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { +// if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { +// resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); +// continue; +// } +// if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { +// resourcePath.append("/" + holder.getNextValue()); +//// resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); +// continue; +// } +// } +// String functionName = method + "-" + resourcePath.toString().replace("/", "-"); Optional metadata = functionDefinitionNode.metadata(); NodeList existingAnnotations = NodeFactory.createNodeList(); MetadataNode metadataNode; @@ -114,7 +113,8 @@ public Optional getModifiedMember(Node node, String servicePath) { //Create and add annotation - NodeList modifiedAnnotations = existingAnnotations.add(getFunctionNameAnnotation(functionName)); + NodeList modifiedAnnotations = + existingAnnotations.add(getFunctionNameAnnotation(uniqueFunctionName)); MetadataNode modifiedMetadata = new MetadataNode.MetadataNodeModifier(metadataNode).withAnnotations(modifiedAnnotations).apply(); FunctionDefinitionNode updatedFunctionNode = diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index 60c1e88e..f6029e76 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -4,23 +4,27 @@ import io.ballerina.compiler.syntax.tree.IdentifierToken; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ResourcePathParameterNode; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.Token; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Optional; import static org.ballerinax.azurefunctions.Util.resourcePathToString; +/** + * Responsible for generating Azure function name for each resource function. + * + * @since 2.0.0 + */ public class AzureFunctionNameGenerator { - private ServiceDeclarationNode serviceDeclarationNode; + private List functionNames = new ArrayList<>(); + private List generatedNames = new ArrayList<>(); public AzureFunctionNameGenerator(ServiceDeclarationNode serviceDeclarationNode) { - this.serviceDeclarationNode = serviceDeclarationNode; NodeList members = serviceDeclarationNode.members(); String servicePath = resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); for (Node node : members) { @@ -34,33 +38,60 @@ public AzureFunctionNameGenerator(ServiceDeclarationNode serviceDeclarationNode) for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { resourcePath.append("/").append(((IdentifierToken) pathBlock).text()); - continue; + } else if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { + Token token = ((ResourcePathParameterNode) pathBlock).paramName(); + resourcePath.append("/").append(token.text()); } } String functionName = method + "-" + resourcePath.toString().replace("/", "-"); this.functionNames.add(functionName); } } - - - + public String getUniqueFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { - int index = 0; String method = functionDefinitionNode.functionName().text(); StringBuilder resourcePath = new StringBuilder(); resourcePath.append(servicePath); for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { - resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); + resourcePath.append("/").append(((IdentifierToken) pathBlock).text()); continue; } if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { - resourcePath.append("/" + index); + Token token = ((ResourcePathParameterNode) pathBlock).paramName(); + resourcePath.append("/").append(token.text()); + // resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); continue; } } String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + functionName = generateUniqueName(functionName, 0); + generatedNames.add(functionName); + return functionName; + } + + private String generateUniqueName(String initialName, int index) { + String newName; + if (index == 0) { + newName = initialName; + } else { + newName = initialName + "-" + index; + } + + if (!isDuplicateName(newName)) { + return newName; + } + return generateUniqueName(initialName, index + 1); + } + + private boolean isDuplicateName(String initialName) { + int count = 0; + for (String functionName : this.functionNames) { + if (functionName.equals(initialName)) { + count++; + } + } + return generatedNames.contains(initialName) || count > 1; } - } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java deleted file mode 100644 index 6cd8da17..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/UniqueIDHolder.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.ballerinax.azurefunctions; - -/** - * Responsible for generating unique identifier for params. - * - * @since 2.0.0 - */ -public class UniqueIDHolder { - - private int currentIndex = 0; - - public int getNextValue() { - return this.currentIndex++; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index 39c2d707..15eddc36 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -17,8 +17,6 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -79,18 +77,10 @@ public static String resourcePathToString(NodeList nodes) { public static void unzipFolder(Path source, Path target) throws IOException { try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile()))) { - - // list files in zip ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) { - boolean isDirectory = false; - // example 1.1 - // some zip stored files and folders separately - // e.g data/ - // data/folder/ - // data/folder/file.txt if (zipEntry.getName().endsWith(File.separator)) { isDirectory = true; } @@ -100,49 +90,25 @@ public static void unzipFolder(Path source, Path target) throws IOException { if (isDirectory) { Files.createDirectories(newPath); } else { - - // example 1.2 - // some zip stored file path only, need create parent directories - // e.g data/folder/file.txt - if (newPath.getParent() != null) { - if (Files.notExists(newPath.getParent())) { - Files.createDirectories(newPath.getParent()); + Path newPathParent = newPath.getParent(); + if (newPathParent != null) { + if (Files.notExists(newPathParent)) { + Files.createDirectories(newPathParent); } } - - // copy files, nio Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING); - - // copy files, classic - /*try (FileOutputStream fos = new FileOutputStream(newPath.toFile())) { - byte[] buffer = new byte[1024]; - int len; - while ((len = zis.read(buffer)) > 0) { - fos.write(buffer, 0, len); - } - }*/ } - zipEntry = zis.getNextEntry(); - } zis.closeEntry(); - } - } // protect zip slip attack public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) throws IOException { - - // test zip slip vulnerability - // Path targetDirResolved = targetDir.resolve("../../" + zipEntry.getName()); - Path targetDirResolved = targetDir.resolve(zipEntry.getName()); - - // make sure normalized file still has targetDir as its prefix - // else throws exception + Path normalizePath = targetDirResolved.normalize(); if (!normalizePath.startsWith(targetDir)) { throw new IOException("Bad zip entry: " + zipEntry.getName()); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java index c5be7eec..b7b0d553 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/AZFParameter.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.types.Parameter; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index 155b920b..0ce7f900 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 1fbe53ab..c06209ee 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index a0de0dbf..e772eb91 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.types.Parameter; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java index 20220e3c..38eb713e 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/InputBindingParameter.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.types.Parameter; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java index 1fcc0b37..99db0300 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ModuleUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 3e076026..285b3a1b 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.utils.StringUtils; @@ -10,7 +27,8 @@ enum InputBindings { COSMOS("CosmosDBInput"); private String annotation; - InputBindings (String annotation) { + + InputBindings(String annotation) { this.annotation = annotation; } @@ -21,7 +39,7 @@ public String getAnnotation() { /** * Represents the input binding handler. - * + * * @since 2.0.0 */ public class ParamHandler { @@ -37,7 +55,7 @@ public static boolean isPayloadAnnotationParam(Object annotation) { Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:Payload")); return value instanceof Boolean; } - + public static boolean isInputAnnotationParam(Object annotation) { if (annotation == null) { return false; @@ -64,7 +82,8 @@ public static boolean isBindingNameParam(Object annotation) { return false; } - Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:BindingName")); + Object value = + ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:BindingName")); return value != null; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java index 5b531a2a..3674b8ea 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.types.Parameter; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java index 903d5c99..738d559c 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/PayloadParameter.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.types.Parameter; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java index 8825757f..ddf558c3 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/QueryParameter.java @@ -1,3 +1,21 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; import io.ballerina.runtime.api.types.Parameter; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java deleted file mode 100644 index 141f2a10..00000000 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Utilities.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.ballerina.stdlib.azure.functions; - -import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.utils.JsonUtils; -import io.ballerina.runtime.api.values.BError; -import io.ballerina.runtime.api.values.BString; -import org.ballerinalang.langlib.value.CloneWithType; -import org.ballerinalang.langlib.value.FromJsonString; - -import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; -import static io.ballerina.runtime.api.TypeTags.JSON_TAG; -import static io.ballerina.runtime.api.TypeTags.RECORD_TYPE_TAG; -import static io.ballerina.runtime.api.TypeTags.STRING_TAG; -/** - * Contains the utilities required for azure functions runtime. - * - * @since 2.0.0 - */ -public class Utilities { - -// public static final String ENTITY = "Entity"; - -// public static BObject createEntityObject() { -// return createObjectValue(MimeUtil.getMimePackage(), ENTITY); -// } - -// private static BObject createObjectValue(Module module, String objectTypeName, -// Object... fieldValues) { -// -// Object[] fields = new Object[fieldValues.length * 2]; -// -// // Adding boolean values for each arg -// for (int i = 0, j = 0; i < fieldValues.length; i++) { -// fields[j++] = fieldValues[i]; -// fields[j++] = true; -// } -// -// // passing scheduler, strand and properties as null for the moment, but better to expose them via this method -// return ValueCreator.createObjectValue(module, objectTypeName, null, null, null, fields); -// } - - public static Object convertJsonToDataBoundParamValue(BString bodyValue, Type type) { - Object bValue = bodyValue; - if (type.getTag() == JSON_TAG) { - bValue = FromJsonString.fromJsonString((BString) JsonUtils.parse(bodyValue)); - } else if (type.getTag() == RECORD_TYPE_TAG) { - Object jsonValue = FromJsonString.fromJsonString((BString) JsonUtils.parse(bodyValue)); - bValue = CloneWithType.convert(type, jsonValue); - } else if (type.getTag() == ARRAY_TAG) { - bValue = CloneWithType.convert(type, JsonUtils.parse(bodyValue)); - } else if (type.getTag() == STRING_TAG) { - try { - bValue = CloneWithType.convert(type, JsonUtils.parse(bodyValue)); - } catch (BError error) { - return bValue; - } - } -// switch (type.getTag()) { -// case TypeTags.STRING_TAG: -// bValue = StringUtils.fromString(stringAggregator.getAggregateString()); -// break; -// case TypeTags.XML_TAG: -// bValue = XmlUtils.parse(stringAggregator.getAggregateString()); -// break; -// case TypeTags.RECORD_TYPE_TAG: -// bValue = CloneWithType.convert(targetType, JsonUtils.parse( -// stringAggregator.getAggregateString())); -// break; -// default: -// bValue = FromJsonStringWithType.fromJsonStringWithType(bodyValue), -// ValueCreator.createTypedescValue(targetType)); -// break; -// } - return bValue; - } -} From 90e01300444e2c24068692440b6ffb6698f2dc19 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Wed, 3 Aug 2022 22:15:23 +0530 Subject: [PATCH 10/59] Add http service route literal support --- .../src/test/resources/handlers/main.bal | 2 +- .../azurefunctions/AzureFunctionModifier.java | 13 +------- .../AzureFunctionNameGenerator.java | 32 ++++++------------- .../org/ballerinax/azurefunctions/Util.java | 17 ++++++++-- 4 files changed, 25 insertions(+), 39 deletions(-) diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index 754af451..faa5a539 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -12,7 +12,7 @@ type Person record { }; // @af:HTTPTest -service "hello" on ep { +service /hello on ep { resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from . path "; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index f923e75d..42db10db 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -44,7 +44,7 @@ public AzureFunctionModifier(SemanticModel semanticModel) { @Override public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclarationNode) { - String servicePath = resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); ExpressionNode listenerExpressionNode = serviceDeclarationNode.expressions().get(0); Optional typeSymbol = semanticModel.typeOf(listenerExpressionNode); if (typeSymbol.isEmpty()) { @@ -144,15 +144,4 @@ public AnnotationNode getFunctionNameAnnotation(String functionName) { return NodeFactory.createAnnotationNode(NodeFactory.createToken(SyntaxKind.AT_TOKEN), azureFunctionAnnotRef, annotationValue); } - - public String resourcePathToString(NodeList nodes) { - StringBuilder out = new StringBuilder(); - for (Node node : nodes) { - if (node.kind() == SyntaxKind.STRING_LITERAL) { - BasicLiteralNode basicLiteralNode = (BasicLiteralNode) node; - out.append(basicLiteralNode.literalToken().text()); - } - } - return out.substring(1, out.toString().length() - 1); - } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index f6029e76..4a99d396 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -12,8 +12,6 @@ import java.util.ArrayList; import java.util.List; -import static org.ballerinax.azurefunctions.Util.resourcePathToString; - /** * Responsible for generating Azure function name for each resource function. * @@ -26,46 +24,34 @@ public class AzureFunctionNameGenerator { public AzureFunctionNameGenerator(ServiceDeclarationNode serviceDeclarationNode) { NodeList members = serviceDeclarationNode.members(); - String servicePath = resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); for (Node node : members) { if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { continue; } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; - String method = functionDefinitionNode.functionName().text(); - StringBuilder resourcePath = new StringBuilder(); - resourcePath.append(servicePath); - for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { - if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { - resourcePath.append("/").append(((IdentifierToken) pathBlock).text()); - } else if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { - Token token = ((ResourcePathParameterNode) pathBlock).paramName(); - resourcePath.append("/").append(token.text()); - } - } - String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + String functionName = getFunctionName(servicePath, functionDefinitionNode); this.functionNames.add(functionName); } } - public String getUniqueFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { + private String getFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { String method = functionDefinitionNode.functionName().text(); StringBuilder resourcePath = new StringBuilder(); resourcePath.append(servicePath); for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { resourcePath.append("/").append(((IdentifierToken) pathBlock).text()); - continue; - } - if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { + } else if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { Token token = ((ResourcePathParameterNode) pathBlock).paramName(); resourcePath.append("/").append(token.text()); - -// resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); - continue; } } - String functionName = method + "-" + resourcePath.toString().replace("/", "-"); + return method + "-" + resourcePath.toString().replace("/", "-"); + } + + public String getUniqueFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { + String functionName = getFunctionName(servicePath, functionDefinitionNode); functionName = generateUniqueName(functionName, 0); generatedNames.add(functionName); return functionName; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index 15eddc36..4f56ef4b 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -3,6 +3,7 @@ import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.syntax.tree.BasicLiteralNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; import io.ballerina.compiler.syntax.tree.ModulePartNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; @@ -11,6 +12,7 @@ import io.ballerina.compiler.syntax.tree.SpecificFieldNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.compiler.syntax.tree.Token; import io.ballerina.tools.text.LineRange; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextRange; @@ -67,11 +69,20 @@ public static String resourcePathToString(NodeList nodes) { StringBuilder out = new StringBuilder(); for (Node node : nodes) { if (node.kind() == SyntaxKind.STRING_LITERAL) { - BasicLiteralNode basicLiteralNode = (BasicLiteralNode) node; - out.append(basicLiteralNode.literalToken().text()); + String value = ((BasicLiteralNode) node).literalToken().text(); + out.append(value, 1, value.length() - 1); + } else if (node.kind() == SyntaxKind.SLASH_TOKEN) { + Token token = (Token) node; + out.append(token.text()); + } else if (node.kind() == SyntaxKind.IDENTIFIER_TOKEN) { + out.append(((IdentifierToken) node).text()); } } - return out.substring(1, out.toString().length() - 1); + String finalPath = out.toString(); + if (finalPath.startsWith("/")) { + return finalPath.substring(1); + } + return finalPath; } public static void unzipFolder(Path source, Path target) throws IOException { From b5df20c0d05cce076c7f530fedf3efd59ffe4660 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Thu, 4 Aug 2022 08:41:17 +0530 Subject: [PATCH 11/59] Add default accessor support --- ballerina-tests/main.bal | 7 +- ballerina-tests/tests/resources/default.json | 107 ++++++++++++++++++ ballerina-tests/tests/test.bal | 10 ++ .../azurefunctions/test/HandlerTest.java | 12 +- .../src/test/resources/handlers/main.bal | 4 + .../azurefunctions/AzureFunctionModifier.java | 16 --- .../service/http/HTTPTriggerBinding.java | 24 +++- .../azure/functions/FunctionCallback.java | 2 +- publish.sh | 6 - 9 files changed, 157 insertions(+), 31 deletions(-) create mode 100644 ballerina-tests/tests/resources/default.json delete mode 100755 publish.sh diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index ef3f01bc..83b16215 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -11,8 +11,11 @@ type Person record { int age; }; -// @af:HTTPTest -service "hello" on ep { +service /hello on ep { + resource function default all() returns @af:HTTPOutput string { + return "Hello from all"; + } + resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from . path "; } diff --git a/ballerina-tests/tests/resources/default.json b/ballerina-tests/tests/resources/default.json new file mode 100644 index 00000000..db53e9f3 --- /dev/null +++ b/ballerina-tests/tests/resources/default.json @@ -0,0 +1,107 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/all", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/all" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/all" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/all", + "X-WAWS-Unencoded-URL": "/api/hello/all" + }, + "sys": { + "MethodName": "default-hello-all", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 9259493f..5ad46b9d 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -2,6 +2,16 @@ import ballerina/test; import ballerina/io; import ballerina/http; +@test:Config { } +function testDefault() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/default.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/default-hello-all", readJson); + json expectedResp = {"Outputs":{"resp":{"body":"Hello from all"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function testBaseDot() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java index ecab40e3..a5bbf03e 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -73,7 +73,7 @@ public void compileSample() { DiagnosticResult diagnosticResult = compilation.diagnosticResult(); Assert.assertFalse(diagnosticResult.hasErrors()); - Assert.assertEquals(generatedFunctions.size(), 16); + Assert.assertEquals(generatedFunctions.size(), 17); } @Test @@ -87,6 +87,16 @@ public void testHttpHello() { Assert.assertEquals(httpHello, parse); } + @Test + public void testHttpDefault() { + JsonObject httpHello = generatedFunctions.get("default-hello-all"); + String str = "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"DELETE\"," + + "\"GET\",\"HEAD\",\"OPTIONS\",\"POST\",\"PUT\"],\"direction\":\"in\",\"name\":\"httpPayload\"," + + "\"route\":\"hello/all\"},{\"type\":\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(httpHello, parse); + } + @Test public void testHttpHelloFoo() { JsonObject actual = generatedFunctions.get("post-hello-foo"); diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index faa5a539..43d5ea0e 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -13,6 +13,10 @@ type Person record { // @af:HTTPTest service /hello on ep { + resource function default all() returns @af:HTTPOutput string { + return "Hello from all "; + } + resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from . path "; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index 42db10db..f97dd930 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -86,21 +86,6 @@ public Optional getModifiedMember(Node node, String servicePath, AzureFunc } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; String uniqueFunctionName = nameGen.getUniqueFunctionName(servicePath, functionDefinitionNode); -// String method = functionDefinitionNode.functionName().text(); -// StringBuilder resourcePath = new StringBuilder(); -// resourcePath.append(servicePath); -// for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { -// if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { -// resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); -// continue; -// } -// if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { -// resourcePath.append("/" + holder.getNextValue()); -//// resourcePath.append("{").append(pathParamNode.paramName().text()).append("}"); -// continue; -// } -// } -// String functionName = method + "-" + resourcePath.toString().replace("/", "-"); Optional metadata = functionDefinitionNode.metadata(); NodeList existingAnnotations = NodeFactory.createNodeList(); MetadataNode metadataNode; @@ -112,7 +97,6 @@ public Optional getModifiedMember(Node node, String servicePath, AzureFunc } //Create and add annotation - NodeList modifiedAnnotations = existingAnnotations.add(getFunctionNameAnnotation(uniqueFunctionName)); MetadataNode modifiedMetadata = diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index a70a70ca..35aa8ba7 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -36,10 +36,11 @@ /** * Represents a HTTP Trigger binding in functions.json. - * + * * @since 2.0.0 */ public class HTTPTriggerBinding extends TriggerBinding { + private String path; private String authLevel = "anonymous"; private String methods; @@ -110,7 +111,7 @@ public List getBindings() { ReturnTypeDescriptorNode returnTypeDescriptorNode = functionDefinitionNode.functionSignature().returnTypeDesc().get(); //TODO recheck if return is must OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); - Optional returnBinding = outputBuilder.getOutputBinding(returnTypeDescriptorNode.annotations()); + Optional returnBinding = outputBuilder.getOutputBinding(returnTypeDescriptorNode.annotations()); if (returnBinding.isEmpty()) { bindings.add(new HTTPOutputBinding(null)); } else { @@ -221,12 +222,25 @@ public JsonObject getJsonObject() { JsonObject inputTrigger = new JsonObject(); inputTrigger.addProperty("type", this.getTriggerType()); inputTrigger.addProperty("authLevel", this.authLevel); - JsonArray methods = new JsonArray(); - methods.add(this.methods); //TODO add default - inputTrigger.add("methods", methods); + inputTrigger.add("methods", generateMethods()); inputTrigger.addProperty("direction", this.getDirection()); inputTrigger.addProperty("name", this.getVarName()); inputTrigger.addProperty("route", this.getPath()); return inputTrigger; } + + private JsonArray generateMethods() { + JsonArray methods = new JsonArray(); + if (this.methods.equals("default")) { + methods.add("DELETE"); + methods.add("GET"); + methods.add("HEAD"); + methods.add("OPTIONS"); + methods.add("POST"); + methods.add("PUT"); + } else { + methods.add(this.methods); //TODO add default + } + return methods; + } } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index c06209ee..4c3a9779 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -87,7 +87,7 @@ public void notifySuccess(Object result) { BString encodedString = ToBase64.toBase64(arrayValue); mapValue.put(StringUtils.fromString("outMsg"), encodedString); } - }else if (Constants.HTTP_OUTPUT.equals(outputBinding)) { + } else if (Constants.HTTP_OUTPUT.equals(outputBinding)) { //Check HTTPResponse if (isHTTPResponse(result)) { BMap resultMap = (BMap) result; diff --git a/publish.sh b/publish.sh deleted file mode 100755 index f7a59e26..00000000 --- a/publish.sh +++ /dev/null @@ -1,6 +0,0 @@ -./gradlew clean build -x check -x test -rm -rf ~/.ballerina/repositories/local/ -cd ballerina -#bal test -/home/anjana/repos/module-ballerinax-azure.functions/target/ballerina-runtime/bin/bal pack -/home/anjana/repos/module-ballerinax-azure.functions/target/ballerina-runtime/bin/bal push --repository local From d6881f8bc6e0ecfc7ab385f00b36805133a0948d Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 9 Aug 2022 16:55:00 +0530 Subject: [PATCH 12/59] Implement HTTP Return implicit status codes and content types --- ballerina-tests/main.bal | 15 +- .../tests/resources/nonHttpResTest1.json | 108 +++++++++ .../tests/resources/nonReturnTest1.json | 108 +++++++++ ballerina-tests/tests/test.bal | 83 +++++-- .../stdlib/azure/functions/Constants.java | 1 + .../azure/functions/FunctionCallback.java | 228 +++++++++++++----- .../functions/NativeHttpToAzureAdaptor.java | 11 +- .../azure/functions/NativeRemoteAdapter.java | 8 +- 8 files changed, 464 insertions(+), 98 deletions(-) create mode 100644 ballerina-tests/tests/resources/nonHttpResTest1.json create mode 100644 ballerina-tests/tests/resources/nonReturnTest1.json diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 83b16215..5500c34a 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -15,7 +15,7 @@ service /hello on ep { resource function default all() returns @af:HTTPOutput string { return "Hello from all"; } - + resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from . path "; } @@ -49,6 +49,14 @@ service /hello on ep { return err; } + resource function post nonHttpResTest1(@af:Payload string greeting) returns string { + string s1 = "alpha"; + return s1; + } + resource function post nonReturnTest1(@af:Payload string greeting) { + + } + resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from foo path " + greeting; } @@ -151,8 +159,9 @@ service "queue-input" on queueListener1 { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated (@af:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { - path: "bpath1/newBlob" } byte[]|error { + remote function onUpdated(@af:Payload byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { + path: "bpath1/newBlob" + } byte[]|error { return blobIn; } } diff --git a/ballerina-tests/tests/resources/nonHttpResTest1.json b/ballerina-tests/tests/resources/nonHttpResTest1.json new file mode 100644 index 00000000..c359bef7 --- /dev/null +++ b/ballerina-tests/tests/resources/nonHttpResTest1.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/nonHttpResTest1", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/nonHttpResTest1" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/nonHttpResTest1" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/nonHttpResTest1", + "X-WAWS-Unencoded-URL": "/api/hello/nonHttpResTest1" + }, + "sys": { + "MethodName": "post-hello-nonHttpResTest1", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/nonReturnTest1.json b/ballerina-tests/tests/resources/nonReturnTest1.json new file mode 100644 index 00000000..ceb6786b --- /dev/null +++ b/ballerina-tests/tests/resources/nonReturnTest1.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/nonReturnTest1", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/nonReturnTest1" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/nonReturnTest1" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/nonReturnTest1", + "X-WAWS-Unencoded-URL": "/api/hello/nonReturnTest1" + }, + "sys": { + "MethodName": "post-hello-nonReturnTest1", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 5ad46b9d..0d6f0a7b 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -2,14 +2,14 @@ import ballerina/test; import ballerina/io; import ballerina/http; -@test:Config { } +@test:Config {} function testDefault() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/default.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/default-hello-all", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from all"}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/default.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/default-hello-all", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); } @test:Config {} @@ -18,7 +18,7 @@ function testBaseDot() returns error? { string jsonFilePath = "./tests/resources/base-dot.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello from . path "}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from . path "}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -53,7 +53,7 @@ function httpResTest2() returns error? { "resp": { "statusCode": "200", "body": "Helloworld.....", - "headers": {"content-type": "text/plain"} + "headers": {"content-type": "application/json"} } }, "Logs": [], @@ -92,6 +92,26 @@ function httpResTest4() returns error? { "Outputs": { "resp": { "statusCode": "500", + "headers": {"content-type": "application/json"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest1() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest1.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest1", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "201", + "body": "alpha", "headers": {"content-type": "text/plain"} } }, @@ -101,44 +121,61 @@ function httpResTest4() returns error? { test:assertEquals(resp, expectedResp); } +@test:Config {} +function nonReturnTest1() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonReturnTest1.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest1", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "202" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function testSimpleResourcePath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/res-path.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello from foo path Jack"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from foo path Jack"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testSimpleMultiResourcePath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/res-path-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo-bar-2", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo bar res"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from foo bar res"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testSimpleConflictingPathParam() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/res-path-conflict-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo-bar-1", readJson); - json expectedResp = {"Outputs":{"resp":{"body":"Hello from foo param meow"}},"Logs":[],"ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from foo param meow"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } - @test:Config {} function testSimpleMultiQueryPath() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/query-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-query", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello from the query Jack test1"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from the query Jack test1"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -158,7 +195,7 @@ function testCosmosInputArr() returns error? { string jsonFilePath = "./tests/resources/cosmos-db-arr.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-db", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello Jackhello1"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello Jackhello1"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -168,7 +205,7 @@ function testJsonJsonPayload() returns error? { string jsonFilePath = "./tests/resources/payload-json-json.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-jsonToJson", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello from json to json Anjana"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from json to json Anjana"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -178,7 +215,7 @@ function testJsonRecordPayload() returns error? { string jsonFilePath = "./tests/resources/payload-json-record.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-jsonToRecord", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "Hello from json to record Anjana"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from json to record Anjana"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -189,7 +226,7 @@ function testXmlPayload() returns error? { json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-xmlToXml", readJson); string xmlPayload = "\"\\n Anjana<\\/name>\\n 12<\\/age>\\n<\\/root>\""; - json expectedResp = {"Outputs": {"resp": {"body": xmlPayload}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": xmlPayload}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -199,7 +236,7 @@ function testTextStringPayload() returns error? { string jsonFilePath = "./tests/resources/payload-text-string.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-textToString", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -209,7 +246,7 @@ function testTextBytePayload() returns error? { string jsonFilePath = "./tests/resources/payload-text-byte.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-textToByte", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -219,7 +256,7 @@ function testOctaBytePayload() returns error? { string jsonFilePath = "./tests/resources/payload-octa-byte.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-octaToByte", readJson); - json expectedResp = {"Outputs": {"resp": {"body": "hello from byte arr\n"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "hello from byte arr\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index 0ce7f900..225f55f0 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -32,6 +32,7 @@ public interface Constants { String OUT_MSG = "outMsg"; String HTTP_OUTPUT = "HTTPOutput"; String BLOB_OUTPUT = "BlobOutput"; + String PAYLOAD_ANNOTATAION = "Payload"; String STATUS = "status"; String CODE = "code"; String STATUS_CODE = "statusCode"; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 4c3a9779..9668a803 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -25,14 +25,21 @@ 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.MethodType; +import io.ballerina.runtime.api.types.ResourceMethodType; +import io.ballerina.runtime.api.types.TableType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BDecimal; 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.BString; +import io.ballerina.runtime.api.values.BTable; +import io.ballerina.runtime.api.values.BXmlItem; import org.ballerinalang.langlib.array.ToBase64; import java.util.ArrayList; @@ -51,11 +58,14 @@ public class FunctionCallback implements Callback { private final Module module; private final List annotations; + private final MethodType methodType; - public FunctionCallback(Future future, Module module, Object[] annotations) { + + public FunctionCallback(Future future, Module module, Object[] annotations, MethodType methodType) { this.future = future; this.module = module; this.annotations = new ArrayList<>(); + this.methodType = methodType; for (Object o : annotations) { BString annotation = (BString) o; String[] split = annotation.getValue().split(":"); @@ -73,86 +83,46 @@ public void notifySuccess(Object result) { future.complete(result); return; } + BMap mapValue = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - String outputBinding = this.annotations.get(0); - if (Constants.QUEUE_OUTPUT.equals(outputBinding) || - Constants.COSMOS_DBOUTPUT.equals(outputBinding)) { + if (result == null) { + BString statusCode = StringUtils.fromString("202"); + BMap respMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); + mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + future.complete(mapValue); + return; + + } + String outputBinding = ""; + if (annotations.size() != 0) { + outputBinding = this.annotations.get(0); + } + + if (Constants.QUEUE_OUTPUT.equals(outputBinding) || Constants.COSMOS_DBOUTPUT.equals(outputBinding)) { mapValue.put(StringUtils.fromString(Constants.OUT_MSG), result); - // Check HTTPOutput with annotations + } else if (Constants.BLOB_OUTPUT.equals(outputBinding)) { if (result instanceof BArray) { BArray arrayValue = (BArray) result; BString encodedString = ToBase64.toBase64(arrayValue); mapValue.put(StringUtils.fromString("outMsg"), encodedString); } - } else if (Constants.HTTP_OUTPUT.equals(outputBinding)) { - //Check HTTPResponse - if (isHTTPResponse(result)) { - BMap resultMap = (BMap) result; - - // Extract status code - BObject status = (BObject) (resultMap.get(StringUtils.fromString(Constants.STATUS))); - Object statusCode = Long.toString(status.getIntValue(StringUtils.fromString(Constants.CODE))); - statusCode = StringUtils.fromString((String) statusCode); - - // Create a BMap for response field - BMap respMap = - ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); - - // Create body field in the response Map - if (resultMap.containsKey(StringUtils.fromString(Constants.BODY))) { - Object body = resultMap.get(StringUtils.fromString(Constants.BODY)); - respMap.put(StringUtils.fromString(Constants.BODY), body); - } - - // Create header field in the response Map - if (resultMap.containsKey(StringUtils.fromString(Constants.HEADERS))) { - Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); - BMap headersMap = (BMap) headers; - // Add Content-type field in headers if there is not - if (!isContentTypeExist(headersMap)) { - headersMap.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("text/plain")); - } - respMap.put(StringUtils.fromString(Constants.HEADERS), headers); - } else { - // If there is no headers add one with default content-type - Object headers = - ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("text/plain")); - respMap.put(StringUtils.fromString(Constants.HEADERS), headers); - - } - - // If there is mediaType replace content-type in headers - if (resultMap.containsKey(StringUtils.fromString(Constants.MEDIA_TYPE))) { - Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); - Object mediaType = resultMap.get(StringUtils.fromString(Constants.MEDIA_TYPE)); - ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), mediaType); - } - mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + } else if ("".equals(outputBinding) || Constants.HTTP_OUTPUT.equals(outputBinding)) { + if (isHTTPResponse(result)) { + handleHTTPResponse((BMap) result, mapValue); } else { - //Handle result except HTTPResponse cases - BMap respMap = - ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - respMap.put(StringUtils.fromString(Constants.BODY), result); - mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + handleNonHTTPResponse(result, mapValue); } - } else { - // Handle other output bindings - BMap respMap = - ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - respMap.put(StringUtils.fromString(Constants.BODY), result); - mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } future.complete(mapValue); } + @Override public void notifyFailure(BError bError) { bError.printStackTrace(); @@ -187,4 +157,134 @@ private boolean isContentTypeExist(BMap headersMap) { } return false; } + + private void addStatusCodeImplicitly(BMap respMap) { + String accessor = ((ResourceMethodType) this.methodType).getAccessor(); + Object statusCode = ""; + if ("post".equals(accessor)) { + statusCode = "201"; + } else if ("get".equals(accessor) || "put".equals(accessor) || "patch".equals(accessor) || + "delete".equals(accessor) || "head".equals(accessor) || "options".equals(accessor) || + "default".equals(accessor)) { + statusCode = "200"; + } + statusCode = StringUtils.fromString((String) statusCode); + respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); + } + + private void addContentTypeImplicitly(Object result, BMap headers) { + if (result instanceof BString) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("text/plain")); + + } else if (result instanceof BXmlItem) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("application/xml")); + + } else if (result instanceof BArray) { + BArray arrayResult = (BArray) result; + if ("byte".equals(arrayResult.getElementType().getName())) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString("application/octet-stream")); + + } else if ("map".equals(arrayResult.getElementType().getName())) { + MapType mapContent = (MapType) arrayResult.getElementType(); + if ("json".equals(mapContent.getConstrainedType().getName())) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString("application/json")); + } + + } else if ("table".equals(arrayResult.getElementType().getName())) { + TableType tableContent = (TableType) arrayResult.getElementType(); + if ("map".equals(tableContent.getConstrainedType().getName())) { + MapType mapContent = (MapType) tableContent.getConstrainedType(); + if ("json".equals(mapContent.getConstrainedType().getName())) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString("application/json")); + } + + } + } + + } else if (result instanceof BTable) { + BTable tableResult = (BTable) result; + TableType tableContent = (TableType) tableResult.getType(); + if ("map".equals(tableContent.getConstrainedType().getName())) { + MapType mapContent = (MapType) tableContent.getConstrainedType(); + if ("json".equals(mapContent.getConstrainedType().getName())) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString("application/json")); + } + + } + } else if (result instanceof BDecimal || result instanceof Long || result instanceof Double || + result instanceof Boolean) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("application/json")); + } else if (isHTTPResponse(result)) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("application/json")); + } + } + + + private void handleNonHTTPResponse(Object result, BMap mapValue) { + BMap respMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + Object headers = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + + addStatusCodeImplicitly(respMap); + addContentTypeImplicitly(result, (BMap) headers); + + respMap.put(StringUtils.fromString(Constants.HEADERS), headers); + respMap.put(StringUtils.fromString(Constants.BODY), result); + mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + } + + private void handleHTTPResponse(BMap result, BMap mapValue) { + BMap resultMap = result; + + // Extract status code + BObject status = (BObject) (resultMap.get(StringUtils.fromString(Constants.STATUS))); + Object statusCode = Long.toString(status.getIntValue(StringUtils.fromString(Constants.CODE))); + statusCode = StringUtils.fromString((String) statusCode); + + // Create a BMap for response field + BMap respMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); + + // Create body field in the response Map + if (resultMap.containsKey(StringUtils.fromString(Constants.BODY))) { + Object body = resultMap.get(StringUtils.fromString(Constants.BODY)); + respMap.put(StringUtils.fromString(Constants.BODY), body); + } + + // Create header field in the response Map + if (resultMap.containsKey(StringUtils.fromString(Constants.HEADERS))) { + Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); + BMap headersMap = (BMap) headers; + // Add Content-type field in headers if there is not + if (!isContentTypeExist(headersMap)) { + headersMap.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString("application/json")); + } + respMap.put(StringUtils.fromString(Constants.HEADERS), headers); + } else { + // If there is no headers add one with default content-type + Object headers = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString("application/json")); + respMap.put(StringUtils.fromString(Constants.HEADERS), headers); + + } + + // If there is mediaType replace content-type in headers + if (resultMap.containsKey(StringUtils.fromString(Constants.MEDIA_TYPE))) { + Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); + Object mediaType = resultMap.get(StringUtils.fromString(Constants.MEDIA_TYPE)); + ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), mediaType); + } + mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + } } + + diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index ce7c777d..b93e7e77 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -82,16 +82,19 @@ private static Object invokeResourceFunction(Environment env, BObject bHubServic Object[] args = httpResource.getArgList(); // Object[] args = new Object[1]; BMap annotation = (BMap) resourceMethod.getAnnotation(StringUtils.fromString("$returns$")); + if (annotation == null) { + annotation = ValueCreator.createMapValue(); + } if (serviceType.isIsolated() && resourceMethod.isIsolated()) { env.getRuntime().invokeMethodAsyncConcurrently( bHubService, resourceMethod.getName(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, - args); + new FunctionCallback(balFuture, module, annotation.getKeys(), resourceMethod), null, + PredefinedTypes.TYPE_NULL, args); } else { env.getRuntime().invokeMethodAsyncSequentially( bHubService, resourceMethod.getName(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, - args); + new FunctionCallback(balFuture, module, annotation.getKeys(), resourceMethod), null, + PredefinedTypes.TYPE_NULL, args); } return null; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index 4ea10fb9..d098591a 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -106,13 +106,13 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, if (serviceType.isIsolated()) { env.getRuntime().invokeMethodAsyncConcurrently( bHubService, remoteFuncName.getValue(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, - args); + new FunctionCallback(balFuture, module, annotation.getKeys(), methodType), null, + PredefinedTypes.TYPE_NULL, args); } else { env.getRuntime().invokeMethodAsyncSequentially( bHubService, remoteFuncName.getValue(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys()), null, PredefinedTypes.TYPE_NULL, - args); + new FunctionCallback(balFuture, module, annotation.getKeys(), methodType), null, + PredefinedTypes.TYPE_NULL, args); } return null; } From 5be607fde44536adf636329560164981c951bcb1 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Wed, 10 Aug 2022 16:25:42 +0530 Subject: [PATCH 13/59] Modify based on review --- ballerina-tests/main.bal | 100 +++++++- .../tests/resources/httpAccessorTest.json | 107 ++++++++ ...nHttpResTest1.json => nonHttpResTest.json} | 12 +- ballerina-tests/tests/test.bal | 238 +++++++++++++++++- .../stdlib/azure/functions/Constants.java | 18 ++ .../azure/functions/FunctionCallback.java | 55 ++-- 6 files changed, 492 insertions(+), 38 deletions(-) create mode 100644 ballerina-tests/tests/resources/httpAccessorTest.json rename ballerina-tests/tests/resources/{nonHttpResTest1.json => nonHttpResTest.json} (92%) diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 5500c34a..719b6967 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -19,6 +19,31 @@ service /hello on ep { resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { return "Hello from . path "; } + + resource function get httpAccessorTest() returns @af:HTTPOutput string { + return "Hello from all"; + } + + resource function put httpAccessorTest() returns @af:HTTPOutput string { + return "Hello from all"; + } + + resource function patch httpAccessorTest() returns @af:HTTPOutput string { + return "Hello from all"; + } + + resource function delete httpAccessorTest() returns @af:HTTPOutput string { + return "Hello from all"; + } + + resource function head httpAccessorTest() returns @af:HTTPOutput string { + return "Hello from all"; + } + + resource function options httpAccessorTest() returns @af:HTTPOutput string { + return "Hello from all"; + } + resource function post httpResTest1(@af:Payload string greeting) returns @af:HTTPOutput af:Unauthorized { af:Unauthorized unauth = { body: "Helloworld.....", @@ -34,6 +59,7 @@ service /hello on ep { af:Ok ok = {body: "Helloworld....."}; return ok; } + resource function post httpResTest3(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { af:InternalServerError err = { body: "Helloworld.....", @@ -44,16 +70,86 @@ service /hello on ep { }; return err; } + resource function post httpResTest4(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { af:InternalServerError err = {}; return err; } - resource function post nonHttpResTest1(@af:Payload string greeting) returns string { + resource function post httpResTest5() returns af:StatusCodeResponse { + af:InternalServerError err = {}; + return err; + } + + resource function post nonHttpResTest1() returns string { string s1 = "alpha"; return s1; } - resource function post nonReturnTest1(@af:Payload string greeting) { + + resource function post nonHttpResTest2() returns xml { + xml x1 = xml `The Lost World`; + return x1; + } + + resource function post nonHttpResTest3() returns byte[] { + byte[] b1 = base64 `yPHaytRgJPg+QjjylUHakEwz1fWPx/wXCW41JSmqYW8=`; + return b1; + + } + + resource function post nonHttpResTest4() returns int { + int i1 = 100; + return i1; + } + + resource function post nonHttpResTest6() returns decimal { + decimal d1 = 100; + return d1; + } + + resource function post nonHttpResTest7() returns boolean { + boolean bo1 = true; + return bo1; + } + + resource function post nonHttpResTest8() returns map { + map mj1 = {"a": {"b": 12, "c": "helloworld"}}; + return mj1; + + } + + resource function post nonHttpResTest9() returns table> { + + table> t = table [ + {"a": {"b": 12, "c": "helloworld"}}, + {"b": 1100} + ]; + + return t; + } + + resource function post nonHttpResTest10() returns map[] { + + map[] mjarr1 = [{"a": {"b": 12, "c": "helloworld"}}, {"b": 12}]; + + return mjarr1; + } + + resource function post nonHttpResTest11() returns table>[] { + table>[] tarr = [ + table [ + {"a": {"b": 12, "c": "helloworld"}}, + {"b": 12} + ], + table [ + {"a": {"b": 14, "c": "helloworld"}}, + {"b": 100} + ] + ]; + return tarr; + } + + resource function post nonReturnTest1() { } diff --git a/ballerina-tests/tests/resources/httpAccessorTest.json b/ballerina-tests/tests/resources/httpAccessorTest.json new file mode 100644 index 00000000..490023fd --- /dev/null +++ b/ballerina-tests/tests/resources/httpAccessorTest.json @@ -0,0 +1,107 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/httpAccessorTest", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/httpAccessorTest" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/httpAccessorTest" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/httpAccessorTest", + "X-WAWS-Unencoded-URL": "/api/hello/httpAccessorTest" + }, + "sys": { + "MethodName": "ACCESSOR_NAME-hello-httpAccessorTest", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/nonHttpResTest1.json b/ballerina-tests/tests/resources/nonHttpResTest.json similarity index 92% rename from ballerina-tests/tests/resources/nonHttpResTest1.json rename to ballerina-tests/tests/resources/nonHttpResTest.json index c359bef7..ce90028b 100644 --- a/ballerina-tests/tests/resources/nonHttpResTest1.json +++ b/ballerina-tests/tests/resources/nonHttpResTest.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/nonHttpResTest1", + "Url": "https://bal-dev.azurewebsites.net/api/hello/FUNC_NAME", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/nonHttpResTest1" + "/api/hello/FUNC_NAME" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/nonHttpResTest1" + "/api/hello/FUNC_NAME" ] }, "Params": {}, @@ -96,11 +96,11 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/nonHttpResTest1", - "X-WAWS-Unencoded-URL": "/api/hello/nonHttpResTest1" + "X-Original-URL": "/api/hello/FUNC_NAME", + "X-WAWS-Unencoded-URL": "/api/hello/FUNC_NAME" }, "sys": { - "MethodName": "post-hello-nonHttpResTest1", + "MethodName": "post-hello-FUNC_NAME", "UtcNow": "2022-06-10T07:10:30.1722785Z", "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" } diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 0d6f0a7b..b74c39cc 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -1,6 +1,9 @@ -import ballerina/test; -import ballerina/io; import ballerina/http; +import ballerina/io; +import ballerina/lang.value; +import ballerina/regex; +import ballerina/test; + @test:Config {} function testDefault() returns error? { @@ -12,6 +15,78 @@ function testDefault() returns error? { test:assertEquals(resp, expectedResp); } +@test:Config {} +function getHttpAccessorTest() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpAccessorTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","get"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/get-hello-httpAccessorTest", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function putHttpAccessorTest() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpAccessorTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","put"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/put-hello-httpAccessorTest", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function patchHttpAccessorTest() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpAccessorTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","patch"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/patch-hello-httpAccessorTest", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function deleteHttpAccessorTest() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpAccessorTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","delete"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/delete-hello-httpAccessorTest", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function headHttpAccessorTest() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpAccessorTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","head"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/head-hello-httpAccessorTest", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function optionsHttpAccessorTest() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpAccessorTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","options"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/options-hello-httpAccessorTest", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function testBaseDot() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); @@ -104,8 +179,10 @@ function httpResTest4() returns error? { @test:Config {} function nonHttpResTest1() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/nonHttpResTest1.json"; - json readJson = check io:fileReadJson(jsonFilePath); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest1"); + json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest1", readJson); json expectedResp = { "Outputs": { @@ -121,6 +198,154 @@ function nonHttpResTest1() returns error? { test:assertEquals(resp, expectedResp); } +@test:Config {} +function nonHttpResTest2() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest2"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest2", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/xml"}, "body":"The Lost World"}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest3() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest3"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest3", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/octet-stream"}, "body":[200, 241, 218, 202, 212, 96, 36, 248, 62, 66, 56, 242, 149, 65, 218, 144, 76, 51, 213, 245, 143, 199, 252, 23, 9, 110, 53, 37, 41, 170, 97, 111]}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest4() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest4"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest4", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest6() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest6"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest6", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest7() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest7"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest7", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/json"}, "body":true}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest8() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest8"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest8", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "201", + "body":{"a":{"b":12, "c":"helloworld"}}, + "headers": {"content-type":"application/json"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest9() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest9"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest9", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "201", + "body":[{"a":{"b":12, "c":"helloworld"}}, {"b":1100}], + "headers": {"content-type":"application/json"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest10() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest10"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest10", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "201", + "body":[{"a":{"b":12, "c":"helloworld"}}, {"b":12}], + "headers":{"content-type":"application/json"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function nonHttpResTest11() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/nonHttpResTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest11"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-nonHttpResTest11", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "201", + "body":[[{"a":{"b":12, "c":"helloworld"}}, {"b":12}], [{"a":{"b":14, "c":"helloworld"}}, {"b":100}]], + "headers": {"content-type":"application/json"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function nonReturnTest1() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); @@ -289,3 +514,8 @@ function testQueueInput() returns error? { json expectedResp = {"Outputs": {"outMsg": "helloo qqeeewwww hello1"}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } + + +function replaceFuncName(string actual) { + +} \ No newline at end of file diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index 225f55f0..cc346b7b 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -41,4 +41,22 @@ public interface Constants { String CONTENT_TYPE = "content-type"; String MEDIA_TYPE = "mediaType"; String RESP = "resp"; + String POST = "post"; + String CREATED_201 = "201"; + String GET = "get"; + String PUT = "put"; + String PATCH = "patch"; + String DELETE = "delete"; + String HEAD = "head"; + String OPTIONS = "options"; + String DEFAULT = "default"; + String OK_200 = "200"; + String TEXT_PLAIN = "text/plain"; + String APPLICATION_XML = "application/xml"; + String APPLICATION_OCTET_STREAM = "application/octet-stream"; + String APPLICATION_JSON = "application/json"; + String BYTE_TYPE = "byte"; + String MAP_TYPE = "map"; + String JSON_TYPE = "json"; + String TABLE_TYPE = "table"; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 9668a803..c1faa5b7 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -94,7 +94,6 @@ public void notifySuccess(Object result) { mapValue.put(StringUtils.fromString(Constants.RESP), respMap); future.complete(mapValue); return; - } String outputBinding = ""; @@ -161,12 +160,13 @@ private boolean isContentTypeExist(BMap headersMap) { private void addStatusCodeImplicitly(BMap respMap) { String accessor = ((ResourceMethodType) this.methodType).getAccessor(); Object statusCode = ""; - if ("post".equals(accessor)) { - statusCode = "201"; - } else if ("get".equals(accessor) || "put".equals(accessor) || "patch".equals(accessor) || - "delete".equals(accessor) || "head".equals(accessor) || "options".equals(accessor) || - "default".equals(accessor)) { - statusCode = "200"; + if (Constants.POST.equals(accessor)) { + statusCode = Constants.CREATED_201; + } else if (Constants.GET.equals(accessor) || Constants.PUT.equals(accessor) || + Constants.PATCH.equals(accessor) || Constants.DELETE.equals(accessor) || + Constants.HEAD.equals(accessor) || Constants.OPTIONS.equals(accessor) || + Constants.DEFAULT.equals(accessor)) { + statusCode = Constants.OK_200; } statusCode = StringUtils.fromString((String) statusCode); respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); @@ -174,31 +174,32 @@ private void addStatusCodeImplicitly(BMap respMap) { private void addContentTypeImplicitly(Object result, BMap headers) { if (result instanceof BString) { - headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("text/plain")); + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString(Constants.TEXT_PLAIN)); } else if (result instanceof BXmlItem) { - headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("application/xml")); + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString(Constants.APPLICATION_XML)); } else if (result instanceof BArray) { BArray arrayResult = (BArray) result; - if ("byte".equals(arrayResult.getElementType().getName())) { + if (Constants.BYTE_TYPE.equals(arrayResult.getElementType().getName())) { headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("application/octet-stream")); + StringUtils.fromString(Constants.APPLICATION_OCTET_STREAM)); - } else if ("map".equals(arrayResult.getElementType().getName())) { + } else if (Constants.MAP_TYPE.equals(arrayResult.getElementType().getName())) { MapType mapContent = (MapType) arrayResult.getElementType(); - if ("json".equals(mapContent.getConstrainedType().getName())) { + if (Constants.JSON_TYPE.equals(mapContent.getConstrainedType().getName())) { headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("application/json")); + StringUtils.fromString(Constants.APPLICATION_JSON)); } - } else if ("table".equals(arrayResult.getElementType().getName())) { + } else if (Constants.TABLE_TYPE.equals(arrayResult.getElementType().getName())) { TableType tableContent = (TableType) arrayResult.getElementType(); - if ("map".equals(tableContent.getConstrainedType().getName())) { + if (Constants.MAP_TYPE.equals(tableContent.getConstrainedType().getName())) { MapType mapContent = (MapType) tableContent.getConstrainedType(); - if ("json".equals(mapContent.getConstrainedType().getName())) { + if (Constants.JSON_TYPE.equals(mapContent.getConstrainedType().getName())) { headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("application/json")); + StringUtils.fromString(Constants.APPLICATION_JSON)); } } @@ -207,19 +208,21 @@ private void addContentTypeImplicitly(Object result, BMap headers) { } else if (result instanceof BTable) { BTable tableResult = (BTable) result; TableType tableContent = (TableType) tableResult.getType(); - if ("map".equals(tableContent.getConstrainedType().getName())) { + if (Constants.MAP_TYPE.equals(tableContent.getConstrainedType().getName())) { MapType mapContent = (MapType) tableContent.getConstrainedType(); - if ("json".equals(mapContent.getConstrainedType().getName())) { + if (Constants.JSON_TYPE.equals(mapContent.getConstrainedType().getName())) { headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("application/json")); + StringUtils.fromString(Constants.APPLICATION_JSON)); } } } else if (result instanceof BDecimal || result instanceof Long || result instanceof Double || result instanceof Boolean) { - headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("application/json")); - } else if (isHTTPResponse(result)) { - headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString("application/json")); + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString(Constants.APPLICATION_JSON)); + } else if (result instanceof BMap) { + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString(Constants.APPLICATION_JSON)); } } @@ -264,7 +267,7 @@ private void handleHTTPResponse(BMap result, BMap mapValue) { // Add Content-type field in headers if there is not if (!isContentTypeExist(headersMap)) { headersMap.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("application/json")); + StringUtils.fromString(Constants.APPLICATION_JSON)); } respMap.put(StringUtils.fromString(Constants.HEADERS), headers); } else { @@ -272,7 +275,7 @@ private void handleHTTPResponse(BMap result, BMap mapValue) { Object headers = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString("application/json")); + StringUtils.fromString(Constants.APPLICATION_JSON)); respMap.put(StringUtils.fromString(Constants.HEADERS), headers); } From b59f63ac104bfd68cdb1b9a19ff808ce48bcdb26 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 8 Aug 2022 16:17:09 +0530 Subject: [PATCH 14/59] Add payload error handling --- README.md | 22 +- ballerina-tests/main.bal | 189 ++++++++++-------- .../resources/error-invalid-payload.json | 116 +++++++++++ .../resources/error-missing-payload.json | 99 +++++++++ .../tests/resources/http-optional-out.json | 108 ++++++++++ .../resources/http-optional-with-payload.json | 108 ++++++++++ .../http-optional-without-payload.json | 107 ++++++++++ ballerina-tests/tests/test.bal | 122 +++++++---- ballerina/Module.md | 20 +- ballerina/annotation.bal | 4 +- ballerina/dispatcher_service.bal | 7 +- ballerina/errors.bal | 7 + ballerina/http_service.bal | 24 ++- .../azurefunctions/test/HandlerTest.java | 13 +- .../test/resources/deployment/functions.bal | 24 +-- .../src/test/resources/handlers/main.bal | 30 +-- .../test/resources/validations/main/main.bal | 2 +- .../validations/single-file/functions.bal | 2 +- .../resources/validations/submodule/main.bal | 2 +- .../submodule/modules/mod1/mod1.bal | 2 +- .../service/OutputBindingBuilder.java | 2 +- .../service/ServiceHandler.java | 2 +- .../stdlib/azure/functions/Constants.java | 74 +++---- .../azure/functions/FunctionCallback.java | 54 ++--- .../stdlib/azure/functions/HttpResource.java | 69 +++++-- .../functions/NativeHttpToAzureAdaptor.java | 43 ++-- .../azure/functions/NativeRemoteAdapter.java | 9 +- .../stdlib/azure/functions/Utils.java | 38 ++++ .../exceptions/BadRequestException.java | 37 ++++ .../exceptions/FunctionNotFoundException.java | 33 +++ .../exceptions/InvalidPayloadException.java | 33 +++ .../exceptions/PayloadNotFoundException.java | 33 +++ spec/spec.md | 4 +- 33 files changed, 1153 insertions(+), 286 deletions(-) create mode 100644 ballerina-tests/tests/resources/error-invalid-payload.json create mode 100644 ballerina-tests/tests/resources/error-missing-payload.json create mode 100644 ballerina-tests/tests/resources/http-optional-out.json create mode 100644 ballerina-tests/tests/resources/http-optional-with-payload.json create mode 100644 ballerina-tests/tests/resources/http-optional-without-payload.json create mode 100644 ballerina/errors.bal create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/BadRequestException.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/FunctionNotFoundException.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/InvalidPayloadException.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/PayloadNotFoundException.java diff --git a/README.md b/README.md index a6b9f32e..f0bc504d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ import ballerinax/azure_functions as af; // HTTP request/response with no authentication @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } @@ -39,7 +39,7 @@ public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string public isolated function fromHttpToQueue(af:Context ctx, @af:HTTPTrigger af:HTTPRequest req, @af:QueueOutput { queueName: "queue1" } af:StringOutputBinding msg) - returns @af:HTTPOutput af:HTTPBinding { + returns @af:HttpOutput af:HTTPBinding { msg.value = req.body; return { statusCode: 200, payload: "Request: " + req.toString() }; } @@ -68,7 +68,7 @@ public isolated function fromBlobToQueue(af:Context ctx, @af:Function public isolated function httpTriggerBlobInput(@af:HTTPTrigger af:HTTPRequest req, @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { int length = 0; if blobIn is byte[] { length = blobIn.length(); @@ -81,7 +81,7 @@ public isolated function httpTriggerBlobInput(@af:HTTPTrigger af:HTTPRequest req @af:Function public isolated function httpTriggerBlobOutput(@af:HTTPTrigger af:HTTPRequest req, @af:BlobOutput { path: "bpath1/{Query.name}" } af:StringOutputBinding bb) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { bb.value = req.body; return "Blob: " + req.query["name"].toString() + " Content: " + bb?.value.toString(); @@ -91,7 +91,7 @@ public isolated function httpTriggerBlobOutput(@af:HTTPTrigger af:HTTPRequest re @af:Function public isolated function httpTriggerBlobOutput2(@af:HTTPTrigger af:HTTPRequest req, @af:BlobOutput { path: "bpath1/{Query.name}" } af:BytesOutputBinding bb) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { bb.value = [65, 66, 67, 97, 98]; return "Blob: " + req.query["name"].toString() + " Content: " + bb?.value.toString(); @@ -102,7 +102,7 @@ public isolated function httpTriggerBlobOutput2(@af:HTTPTrigger af:HTTPRequest r public isolated function sendSMS(@af:HTTPTrigger af:HTTPRequest req, @af:TwilioSmsOutput { fromNumber: "+12069845840" } af:TwilioSmsOutputBinding tb) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { tb.to = req.query["to"].toString(); tb.body = req.body.toString(); return "Message - to: " + tb?.to.toString() + " body: " + tb?.body.toString(); @@ -138,7 +138,7 @@ public isolated function httpTriggerCosmosDBInput1( @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1", id: "{Query.id}", partitionKey: "{Query.country}" } json dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } @@ -148,7 +148,7 @@ public isolated function httpTriggerCosmosDBInput2( @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1", id: "{Query.id}", partitionKey: "{Query.country}" } Person? dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } @@ -159,14 +159,14 @@ public isolated function httpTriggerCosmosDBInput3( databaseName: "db1", collectionName: "c1", sqlQuery: "select * from c1 where c1.country = {country}" } Person[] dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } // HTTP request to write records to CosmosDB @af:Function public isolated function httpTriggerCosmosDBOutput1( - @af:HTTPTrigger af:HTTPRequest httpReq, @af:HTTPOutput af:HTTPBinding hb) + @af:HTTPTrigger af:HTTPRequest httpReq, @af:HttpOutput af:HTTPBinding hb) returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1" } json { json entry = { id: uuid:createType1AsString(), name: "Saman", country: "Sri Lanka" }; @@ -177,7 +177,7 @@ public isolated function httpTriggerCosmosDBOutput1( @af:Function public isolated function httpTriggerCosmosDBOutput2( @af:HTTPTrigger af:HTTPRequest httpReq, - @af:HTTPOutput af:HTTPBinding hb) + @af:HttpOutput af:HTTPBinding hb) returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1" } json { diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 719b6967..7c4b12f0 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -12,39 +12,25 @@ type Person record { }; service /hello on ep { - resource function default all() returns @af:HTTPOutput string { + resource function default all() returns @af:HttpOutput string { return "Hello from all"; } - - resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { - return "Hello from . path "; - } - - resource function get httpAccessorTest() returns @af:HTTPOutput string { - return "Hello from all"; - } - - resource function put httpAccessorTest() returns @af:HTTPOutput string { - return "Hello from all"; + + resource function post optional/out(@af:Payload string greeting) returns string { + return "Hello from optional output binding"; } - - resource function patch httpAccessorTest() returns @af:HTTPOutput string { - return "Hello from all"; - } - - resource function delete httpAccessorTest() returns @af:HTTPOutput string { - return "Hello from all"; - } - - resource function head httpAccessorTest() returns @af:HTTPOutput string { - return "Hello from all"; + + resource function post optional/payload(@af:Payload string? greeting) returns string { + if (greeting is string) { + return "Hello, the payload found " + greeting; + } + return "Hello, the payload wasn't set but all good ;)"; } - - resource function options httpAccessorTest() returns @af:HTTPOutput string { - return "Hello from all"; + + resource function post .(@af:Payload string greeting) returns @af:HttpOutput string { + return "Hello from . path "; } - - resource function post httpResTest1(@af:Payload string greeting) returns @af:HTTPOutput af:Unauthorized { + resource function post httpResTest1(@af:Payload string greeting) returns @af:HttpOutput af:Unauthorized { af:Unauthorized unauth = { body: "Helloworld.....", mediaType: "application/account+json", @@ -55,27 +41,107 @@ service /hello on ep { return unauth; } - resource function post httpResTest2(@af:Payload string greeting) returns @af:HTTPOutput af:Ok { + resource function post httpResTest2(@af:Payload string greeting) returns @af:HttpOutput af:Ok { af:Ok ok = {body: "Helloworld....."}; return ok; } - - resource function post httpResTest3(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { + resource function post httpResTest3(@af:Payload string greeting) returns @af:HttpOutput af:InternalServerError { af:InternalServerError err = { body: "Helloworld.....", headers: { - "content-type": "application/json+id", + "Content-Type": "application/json+id", "Location": "/myServer/084230" } }; return err; } - - resource function post httpResTest4(@af:Payload string greeting) returns @af:HTTPOutput af:InternalServerError { + resource function post httpResTest4(@af:Payload string greeting) returns @af:HttpOutput af:InternalServerError { af:InternalServerError err = {}; return err; } + resource function post foo(@af:Payload string greeting) returns @af:HttpOutput string { + return "Hello from foo path " + greeting; + } + + resource function post foo/[string bar](@af:Payload string greeting) returns @af:HttpOutput string { + return "Hello from foo param " + bar; + } + + resource function post foo/bar(@af:Payload string greeting) returns @af:HttpOutput string { + return "Hello from foo bar res"; + } + + resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { + return "Hello from the query " + greeting + " " + name; + } + + resource function post db(@af:Payload string greeting, @af:CosmosDBInput { + connectionStringSetting: "CosmosDBConnection", + databaseName: "db1", + collectionName: "c2", + sqlQuery: "SELECT * FROM Items" + } DBEntry[] input1) returns @af:HttpOutput string|error { + return "Hello " + greeting + input1[0].id; + } + + resource function post payload/jsonToRecord(@af:Payload Person greeting) returns @af:HttpOutput string|error { + return "Hello from json to record " + greeting.name; + } + + resource function post payload/jsonToJson(@af:Payload json greeting) returns @af:HttpOutput string|error { + string name = check greeting.name; + return "Hello from json to json " + name; + } + + resource function post payload/xmlToXml(@af:Payload xml greeting) returns @af:HttpOutput string|error { + return greeting.toJsonString(); + } + + resource function post payload/textToString(@af:Payload string greeting) returns @af:HttpOutput string|error { + return greeting; + } + + resource function post payload/textToByte(@af:Payload byte[] greeting) returns @af:HttpOutput string|error { + return string:fromBytes(greeting); + } + + resource function post payload/octaToByte(@af:Payload byte[] greeting) returns @af:HttpOutput string|error { + return string:fromBytes(greeting); + } + + resource function get err/empty/payload(@af:Payload string greeting) returns @af:HttpOutput string { + return "Hello from get empty payload"; + } + + resource function post err/invalid/payload(@af:Payload string greeting) returns @af:HttpOutput string { + return "Hello from get invalid payload " + greeting; + } + + resource function get httpAccessorTest() returns @af:HttpOutput string { + return "Hello from all"; + } + + resource function put httpAccessorTest() returns @af:HttpOutput string { + return "Hello from all"; + } + + resource function patch httpAccessorTest() returns @af:HttpOutput string { + return "Hello from all"; + } + + resource function delete httpAccessorTest() returns @af:HttpOutput string { + return "Hello from all"; + } + + resource function head httpAccessorTest() returns @af:HttpOutput string { + return "Hello from all"; + } + + resource function options httpAccessorTest() returns @af:HttpOutput string { + return "Hello from all"; + } + resource function post httpResTest5() returns af:StatusCodeResponse { af:InternalServerError err = {}; return err; @@ -152,56 +218,6 @@ service /hello on ep { resource function post nonReturnTest1() { } - - resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { - return "Hello from foo path " + greeting; - } - - resource function post foo/[string bar](@af:Payload string greeting) returns @af:HTTPOutput string { - return "Hello from foo param " + bar; - } - - resource function post foo/bar(@af:Payload string greeting) returns @af:HTTPOutput string { - return "Hello from foo bar res"; - } - - resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { - return "Hello from the query " + greeting + " " + name; - } - - resource function post db(@af:Payload string greeting, @af:CosmosDBInput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", - collectionName: "c2", - sqlQuery: "SELECT * FROM Items" - } DBEntry[] input1) returns @af:HTTPOutput string|error { - return "Hello " + greeting + input1[0].id; - } - - resource function post payload/jsonToRecord(@af:Payload Person greeting) returns @af:HTTPOutput string|error { - return "Hello from json to record " + greeting.name; - } - - resource function post payload/jsonToJson(@af:Payload json greeting) returns @af:HTTPOutput string|error { - string name = check greeting.name; - return "Hello from json to json " + name; - } - - resource function post payload/xmlToXml(@af:Payload xml greeting) returns @af:HTTPOutput string|error { - return greeting.toJsonString(); - } - - resource function post payload/textToString(@af:Payload string greeting) returns @af:HTTPOutput string|error { - return greeting; - } - - resource function post payload/textToByte(@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { - return string:fromBytes(greeting); - } - - resource function post payload/octaToByte(@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { - return string:fromBytes(greeting); - } } @af:QueueTrigger { @@ -255,9 +271,8 @@ service "queue-input" on queueListener1 { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated(@af:Payload byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { - path: "bpath1/newBlob" - } byte[]|error { + remote function onUpdated (@af:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { + path: "bpath1/newBlob" } byte[]|error { return blobIn; } } diff --git a/ballerina-tests/tests/resources/error-invalid-payload.json b/ballerina-tests/tests/resources/error-invalid-payload.json new file mode 100644 index 00000000..704a6e10 --- /dev/null +++ b/ballerina-tests/tests/resources/error-invalid-payload.json @@ -0,0 +1,116 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/octaToByte", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "20" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "4464b495-cbb0-4398-b79f-9b626afc54af" + ], + "X-ARR-LOG-ID": [ + "03f45b02-ca0c-44a4-8fbd-1a916dc44743" + ], + "CLIENT-IP": [ + "10.0.32.9:31863" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.183:8622" + ], + "X-Original-URL": [ + "/api/hello/payload/octaToByte" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/payload/octaToByte" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "aGVsbG8gZnJvbSBieXRlIGFycgo=" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "20", + "Content-Type": "application/octet-stream", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "4464b495-cbb0-4398-b79f-9b626afc54af", + "X-ARR-LOG-ID": "03f45b02-ca0c-44a4-8fbd-1a916dc44743", + "CLIENT-IP": "10.0.32.9:31863", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.183:8622", + "X-Original-URL": "/api/hello/payload/octaToByte", + "X-WAWS-Unencoded-URL": "/api/hello/payload/octaToByte", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "post-hello-err-invalid-payload", + "UtcNow": "2022-06-21T09:16:39.7549127Z", + "RandGuid": "4d6607a9-6f7f-47ab-b604-9d38b6d60166" + } + } +} diff --git a/ballerina-tests/tests/resources/error-missing-payload.json b/ballerina-tests/tests/resources/error-missing-payload.json new file mode 100644 index 00000000..a957e533 --- /dev/null +++ b/ballerina-tests/tests/resources/error-missing-payload.json @@ -0,0 +1,99 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/err/empty/payload", + "Method": "GET", + "Query": {}, + "Headers": { + "Cache-Control": [ + "max-age=0" + ], + "Connection": [ + "keep-alive" + ], + "Accept": [ + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Accept-Language": [ + "en-US,en;q=0.9" + ], + "Cookie": [ + "_ga=GA1.1.720190479.1641539348; _hjSessionUser_865786=eyJpZCI6IjhjNjRiYjkwLTA0NzYtNTc5MS1hZWNiLTRjN2VhZTM4OWM5NCIsImNyZWF0ZWQiOjE2NDE1MzkzNDg4NTAsImV4aXN0aW5nIjp0cnVlfQ==; Idea-2474b141=7a691445-10e4-435d-b306-2b36a77b1d2e; Webstorm-fe5cf5e6=379eafe5-e706-45c1-92e8-6e424ab655f0; cookie_accepted=1" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" + ], + "Upgrade-Insecure-Requests": [ + "1" + ], + "sec-ch-ua": [ + "\".Not\/A)Brand\";v=\"99\", \"Google Chrome\";v=\"103\", \"Chromium\";v=\"103\"" + ], + "sec-ch-ua-mobile": [ + "?0" + ], + "sec-ch-ua-platform": [ + "\"Linux\"" + ], + "Sec-Fetch-Site": [ + "none" + ], + "Sec-Fetch-Mode": [ + "navigate" + ], + "Sec-Fetch-User": [ + "?1" + ], + "Sec-Fetch-Dest": [ + "document" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Cache-Control": "max-age=0", + "Connection": "keep-alive", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "en-US,en;q=0.9", + "Cookie": "_ga=GA1.1.720190479.1641539348; _hjSessionUser_865786=eyJpZCI6IjhjNjRiYjkwLTA0NzYtNTc5MS1hZWNiLTRjN2VhZTM4OWM5NCIsImNyZWF0ZWQiOjE2NDE1MzkzNDg4NTAsImV4aXN0aW5nIjp0cnVlfQ==; Idea-2474b141=7a691445-10e4-435d-b306-2b36a77b1d2e; Webstorm-fe5cf5e6=379eafe5-e706-45c1-92e8-6e424ab655f0; cookie_accepted=1", + "Host": "localhost:7071", + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", + "Upgrade-Insecure-Requests": "1", + "sec-ch-ua": "\".Not\/A)Brand\";v=\"99\", \"Google Chrome\";v=\"103\", \"Chromium\";v=\"103\"", + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": "\"Linux\"", + "Sec-Fetch-Site": "none", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-User": "?1", + "Sec-Fetch-Dest": "document" + }, + "sys": { + "MethodName": "get-hello-err-empty-payload", + "UtcNow": "2022-08-04T16:06:50.5393863Z", + "RandGuid": "0243c7ea-084a-4413-bd70-3cee25cbbd3c" + } + } +} diff --git a/ballerina-tests/tests/resources/http-optional-out.json b/ballerina-tests/tests/resources/http-optional-out.json new file mode 100644 index 00000000..da81f2c2 --- /dev/null +++ b/ballerina-tests/tests/resources/http-optional-out.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/optional/out", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/optional/out" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/optional/out" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/optional/out", + "X-WAWS-Unencoded-URL": "/api/hello/optional/out" + }, + "sys": { + "MethodName": "post-hello-optional-out", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/http-optional-with-payload.json b/ballerina-tests/tests/resources/http-optional-with-payload.json new file mode 100644 index 00000000..d57cb4b3 --- /dev/null +++ b/ballerina-tests/tests/resources/http-optional-with-payload.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/optional/payload", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/optional/payload" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/optional/payload" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/optional/payload", + "X-WAWS-Unencoded-URL": "/api/hello/optional/payload" + }, + "sys": { + "MethodName": "post-hello-optional-payload", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/resources/http-optional-without-payload.json b/ballerina-tests/tests/resources/http-optional-without-payload.json new file mode 100644 index 00000000..40147d14 --- /dev/null +++ b/ballerina-tests/tests/resources/http-optional-without-payload.json @@ -0,0 +1,107 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/optional/payload", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello/optional/payload" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/optional/payload" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello/optional/payload", + "X-WAWS-Unencoded-URL": "/api/hello/optional/payload" + }, + "sys": { + "MethodName": "post-hello-optional-payload", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index b74c39cc..e92c9639 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -11,7 +11,7 @@ function testDefault() returns error? { string jsonFilePath = "./tests/resources/default.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/default-hello-all", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -23,7 +23,7 @@ function getHttpAccessorTest() returns error? { string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","get"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/get-hello-httpAccessorTest", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -35,7 +35,7 @@ function putHttpAccessorTest() returns error? { string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","put"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/put-hello-httpAccessorTest", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -47,7 +47,7 @@ function patchHttpAccessorTest() returns error? { string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","patch"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/patch-hello-httpAccessorTest", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -59,7 +59,7 @@ function deleteHttpAccessorTest() returns error? { string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","delete"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/delete-hello-httpAccessorTest", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -71,7 +71,7 @@ function headHttpAccessorTest() returns error? { string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","head"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/head-hello-httpAccessorTest", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -83,7 +83,7 @@ function optionsHttpAccessorTest() returns error? { string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","options"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/options-hello-httpAccessorTest", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"content-type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -93,7 +93,7 @@ function testBaseDot() returns error? { string jsonFilePath = "./tests/resources/base-dot.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from . path "}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from . path "}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -108,7 +108,7 @@ function httpResTest1() returns error? { "resp": { "statusCode": "401", "body": "Helloworld.....", - "headers": {"Location": "/myServer/084230", "content-type": "application/account+json"} + "headers": {"Location": "/myServer/084230", "Content-Type": "application/account+json"} } }, "Logs": [], @@ -128,7 +128,7 @@ function httpResTest2() returns error? { "resp": { "statusCode": "200", "body": "Helloworld.....", - "headers": {"content-type": "application/json"} + "headers": {"Content-Type": "application/json"} } }, "Logs": [], @@ -148,7 +148,7 @@ function httpResTest3() returns error? { "resp": { "statusCode": "500", "body": "Helloworld.....", - "headers": {"content-type": "application/json+id", "Location": "/myServer/084230"} + "headers": {"Content-Type": "application/json+id", "Location": "/myServer/084230"} } }, "Logs": [], @@ -167,7 +167,7 @@ function httpResTest4() returns error? { "Outputs": { "resp": { "statusCode": "500", - "headers": {"content-type": "application/json"} + "headers": {"Content-Type": "application/json"} } }, "Logs": [], @@ -189,7 +189,7 @@ function nonHttpResTest1() returns error? { "resp": { "statusCode": "201", "body": "alpha", - "headers": {"content-type": "text/plain"} + "headers": {"Content-Type": "text/plain"} } }, "Logs": [], @@ -206,7 +206,7 @@ function nonHttpResTest2() returns error? { string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest2"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest2", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/xml"}, "body":"The Lost World"}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/xml"}, "body":"The Lost World"}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -218,7 +218,7 @@ function nonHttpResTest3() returns error? { string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest3"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest3", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/octet-stream"}, "body":[200, 241, 218, 202, 212, 96, 36, 248, 62, 66, 56, 242, 149, 65, 218, 144, 76, 51, 213, 245, 143, 199, 252, 23, 9, 110, 53, 37, 41, 170, 97, 111]}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/octet-stream"}, "body":[200, 241, 218, 202, 212, 96, 36, 248, 62, 66, 56, 242, 149, 65, 218, 144, 76, 51, 213, 245, 143, 199, 252, 23, 9, 110, 53, 37, 41, 170, 97, 111]}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -230,7 +230,7 @@ function nonHttpResTest4() returns error? { string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest4"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest4", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -242,7 +242,7 @@ function nonHttpResTest6() returns error? { string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest6"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest6", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -254,7 +254,7 @@ function nonHttpResTest7() returns error? { string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest7"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest7", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"content-type":"application/json"}, "body":true}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":true}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -271,7 +271,7 @@ function nonHttpResTest8() returns error? { "resp": { "statusCode": "201", "body":{"a":{"b":12, "c":"helloworld"}}, - "headers": {"content-type":"application/json"} + "headers": {"Content-Type":"application/json"} } }, "Logs": [], @@ -293,7 +293,7 @@ function nonHttpResTest9() returns error? { "resp": { "statusCode": "201", "body":[{"a":{"b":12, "c":"helloworld"}}, {"b":1100}], - "headers": {"content-type":"application/json"} + "headers": {"Content-Type":"application/json"} } }, "Logs": [], @@ -315,7 +315,7 @@ function nonHttpResTest10() returns error? { "resp": { "statusCode": "201", "body":[{"a":{"b":12, "c":"helloworld"}}, {"b":12}], - "headers":{"content-type":"application/json"} + "headers":{"Content-Type":"application/json"} } }, "Logs": [], @@ -337,7 +337,7 @@ function nonHttpResTest11() returns error? { "resp": { "statusCode": "201", "body":[[{"a":{"b":12, "c":"helloworld"}}, {"b":12}], [{"a":{"b":14, "c":"helloworld"}}, {"b":100}]], - "headers": {"content-type":"application/json"} + "headers": {"Content-Type":"application/json"} } }, "Logs": [], @@ -370,7 +370,7 @@ function testSimpleResourcePath() returns error? { string jsonFilePath = "./tests/resources/res-path.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from foo path Jack"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from foo path Jack"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -380,7 +380,7 @@ function testSimpleMultiResourcePath() returns error? { string jsonFilePath = "./tests/resources/res-path-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo-bar-2", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from foo bar res"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from foo bar res"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -390,7 +390,7 @@ function testSimpleConflictingPathParam() returns error? { string jsonFilePath = "./tests/resources/res-path-conflict-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-foo-bar-1", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from foo param meow"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from foo param meow"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -400,7 +400,7 @@ function testSimpleMultiQueryPath() returns error? { string jsonFilePath = "./tests/resources/query-param.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-query", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from the query Jack test1"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from the query Jack test1"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -420,7 +420,7 @@ function testCosmosInputArr() returns error? { string jsonFilePath = "./tests/resources/cosmos-db-arr.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-db", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello Jackhello1"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello Jackhello1"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -430,7 +430,7 @@ function testJsonJsonPayload() returns error? { string jsonFilePath = "./tests/resources/payload-json-json.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-jsonToJson", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from json to json Anjana"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from json to json Anjana"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -440,7 +440,7 @@ function testJsonRecordPayload() returns error? { string jsonFilePath = "./tests/resources/payload-json-record.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-jsonToRecord", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "Hello from json to record Anjana"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from json to record Anjana"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -451,7 +451,7 @@ function testXmlPayload() returns error? { json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-xmlToXml", readJson); string xmlPayload = "\"\\n Anjana<\\/name>\\n 12<\\/age>\\n<\\/root>\""; - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": xmlPayload}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": xmlPayload}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -461,7 +461,7 @@ function testTextStringPayload() returns error? { string jsonFilePath = "./tests/resources/payload-text-string.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-textToString", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -471,7 +471,7 @@ function testTextBytePayload() returns error? { string jsonFilePath = "./tests/resources/payload-text-byte.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-textToByte", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "hello from byte\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -481,7 +481,7 @@ function testOctaBytePayload() returns error? { string jsonFilePath = "./tests/resources/payload-octa-byte.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-payload-octaToByte", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"content-type": "text/plain"}, "body": "hello from byte arr\n"}}, "Logs": [], "ReturnValue": null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "hello from byte arr\n"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -518,4 +518,58 @@ function testQueueInput() returns error? { function replaceFuncName(string actual) { -} \ No newline at end of file +} + +@test:Config {} +function testOptionalOutputBinding() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-optional-out.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-optional-out", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode":"201","headers":{"Content-Type":"text/plain"},"body": "Hello from optional output binding"}}, "Logs": [], + "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + + +@test:Config { } +function testErrorPayloadNotFound() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/error-missing-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-err-empty-payload", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400,"body":"payload not found for the variable 'greeting'", + "headers":{"Content-Type":"text/plain"}}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testErrorInvalidPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/error-invalid-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-err-invalid-payload", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400,"body":"incompatible type found: 'string", + "headers":{"Content-Type":"text/plain"}}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testOptionalPayloadWithPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-optional-with-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-optional-payload", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201","headers":{"Content-Type":"text/plain"},"body":"Hello, the payload found Jack"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testOptionalPayloadWithoutPayload() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-optional-without-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-optional-payload", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201","headers":{"Content-Type":"text/plain"},"body":"Hello, the payload wasn't set but all good ;)"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} diff --git a/ballerina/Module.md b/ballerina/Module.md index 61420596..acfc19ec 100644 --- a/ballerina/Module.md +++ b/ballerina/Module.md @@ -20,7 +20,7 @@ import ballerinax/azure_functions as af; // HTTP request/response with no authentication @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } @@ -29,7 +29,7 @@ public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string public isolated function fromHttpToQueue(af:Context ctx, @af:HTTPTrigger {} af:HTTPRequest req, @af:QueueOutput { queueName: "queue1" } af:StringOutputBinding msg) - returns @af:HTTPOutput af:HTTPBinding { + returns @af:HttpOutput af:HTTPBinding { msg.value = req.body; return { statusCode: 200, payload: "Request: " + req.toString() }; } @@ -58,7 +58,7 @@ public isolated function fromBlobToQueue(af:Context ctx, @af:Function public isolated function httpTriggerBlobInput(@af:HTTPTrigger { } af:HTTPRequest req, @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { int length = 0; if blobIn is byte[] { length = blobIn.length(); @@ -71,7 +71,7 @@ public isolated function httpTriggerBlobInput(@af:HTTPTrigger { } af:HTTPRequest @af:Function public isolated function httpTriggerBlobOutput(@af:HTTPTrigger { } af:HTTPRequest req, @af:BlobOutput { path: "bpath1/{Query.name}" } af:StringOutputBinding bb) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { bb.value = req.body; return "Blob: " + req.query["name"].toString() + " Content: " + bb?.value.toString(); @@ -82,7 +82,7 @@ public isolated function httpTriggerBlobOutput(@af:HTTPTrigger { } af:HTTPReques public isolated function sendSMS(@af:HTTPTrigger { } af:HTTPRequest req, @af:TwilioSmsOutput { fromNumber: "+12069845840" } af:TwilioSmsOutputBinding tb) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { tb.to = req.query["to"].toString(); tb.body = req.body.toString(); return "Message - to: " + tb?.to.toString() + " body: " + tb?.body.toString(); @@ -118,7 +118,7 @@ public isolated function httpTriggerCosmosDBInput1( @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1", id: "{Query.id}", partitionKey: "{Query.country}" } json dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } @@ -128,7 +128,7 @@ public isolated function httpTriggerCosmosDBInput2( @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1", id: "{Query.id}", partitionKey: "{Query.country}" } Person? dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } @@ -139,14 +139,14 @@ public isolated function httpTriggerCosmosDBInput3( databaseName: "db1", collectionName: "c1", sqlQuery: "select * from c1 where c1.country = {country}" } Person[] dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } // HTTP request to write records to CosmosDB @af:Function public isolated function httpTriggerCosmosDBOutput1( - @af:HTTPTrigger { } af:HTTPRequest httpReq, @af:HTTPOutput af:HTTPBinding hb) + @af:HTTPTrigger { } af:HTTPRequest httpReq, @af:HttpOutput af:HTTPBinding hb) returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1" } json { json entry = { id: uuid:createType1AsString(), name: "Saman", country: "Sri Lanka" }; @@ -157,7 +157,7 @@ public isolated function httpTriggerCosmosDBOutput1( @af:Function public isolated function httpTriggerCosmosDBOutput2( @af:HTTPTrigger { } af:HTTPRequest httpReq, - @af:HTTPOutput af:HTTPBinding hb) + @af:HttpOutput af:HTTPBinding hb) returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1" } json { diff --git a/ballerina/annotation.bal b/ballerina/annotation.bal index c2bdc2db..953e2449 100644 --- a/ballerina/annotation.bal +++ b/ballerina/annotation.bal @@ -34,8 +34,8 @@ public type HTTPTriggerConfiguration record {| public annotation Payload on parameter, return; -# @azurefunctions:HTTPOutput annotation -public const annotation HTTPOutput on parameter, return; +# @azurefunctions:HttpOutput annotation +public const annotation HttpOutput on parameter, return; # Queue annotation configuration. # diff --git a/ballerina/dispatcher_service.bal b/ballerina/dispatcher_service.bal index 04df6e6f..aa078974 100644 --- a/ballerina/dispatcher_service.bal +++ b/ballerina/dispatcher_service.bal @@ -34,7 +34,12 @@ isolated service class DispatcherService { io:println(message.toJsonString()); //map body = >check message.Data; - map callRegisterMethod = check self.adaptor.callRemoteFunction(>message, self.remoteMethodName); + map|error callRegisterMethod = self.adaptor.callRemoteFunction(>message, self + .remoteMethodName); + if (callRegisterMethod is error) { + io:println (callRegisterMethod); + return; + } json result = {Outputs: callRegisterMethod.toJson(), Logs: []}; result = check result.mergeJson({ReturnValue: null}); io:println(result); diff --git a/ballerina/errors.bal b/ballerina/errors.bal new file mode 100644 index 00000000..4ef1da1c --- /dev/null +++ b/ballerina/errors.bal @@ -0,0 +1,7 @@ +public type Error distinct error; + +public type FunctionNotFoundError distinct Error; + +public type PayloadNotFoundError distinct Error; + +public type InvalidPayloadError distinct Error; diff --git a/ballerina/http_service.bal b/ballerina/http_service.bal index 4347fae5..543f0208 100644 --- a/ballerina/http_service.bal +++ b/ballerina/http_service.bal @@ -27,22 +27,24 @@ isolated service class ResourceService { } isolated resource function post .(http:Caller caller, http:Request request) returns error? { - http:Response response = new; json message = check request.getJsonPayload(); - // map body = {}; io:println(message.toJsonString()); - //TODO conver to record instead of map Payload payload = check message.cloneWithType(Payload); - // io:println(payload); - // body = >check message.Data; - // string functionName = check message.Metadata.sys.MethodName; string functionName = payload.Metadata.sys.MethodName; - map callRegisterMethod = check self.adaptor.callNativeMethod(payload.Data, functionName); - json result = {Outputs: callRegisterMethod.toJson(), Logs: []}; - result = check result.mergeJson({ReturnValue: null}); - io:println(result); - response.setJsonPayload(result); + map|error callRegisterMethod = self.adaptor.callNativeMethod(payload.Data, functionName); + response.setJsonPayload(getResponsePayload(callRegisterMethod)); check caller->respond(response); } } + + +isolated function getResponsePayload (map|error nativeResponse) returns json { + if (nativeResponse is PayloadNotFoundError || nativeResponse is InvalidPayloadError ) { + return {"Outputs": {"resp": {"statusCode": 400, "body": nativeResponse.message(),"headers": {"Content-Type": "text/plain"}}}, "Logs": [], "ReturnValue": null}; + } else if (nativeResponse is error) { + return {"Outputs": {"resp": {"statusCode": 500}}, "Logs": [], "ReturnValue": null}; + } else { + return {Outputs: nativeResponse.toJson(), Logs: [], ReturnValue: null}; + } +} diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java index a5bbf03e..159604a9 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -73,7 +73,18 @@ public void compileSample() { DiagnosticResult diagnosticResult = compilation.diagnosticResult(); Assert.assertFalse(diagnosticResult.hasErrors()); - Assert.assertEquals(generatedFunctions.size(), 17); + Assert.assertEquals(generatedFunctions.size(), 18); + } + + @Test + public void testOptionalHttp() { + JsonObject httpHello = generatedFunctions.get("post-hello-optional"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello/optional\"}," + + "{\"type\":\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(httpHello, parse); } @Test diff --git a/compiler-plugin-tests/src/test/resources/deployment/functions.bal b/compiler-plugin-tests/src/test/resources/deployment/functions.bal index 98892b78..383d15e6 100644 --- a/compiler-plugin-tests/src/test/resources/deployment/functions.bal +++ b/compiler-plugin-tests/src/test/resources/deployment/functions.bal @@ -4,7 +4,7 @@ import ballerinax/azure_functions as af; // HTTP request/response with no authentication @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } @@ -13,7 +13,7 @@ public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string public isolated function fromHttpToQueue(af:Context ctx, @af:HTTPTrigger af:HTTPRequest req, @af:QueueOutput { queueName: "queue1" } af:StringOutputBinding msg) - returns @af:HTTPOutput af:HTTPBinding { + returns @af:HttpOutput af:HTTPBinding { msg.value = req.body; return { statusCode: 200, payload: "Request: " + req.toString() }; } @@ -42,7 +42,7 @@ public isolated function fromBlobToQueue(af:Context ctx, @af:Function public isolated function httpTriggerBlobInput(@af:HTTPTrigger af:HTTPRequest req, @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { int length = 0; if blobIn is byte[] { length = blobIn.length(); @@ -55,7 +55,7 @@ public isolated function httpTriggerBlobInput(@af:HTTPTrigger af:HTTPRequest req @af:Function public isolated function httpTriggerBlobInputStr(@af:HTTPTrigger af:HTTPRequest req, @af:BlobInput { path: "bpath1/{Query.name}" } string? strIn) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { string str = ""; if strIn is string { str = strIn; @@ -68,7 +68,7 @@ public isolated function httpTriggerBlobInputStr(@af:HTTPTrigger af:HTTPRequest @af:Function public isolated function httpTriggerBlobOutput(@af:HTTPTrigger af:HTTPRequest req, @af:BlobOutput { path: "bpath1/{Query.name}" } af:StringOutputBinding bb) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { bb.value = req.body; return "Blob: " + req.query["name"].toString() + " Content: " + bb?.value.toString(); @@ -78,7 +78,7 @@ public isolated function httpTriggerBlobOutput(@af:HTTPTrigger af:HTTPRequest re @af:Function public isolated function httpTriggerBlobOutput2(@af:HTTPTrigger af:HTTPRequest req, @af:BlobOutput { path: "bpath1/{Query.name}" } af:BytesOutputBinding bb) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { bb.value = [65, 66, 67, 97, 98]; return "Blob: " + req.query["name"].toString() + " Content: " + bb?.value.toString(); @@ -89,7 +89,7 @@ public isolated function httpTriggerBlobOutput2(@af:HTTPTrigger af:HTTPRequest r public isolated function sendSMS(@af:HTTPTrigger af:HTTPRequest req, @af:TwilioSmsOutput { fromNumber: "+12069845840" } af:TwilioSmsOutputBinding tb) - returns @af:HTTPOutput string { + returns @af:HttpOutput string { tb.to = req.query["to"].toString(); tb.body = req.body.toString(); return "Message - to: " + tb?.to.toString() + " body: " + tb?.body.toString(); @@ -125,7 +125,7 @@ public isolated function httpTriggerCosmosDBInput1( @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1", id: "{Query.id}", partitionKey: "{Query.country}" } json dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } @@ -135,7 +135,7 @@ public isolated function httpTriggerCosmosDBInput2( @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1", id: "{Query.id}", partitionKey: "{Query.country}" } Person? dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } @@ -146,14 +146,14 @@ public isolated function httpTriggerCosmosDBInput3( databaseName: "db1", collectionName: "c1", sqlQuery: "select * from c1 where c1.country = {country}" } Person[] dbReq) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return dbReq.toString(); } // HTTP request to write records to CosmosDB @af:Function public isolated function httpTriggerCosmosDBOutput1( - @af:HTTPTrigger af:HTTPRequest httpReq, @af:HTTPOutput af:HTTPBinding hb) + @af:HTTPTrigger af:HTTPRequest httpReq, @af:HttpOutput af:HTTPBinding hb) returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1" } json { json entry = { id: uuid:createType1AsString(), name: "Saman", country: "Sri Lanka" }; @@ -164,7 +164,7 @@ public isolated function httpTriggerCosmosDBOutput1( @af:Function public isolated function httpTriggerCosmosDBOutput2( @af:HTTPTrigger af:HTTPRequest httpReq, - @af:HTTPOutput af:HTTPBinding hb) + @af:HttpOutput af:HTTPBinding hb) returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1" } json { diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index 43d5ea0e..11ebb6df 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -13,58 +13,62 @@ type Person record { // @af:HTTPTest service /hello on ep { - resource function default all() returns @af:HTTPOutput string { + resource function default all() returns @af:HttpOutput string { return "Hello from all "; } - resource function post .(@af:Payload string greeting) returns @af:HTTPOutput string { + resource function post optional(@af:Payload string greeting) returns string { + return "Hello from optional output bindin"; + } + + resource function post .(@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from . path "; } - resource function post foo(@af:Payload string greeting) returns @af:HTTPOutput string { + resource function post foo(@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo path " + greeting; } - resource function post foo/[string bar](@af:Payload string greeting) returns @af:HTTPOutput string { + resource function post foo/[string bar](@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo param " + bar; } - resource function post foo/bar(@af:Payload string greeting) returns @af:HTTPOutput string { + resource function post foo/bar(@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo bar res"; } - resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { + resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { return "Hello from the query " + greeting + " " + name; } resource function post db(@af:Payload string greeting, @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection",databaseName: "db1", - collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:HTTPOutput string|error { + collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:HttpOutput string|error { return "Hello " + greeting + input1[0].id; } - resource function post payload/jsonToRecord (@af:Payload Person greeting) returns @af:HTTPOutput string|error { + resource function post payload/jsonToRecord (@af:Payload Person greeting) returns @af:HttpOutput string|error { return "Hello from json to record " + greeting.name; } - resource function post payload/jsonToJson (@af:Payload json greeting) returns @af:HTTPOutput string|error { + resource function post payload/jsonToJson (@af:Payload json greeting) returns @af:HttpOutput string|error { string name = check greeting.name; return "Hello from json to json "+ name; } - resource function post payload/xmlToXml (@af:Payload xml greeting) returns @af:HTTPOutput string|error { + resource function post payload/xmlToXml (@af:Payload xml greeting) returns @af:HttpOutput string|error { return greeting.toJsonString(); } - resource function post payload/textToString (@af:Payload string greeting) returns @af:HTTPOutput string|error { + resource function post payload/textToString (@af:Payload string greeting) returns @af:HttpOutput string|error { return greeting; } - resource function post payload/textToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + resource function post payload/textToByte (@af:Payload byte[] greeting) returns @af:HttpOutput string|error { return string:fromBytes(greeting); } - resource function post payload/octaToByte (@af:Payload byte[] greeting) returns @af:HTTPOutput string|error { + resource function post payload/octaToByte (@af:Payload byte[] greeting) returns @af:HttpOutput string|error { return string:fromBytes(greeting); } } diff --git a/compiler-plugin-tests/src/test/resources/validations/main/main.bal b/compiler-plugin-tests/src/test/resources/validations/main/main.bal index fd688ef1..f3a85429 100644 --- a/compiler-plugin-tests/src/test/resources/validations/main/main.bal +++ b/compiler-plugin-tests/src/test/resources/validations/main/main.bal @@ -20,7 +20,7 @@ import ballerinax/azure_functions as af; // HTTP request/response with no authentication @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } diff --git a/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal b/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal index 888bc79c..8a21e546 100644 --- a/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal +++ b/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal @@ -3,6 +3,6 @@ import ballerinax/azure_functions as af; // HTTP request/response with no authentication @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } diff --git a/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal b/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal index 7e002fb5..15aa1353 100644 --- a/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal +++ b/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal @@ -20,7 +20,7 @@ import submodule.mod1; // HTTP request/response with no authentication @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } diff --git a/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal b/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal index d515e39e..44f3f03d 100644 --- a/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal +++ b/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal @@ -17,6 +17,6 @@ import ballerinax/azure_functions as af; @af:Function public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HTTPOutput string|error { + returns @af:HttpOutput string|error { return "Hello, " + payload + "!"; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java index e12a7fa9..e5392ad0 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java @@ -31,7 +31,7 @@ public Optional getOutputBinding(NodeList nodes) { switch (annotationName) { case "QueueOutput": return Optional.of(new QueueOutputBinding(annotationNode)); - case "HTTPOutput": + case "HttpOutput": return Optional.of(new HTTPOutputBinding(annotationNode)); case "CosmosDBOutput": return Optional.of(new CosmosDBOutputBinding(annotationNode)); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java index 89f4fc9a..7bd6a196 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java @@ -100,7 +100,7 @@ public static TriggerBinding getBuilder(ServiceDeclarationNode svcDeclarationNod // } // } // break; -// case "HTTPOutput": +// case "HttpOutput": // return Optional.of(new HTTPOutputBinding()); // case "CosmosDBOutput": // CosmosDBOutputBinding cosmosDBOutputBinding = new CosmosDBOutputBinding(); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index cc346b7b..47774e4e 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -22,41 +22,45 @@ * {@code Constants} contains the public constants to be used. */ public interface Constants { - String PACKAGE_ORG = "ballerinax"; - String PACKAGE_NAME = "azure_functions"; + String PACKAGE_ORG = "ballerinax"; + String PACKAGE_NAME = "azure_functions"; - String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; + String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; - String QUEUE_OUTPUT = "QueueOutput"; - String COSMOS_DBOUTPUT = "CosmosDBOutput"; - String OUT_MSG = "outMsg"; - String HTTP_OUTPUT = "HTTPOutput"; - String BLOB_OUTPUT = "BlobOutput"; - String PAYLOAD_ANNOTATAION = "Payload"; - String STATUS = "status"; - String CODE = "code"; - String STATUS_CODE = "statusCode"; - String BODY = "body"; - String HEADERS = "headers"; - String CONTENT_TYPE = "content-type"; - String MEDIA_TYPE = "mediaType"; - String RESP = "resp"; - String POST = "post"; - String CREATED_201 = "201"; - String GET = "get"; - String PUT = "put"; - String PATCH = "patch"; - String DELETE = "delete"; - String HEAD = "head"; - String OPTIONS = "options"; - String DEFAULT = "default"; - String OK_200 = "200"; - String TEXT_PLAIN = "text/plain"; - String APPLICATION_XML = "application/xml"; - String APPLICATION_OCTET_STREAM = "application/octet-stream"; - String APPLICATION_JSON = "application/json"; - String BYTE_TYPE = "byte"; - String MAP_TYPE = "map"; - String JSON_TYPE = "json"; - String TABLE_TYPE = "table"; + String QUEUE_OUTPUT = "QueueOutput"; + String COSMOS_DBOUTPUT = "CosmosDBOutput"; + String OUT_MSG = "outMsg"; + String HTTP_OUTPUT = "HttpOutput"; + String BLOB_OUTPUT = "BlobOutput"; + String PAYLOAD_ANNOTATAION = "Payload"; + String STATUS = "status"; + String CODE = "code"; + String STATUS_CODE = "statusCode"; + String BODY = "body"; + String HEADERS = "headers"; + String CONTENT_TYPE = "Content-Type"; + String MEDIA_TYPE = "mediaType"; + String RESP = "resp"; + String POST = "post"; + String CREATED_201 = "201"; + String GET = "get"; + String PUT = "put"; + String PATCH = "patch"; + String DELETE = "delete"; + String HEAD = "head"; + String OPTIONS = "options"; + String DEFAULT = "default"; + String OK_200 = "200"; + String TEXT_PLAIN = "text/plain"; + String APPLICATION_XML = "application/xml"; + String APPLICATION_OCTET_STREAM = "application/octet-stream"; + String APPLICATION_JSON = "application/json"; + String BYTE_TYPE = "byte"; + String MAP_TYPE = "map"; + String JSON_TYPE = "json"; + String TABLE_TYPE = "table"; + + String PAYLOAD_NOT_FOUND_ERROR = "PayloadNotFoundError"; + String FUNCTION_NOT_FOUND_ERROR = "FunctionNotFoundError"; + String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index c1faa5b7..b98b7e88 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -57,22 +57,33 @@ public class FunctionCallback implements Callback { private final Future future; private final Module module; private final List annotations; - private final MethodType methodType; - - public FunctionCallback(Future future, Module module, Object[] annotations, MethodType methodType) { + public FunctionCallback(Future future, Module module, MethodType methodType) { this.future = future; this.module = module; - this.annotations = new ArrayList<>(); this.methodType = methodType; - for (Object o : annotations) { - BString annotation = (BString) o; - String[] split = annotation.getValue().split(":"); - this.annotations.add(split[split.length - 1]); + this.annotations = new ArrayList<>(); + BMap annotations = (BMap) methodType.getAnnotation(StringUtils.fromString("$returns$")); + if (annotations != null) { + for (BString annotation : annotations.getKeys()) { + String[] split = annotation.getValue().split(":"); + this.annotations.add(split[split.length - 1]); + } + } + } + + private String getOutputAnnotation() { + if (this.annotations.size() == 0) { + if (methodType instanceof ResourceMethodType) { + return Constants.HTTP_OUTPUT; + } + //TODO impl compiler ext validations to make sure output annotations exists } + return this.annotations.get(0); } + @Override public void notifySuccess(Object result) { if (result instanceof BError) { @@ -96,10 +107,7 @@ public void notifySuccess(Object result) { return; } - String outputBinding = ""; - if (annotations.size() != 0) { - outputBinding = this.annotations.get(0); - } + String outputBinding = getOutputAnnotation(); if (Constants.QUEUE_OUTPUT.equals(outputBinding) || Constants.COSMOS_DBOUTPUT.equals(outputBinding)) { mapValue.put(StringUtils.fromString(Constants.OUT_MSG), result); @@ -111,11 +119,11 @@ public void notifySuccess(Object result) { mapValue.put(StringUtils.fromString("outMsg"), encodedString); } - } else if ("".equals(outputBinding) || Constants.HTTP_OUTPUT.equals(outputBinding)) { - if (isHTTPResponse(result)) { - handleHTTPResponse((BMap) result, mapValue); + } else if (outputBinding == null || Constants.HTTP_OUTPUT.equals(outputBinding)) { + if (isHTTPStatusCodeResponse(result)) { + handleStatusCodeResponse((BMap) result, mapValue); } else { - handleNonHTTPResponse(result, mapValue); + handleNonStatusCodeResponse(result, mapValue); } } future.complete(mapValue); @@ -139,7 +147,7 @@ private boolean isModuleDefinedError(BError error) { return Constants.PACKAGE_ORG.equals(orgName) && Constants.PACKAGE_NAME.equals(packageName); } - private boolean isHTTPResponse(Object result) { + private boolean isHTTPStatusCodeResponse(Object result) { Module resultPkg = TypeUtils.getType(result).getPackage(); return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))) && Constants.PACKAGE_ORG.equals(resultPkg.getOrg()) && @@ -150,7 +158,7 @@ private boolean isHTTPResponse(Object result) { private boolean isContentTypeExist(BMap headersMap) { for (BString headerKey : headersMap.getKeys()) { - if (headerKey.getValue().toLowerCase(Locale.ROOT).equals(Constants.CONTENT_TYPE)) { + if (headerKey.getValue().toLowerCase(Locale.ROOT).equals(Constants.CONTENT_TYPE.toLowerCase(Locale.ROOT))) { return true; } } @@ -183,8 +191,8 @@ private void addContentTypeImplicitly(Object result, BMap headers) { } else if (result instanceof BArray) { BArray arrayResult = (BArray) result; if (Constants.BYTE_TYPE.equals(arrayResult.getElementType().getName())) { - headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), - StringUtils.fromString(Constants.APPLICATION_OCTET_STREAM)); + headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), + StringUtils.fromString(Constants.APPLICATION_OCTET_STREAM)); } else if (Constants.MAP_TYPE.equals(arrayResult.getElementType().getName())) { MapType mapContent = (MapType) arrayResult.getElementType(); @@ -192,7 +200,7 @@ private void addContentTypeImplicitly(Object result, BMap headers) { headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString(Constants.APPLICATION_JSON)); } - + } else if (Constants.TABLE_TYPE.equals(arrayResult.getElementType().getName())) { TableType tableContent = (TableType) arrayResult.getElementType(); if (Constants.MAP_TYPE.equals(tableContent.getConstrainedType().getName())) { @@ -227,7 +235,7 @@ private void addContentTypeImplicitly(Object result, BMap headers) { } - private void handleNonHTTPResponse(Object result, BMap mapValue) { + private void handleNonStatusCodeResponse(Object result, BMap mapValue) { BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); Object headers = @@ -241,7 +249,7 @@ private void handleNonHTTPResponse(Object result, BMap mapValue mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } - private void handleHTTPResponse(BMap result, BMap mapValue) { + private void handleStatusCodeResponse(BMap result, BMap mapValue) { BMap resultMap = result; // Extract status code diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index e772eb91..371b364e 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -17,15 +17,20 @@ */ package io.ballerina.stdlib.azure.functions; +import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; +import io.ballerina.stdlib.azure.functions.exceptions.InvalidPayloadException; +import io.ballerina.stdlib.azure.functions.exceptions.PayloadNotFoundException; import java.util.ArrayList; import java.util.Arrays; @@ -145,7 +150,8 @@ private PathParameter[] getPathParams(ResourceMethodType resourceMethod, BMap processPayloadParam(ResourceMethodType resourceMethod, BMap body) { + private Optional processPayloadParam(ResourceMethodType resourceMethod, BMap body) + throws PayloadNotFoundException { Parameter[] parameters = resourceMethod.getParameters(); for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { Parameter parameter = parameters[i]; @@ -163,23 +169,58 @@ private Optional processPayloadParam(ResourceMethodType resour continue; } BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); - BMap headers = body.getMapValue(StringUtils.fromString("httpPayload")) - .getMapValue(StringUtils.fromString("Headers")); - BString contentType = headers.getArrayValue(StringUtils.fromString("Content-Type")).getBString(0); - BString bodyValue = httpPayload.getStringValue(StringUtils.fromString("Body")); - //TODO handle payload 400 + BMap headers = httpPayload.getMapValue(StringUtils.fromString("Headers")); Type type = parameter.type; - AbstractPayloadBuilder builder = AbstractPayloadBuilder.getBuilder(contentType.getValue(), type); - Object bValue = builder.getValue(bodyValue, false); -// Object bValue = Utilities.convertJsonToDataBoundParamValue(bodyValue, type); -// str.createValue(type, false); -// Object bValue = Utilities.convertJsonToDataBoundParamValue(bodyValue, type); -// Object bValue = builder.getValue((BObject) body1, false); - //TODO handle other types records n stuff - return Optional.of(new PayloadParameter(i, parameter, bValue)); + String contentType = getContentTypeHeader(headers); + BString bodyValue = getRequestBody(httpPayload, name, type); + if (isNilType(type) && bodyValue == null) { + return Optional.of(new PayloadParameter(i, parameter, null)); + } + try { + AbstractPayloadBuilder builder = AbstractPayloadBuilder.getBuilder(contentType, type); + Object bValue = builder.getValue(bodyValue, false); + return Optional.of(new PayloadParameter(i, parameter, bValue)); + } catch (BError error) { + throw new InvalidPayloadException(error.getMessage()); + } } return Optional.empty(); } + + private boolean isNilType(Type type) { + if (type.getTag() == TypeTags.UNION_TAG) { + List memberTypes = ((UnionType) type).getMemberTypes(); + for (Type memberType : memberTypes) { + if (isNilType(memberType)) { + return true; + } + } + } else if (type.getTag() == TypeTags.NULL_TAG) { + return true; + } + return false; + } + + private BString getRequestBody(BMap httpPayload, String name, Type type) throws PayloadNotFoundException { + BString bBody = StringUtils.fromString("Body"); + if (httpPayload.containsKey(bBody)) { + return httpPayload.getStringValue(bBody); + } + if (!isNilType(type)) { + throw new PayloadNotFoundException("payload not found for the variable '" + name + "'"); + } + return null; + } + + private String getContentTypeHeader(BMap headers) { + //TODO fix lower case + if (headers.containsKey(StringUtils.fromString(Constants.CONTENT_TYPE))) { + BArray headersArrayValue = headers.getArrayValue(StringUtils.fromString(Constants.CONTENT_TYPE)); + return headersArrayValue.getBString(0).getValue(); + } else { + return null; + } + } public Object[] getArgList() { List parameters = new ArrayList<>(Arrays.asList(pathParams)); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index b93e7e77..519037dc 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -31,6 +31,7 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.exceptions.BadRequestException; import java.util.ArrayList; import java.util.List; @@ -65,7 +66,6 @@ public static Object callNativeMethod(Environment env, BObject adaptor, BMap bod return invokeResourceFunction(env, bHubService, "callNativeMethod", body, functionName); } - private static Object invokeResourceFunction(Environment env, BObject bHubService, String parentFunctionName, BMap body, BString functionName) { @@ -74,27 +74,32 @@ private static Object invokeResourceFunction(Environment env, BObject bHubServic StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), parentFunctionName); ServiceType serviceType = (ServiceType) bHubService.getType(); + ResourceMethodType[] resourceMethods = serviceType.getResourceMethods(); //TODO restrict "httpPayload" from the param names. - ResourceMethodType resourceMethod = - getResourceMethodType(resourceMethods, functionName).orElseThrow(); - HttpResource httpResource = new HttpResource(resourceMethod, body); - Object[] args = httpResource.getArgList(); -// Object[] args = new Object[1]; - BMap annotation = (BMap) resourceMethod.getAnnotation(StringUtils.fromString("$returns$")); - if (annotation == null) { - annotation = ValueCreator.createMapValue(); + Optional resourceMethodType = getResourceMethodType(resourceMethods, functionName); + if (resourceMethodType.isEmpty()) { + balFuture.complete(Utils.createError(module, "function " + functionName.getValue() + " not found in the " + + "code", Constants.FUNCTION_NOT_FOUND_ERROR)); + return null; } - if (serviceType.isIsolated() && resourceMethod.isIsolated()) { - env.getRuntime().invokeMethodAsyncConcurrently( - bHubService, resourceMethod.getName(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys(), resourceMethod), null, - PredefinedTypes.TYPE_NULL, args); - } else { - env.getRuntime().invokeMethodAsyncSequentially( - bHubService, resourceMethod.getName(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys(), resourceMethod), null, - PredefinedTypes.TYPE_NULL, args); + ResourceMethodType resourceMethod = resourceMethodType.get(); + try { + HttpResource httpResource = new HttpResource(resourceMethod, body); + Object[] args = httpResource.getArgList(); + if (serviceType.isIsolated() && resourceMethod.isIsolated()) { + env.getRuntime().invokeMethodAsyncConcurrently( + bHubService, resourceMethod.getName(), null, metadata, + new FunctionCallback(balFuture, module, resourceMethod), null, PredefinedTypes.TYPE_NULL, + args); + } else { + env.getRuntime().invokeMethodAsyncSequentially( + bHubService, resourceMethod.getName(), null, metadata, + new FunctionCallback(balFuture, module, resourceMethod), null, PredefinedTypes.TYPE_NULL, + args); + } + } catch (BadRequestException e) { + balFuture.complete(Utils.createError(module, e.getMessage(), e.getType())); } return null; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index d098591a..127cb0ab 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -102,17 +102,16 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, } } Object[] args = argList.toArray(); - BMap annotation = (BMap) methodType.getAnnotation(StringUtils.fromString("$returns$")); if (serviceType.isIsolated()) { env.getRuntime().invokeMethodAsyncConcurrently( bHubService, remoteFuncName.getValue(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys(), methodType), null, - PredefinedTypes.TYPE_NULL, args); + new FunctionCallback(balFuture, module, methodType), null, PredefinedTypes.TYPE_NULL, + args); } else { env.getRuntime().invokeMethodAsyncSequentially( bHubService, remoteFuncName.getValue(), null, metadata, - new FunctionCallback(balFuture, module, annotation.getKeys(), methodType), null, - PredefinedTypes.TYPE_NULL, args); + new FunctionCallback(balFuture, module, methodType), null, PredefinedTypes.TYPE_NULL, + args); } return null; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java new file mode 100644 index 00000000..40eba301 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; + +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BString; + +import static io.ballerina.runtime.api.utils.StringUtils.fromString; + +/** + * Contains Utilities related to natives. + * + * @since 2.0.0 + */ +public class Utils { + public static BError createError(Module module, String message, String type) { + BString errorMessage = fromString(message); + return ErrorCreator.createError(module, type, errorMessage, ErrorCreator.createError(errorMessage), null); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/BadRequestException.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/BadRequestException.java new file mode 100644 index 00000000..7c1ec4a4 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/BadRequestException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.exceptions; + +/** + * Represents Bad Request related exceptions. + * + * @since 2.0.0 + */ +public abstract class BadRequestException extends RuntimeException { + private String type; + + public BadRequestException(String message, String type) { + super(message); + this.type = type; + } + + public String getType() { + return type; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/FunctionNotFoundException.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/FunctionNotFoundException.java new file mode 100644 index 00000000..a016ad25 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/FunctionNotFoundException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.exceptions; + +import io.ballerina.stdlib.azure.functions.Constants; + +/** + * Represents the exception thrown when theres no functions to be routed. + * + * @since 2.0.0 + */ +public class FunctionNotFoundException extends BadRequestException { + + public FunctionNotFoundException(String message) { + super(message, Constants.FUNCTION_NOT_FOUND_ERROR); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/InvalidPayloadException.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/InvalidPayloadException.java new file mode 100644 index 00000000..1ef38297 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/InvalidPayloadException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.exceptions; + +import io.ballerina.stdlib.azure.functions.Constants; + +/** + * Represents an invalid payload type received from the request. + * + * @since 2.0.0 + */ +public class InvalidPayloadException extends BadRequestException { + + public InvalidPayloadException(String message) { + super(message, Constants.INVALID_PAYLOAD_ERROR); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/PayloadNotFoundException.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/PayloadNotFoundException.java new file mode 100644 index 00000000..4ba9e210 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/PayloadNotFoundException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.exceptions; + +import io.ballerina.stdlib.azure.functions.Constants; + +/** + * Represents when the expected payload is not found. + * + * @since 2.0.0 + */ +public class PayloadNotFoundException extends BadRequestException { + + public PayloadNotFoundException(String message) { + super(message, Constants.PAYLOAD_NOT_FOUND_ERROR); + } +} diff --git a/spec/spec.md b/spec/spec.md index 57767a3a..e136c38a 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -68,7 +68,7 @@ public type HTTPTriggerConfiguration record {| |}; ``` -If an output Binding annotation is specified, HTTPOutput will be taken implicitly. +If an output Binding annotation is specified, HttpOutput will be taken implicitly. #### 2.2.2. Service base path @@ -312,7 +312,7 @@ record| |❌|❌|✅|❌|❌ | |map\| ❌ | ❌ | ✅|❌|❌ | |table\| ❌ | ❌ | ✅|❌|❌ ```bal - resource function post query(string name, @af:Payload string greeting) returns @af:HTTPOutput string|error { + resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { return "Hello from the query " + greeting + " " + name; } ``` From c14ae4208b51d332fdf3b32f97e3bb8d62fa7c8a Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Fri, 12 Aug 2022 09:01:31 +0530 Subject: [PATCH 15/59] Add base64 encoded http raw response --- ballerina/http_service.bal | 1 + .../stdlib/azure/functions/FunctionCallback.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ballerina/http_service.bal b/ballerina/http_service.bal index 543f0208..0f88be96 100644 --- a/ballerina/http_service.bal +++ b/ballerina/http_service.bal @@ -34,6 +34,7 @@ isolated service class ResourceService { string functionName = payload.Metadata.sys.MethodName; map|error callRegisterMethod = self.adaptor.callNativeMethod(payload.Data, functionName); response.setJsonPayload(getResponsePayload(callRegisterMethod)); + //io:println(getResponsePayload(callRegisterMethod)); check caller->respond(response); } } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index b98b7e88..968d9a84 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -25,6 +25,7 @@ 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.ByteType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ResourceMethodType; @@ -245,7 +246,18 @@ private void handleNonStatusCodeResponse(Object result, BMap ma addContentTypeImplicitly(result, (BMap) headers); respMap.put(StringUtils.fromString(Constants.HEADERS), headers); - respMap.put(StringUtils.fromString(Constants.BODY), result); + if (result instanceof BArray) { + BArray arrayResult = (BArray) result; + if (Constants.BYTE_TYPE.equals(arrayResult.getElementType().getName())) { + respMap.put(StringUtils.fromString(Constants.BODY), ToBase64.toBase64(arrayResult)); +// respMap.put(StringUtils.fromString(Constants.BODY), arrayResult); +// respMap.put(StringUtils.fromString("isRaw"), true); + } else { + respMap.put(StringUtils.fromString(Constants.BODY), result); + } + } else { + respMap.put(StringUtils.fromString(Constants.BODY), result); + } mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } From 72928c3727a0f94135edb3b400ce49cc9cca7621 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 15 Aug 2022 12:08:20 +0530 Subject: [PATCH 16/59] Fix blob datatypes --- ballerina-tests/tests/test.bal | 3 ++- .../org/ballerinax/azurefunctions/test/HandlerTest.java | 9 ++++----- .../azurefunctions/service/blob/BlobOutputBinding.java | 2 +- .../azurefunctions/service/blob/BlobTriggerBinding.java | 3 +-- .../stdlib/azure/functions/FunctionCallback.java | 1 - 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index e92c9639..c4ec2ccc 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -218,7 +218,8 @@ function nonHttpResTest3() returns error? { string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest3"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest3", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/octet-stream"}, "body":[200, 241, 218, 202, 212, 96, 36, 248, 62, 66, 56, 242, 149, 65, 218, 144, 76, 51, 213, 245, 143, 199, 252, 23, 9, 110, 53, 37, 41, 170, 97, 111]}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", + "headers":{"Content-Type":"application/octet-stream"}, "body":"yPHaytRgJPg+QjjylUHakEwz1fWPx/wXCW41JSmqYW8="}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java index 159604a9..b0a6c81d 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -179,11 +179,10 @@ public void testTimerTrigger() { @Test public void testBlobTrigger() { JsonObject actual = generatedFunctions.get("blob"); - String str = - "{\"bindings\":[{\"type\":\"blobTrigger\",\"name\":\"blobIn\",\"direction\":\"in\",\"path\":" + - "\"bpath1/{name}\",\"dataType\":\"binary\",\"connection\":\"AzureWebJobsStorage\"}," + - "{\"type\":\"blob\",\"direction\":\"out\",\"name\":\"outMsg\",\"path\":\"bpath1/newBlob\"," + - "\"connection\":\"AzureWebJobsStorage\",\"dataType\":\"binary\"}]}"; + String str = "{\"bindings\":[{\"type\":\"blobTrigger\",\"name\":\"blobIn\",\"direction\":\"in\"," + + "\"path\":\"bpath1/{name}\",\"connection\":\"AzureWebJobsStorage\"},{\"type\":\"blob\"," + + "\"direction\":\"out\",\"name\":\"outMsg\",\"path\":\"bpath1/newBlob\"," + + "\"connection\":\"AzureWebJobsStorage\",\"dataType\":\"string\"}]}"; JsonElement parse = jsonParser.parse(str); Assert.assertEquals(actual, parse); } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java index 73aaefeb..223b726e 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java @@ -20,7 +20,7 @@ public class BlobOutputBinding extends OutputBinding { private String path; private String connection = "AzureWebJobsStorage"; - private String dataType = "binary"; + private String dataType = "string"; public BlobOutputBinding(AnnotationNode annotationNode) { super("blob"); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java index b4099e32..d7db3e2a 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java @@ -20,7 +20,7 @@ public class BlobTriggerBinding extends RemoteTriggerBinding { private String path; private String connection = "AzureWebJobsStorage"; - private String dataType = "binary"; + private String dataType = "string"; public BlobTriggerBinding(ServiceDeclarationNode serviceDeclarationNode, SemanticModel semanticModel) { super("blobTrigger", "onUpdated", Constants.ANNOTATION_BLOB_TRIGGER, serviceDeclarationNode, @@ -77,7 +77,6 @@ public JsonObject getJsonObject() { inputTrigger.addProperty("name", this.getVarName()); inputTrigger.addProperty("direction", this.getDirection()); inputTrigger.addProperty("path", this.path); - inputTrigger.addProperty("dataType", this.dataType); inputTrigger.addProperty("connection", this.connection); return inputTrigger; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 968d9a84..624d4528 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -25,7 +25,6 @@ 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.ByteType; import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ResourceMethodType; From dc3d184742f1374a8828e5df432433b8c9f7b63a Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 16 Aug 2022 17:58:45 +0530 Subject: [PATCH 17/59] Add query param support --- ballerina-tests/main.bal | 49 ++++++- .../resources/http-query-blob-input.json | 122 ++++++++++++++++++ .../http-query-blob-optional-input.json | 122 ++++++++++++++++++ .../resources/query-arr-optional-without.json | 59 +++++++++ .../tests/resources/query-arr.json | 64 +++++++++ .../tests/resources/query-bool.json | 64 +++++++++ .../tests/resources/query-float.json | 64 +++++++++ .../tests/resources/query-optional-with.json | 113 ++++++++++++++++ .../resources/query-optional-without.json | 108 ++++++++++++++++ ballerina-tests/tests/test.bal | 22 ++++ .../service/blob/BlobInputBinding.java | 2 +- native/spotbugs-exclude.xml | 4 + .../stdlib/azure/functions/HttpResource.java | 94 ++++++++------ .../azure/functions/NativeRemoteAdapter.java | 30 ++--- .../stdlib/azure/functions/ParamHandler.java | 49 ++++--- .../functions/bindings/input/BlobInput.java | 39 ++++++ .../functions/bindings/input/CosmosInput.java | 39 ++++++ .../bindings/input/InputBinding.java | 41 ++++++ .../functions/builder/JsonPayloadBuilder.java | 31 ++++- 19 files changed, 1027 insertions(+), 89 deletions(-) create mode 100644 ballerina-tests/tests/resources/http-query-blob-input.json create mode 100644 ballerina-tests/tests/resources/http-query-blob-optional-input.json create mode 100644 ballerina-tests/tests/resources/query-arr-optional-without.json create mode 100644 ballerina-tests/tests/resources/query-arr.json create mode 100644 ballerina-tests/tests/resources/query-bool.json create mode 100644 ballerina-tests/tests/resources/query-float.json create mode 100644 ballerina-tests/tests/resources/query-optional-with.json create mode 100644 ballerina-tests/tests/resources/query-optional-without.json create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/BlobInput.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/CosmosInput.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/InputBinding.java diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 7c4b12f0..2ec0dcf4 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -72,10 +72,6 @@ service /hello on ep { return "Hello from foo bar res"; } - resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { - return "Hello from the query " + greeting + " " + name; - } - resource function post db(@af:Payload string greeting, @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", @@ -218,6 +214,51 @@ service /hello on ep { resource function post nonReturnTest1() { } + + resource function get blobInput(@af:Payload string greeting, string name, @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) returns string|error { + if blobIn is byte[] { + string content = check string:fromBytes(blobIn); + return "Blob from " + name + ", content is " + content; + } else { + return "Blob from "+ name + " not found"; + } + } + + resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { + return "Hello from the query " + greeting + " " + name; + } + + resource function get query/optional(string? name) returns string|error { + if (name is string) { + return "Hello from the query " + name; + } else { + return "Query not found but all good ;)"; + } + } + + resource function get query/bool(boolean name) returns string|error { + if (name is string) { + return "Hello from the query " + name; + } else { + return "Query not found but all good ;)"; + } + } + + resource function get query/arr(string[] name) returns string|error { + if (name is string) { + return "Hello from the query " + name; + } else { + return "Query not found but all good ;)"; + } + } + + resource function get query/arrOrNil(string[]|() name) returns string|error { + if (name is string) { + return "Hello from the query " + name; + } else { + return "Query not found but all good ;)"; + } + } } @af:QueueTrigger { diff --git a/ballerina-tests/tests/resources/http-query-blob-input.json b/ballerina-tests/tests/resources/http-query-blob-input.json new file mode 100644 index 00000000..5bab970e --- /dev/null +++ b/ballerina-tests/tests/resources/http-query-blob-input.json @@ -0,0 +1,122 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/blobInput?name=hello.txt", + "Method": "GET", + "Query": { + "name": "hello.txt" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "text/plain" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "4c33f9fa-9126-4f46-a34a-e328a2b5d192" + ], + "X-ARR-LOG-ID": [ + "4dd64a38-fed1-42d2-b765-269065bd8a4f" + ], + "CLIENT-IP": [ + "10.0.32.12:47555" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.76:48146" + ], + "X-Original-URL": [ + "/api/hello/blobInput?name=hello.txt" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/blobInput?name=hello.txt" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + }, + "blobIn": "\"hello from byte\\n\"" + }, + "Metadata": { + "name": "\"hello.txt\"", + "Query": { + "name": "hello.txt" + }, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "4", + "Content-Type": "text/plain", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "4c33f9fa-9126-4f46-a34a-e328a2b5d192", + "X-ARR-LOG-ID": "4dd64a38-fed1-42d2-b765-269065bd8a4f", + "CLIENT-IP": "10.0.32.12:47555", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.76:48146", + "X-Original-URL": "/api/hello/blobInput?name=hello.txt", + "X-WAWS-Unencoded-URL": "/api/hello/blobInput?name=hello.txt", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "get-hello-blobInput", + "UtcNow": "2022-08-15T07:52:01.8861913Z", + "RandGuid": "fa58f25f-74af-40e7-83a0-029a4331e9a7" + } + } +} diff --git a/ballerina-tests/tests/resources/http-query-blob-optional-input.json b/ballerina-tests/tests/resources/http-query-blob-optional-input.json new file mode 100644 index 00000000..db64c352 --- /dev/null +++ b/ballerina-tests/tests/resources/http-query-blob-optional-input.json @@ -0,0 +1,122 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/blobInput?name=hello1.txt", + "Method": "GET", + "Query": { + "name": "hello1.txt" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "text/plain" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "01ebc672-cde0-4cc4-a5df-77de30196d8d" + ], + "X-ARR-LOG-ID": [ + "27b8e1d3-8468-4c53-a28c-7f84874488d3" + ], + "CLIENT-IP": [ + "10.0.32.17:43005" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.76:48828" + ], + "X-Original-URL": [ + "/api/hello/blobInput?name=hello1.txt" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/blobInput?name=hello1.txt" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + }, + "blobIn": "null" + }, + "Metadata": { + "name": "\"hello1.txt\"", + "Query": { + "name": "hello1.txt" + }, + "Headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Content-Length": "4", + "Content-Type": "text/plain", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "01ebc672-cde0-4cc4-a5df-77de30196d8d", + "X-ARR-LOG-ID": "27b8e1d3-8468-4c53-a28c-7f84874488d3", + "CLIENT-IP": "10.0.32.17:43005", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.76:48828", + "X-Original-URL": "/api/hello/blobInput?name=hello1.txt", + "X-WAWS-Unencoded-URL": "/api/hello/blobInput?name=hello1.txt", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "get-hello-blobInput", + "UtcNow": "2022-08-15T08:50:15.0529418Z", + "RandGuid": "8b11d3f8-ed35-4cbb-aeda-deeea3ef07c2" + } + } +} diff --git a/ballerina-tests/tests/resources/query-arr-optional-without.json b/ballerina-tests/tests/resources/query-arr-optional-without.json new file mode 100644 index 00000000..30a814c8 --- /dev/null +++ b/ballerina-tests/tests/resources/query-arr-optional-without.json @@ -0,0 +1,59 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/query/arr", + "Method": "GET", + "Query": {}, + "Headers": { + "Connection": [ + "keep-alive" + ], + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "49b7bbcb-bd4f-4f2c-a897-ce35236acdfe" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Host": "localhost:7071", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "49b7bbcb-bd4f-4f2c-a897-ce35236acdfe" + }, + "sys": { + "MethodName": "get-hello-query-arr", + "UtcNow": "2022-08-16T10:38:59.2405064Z", + "RandGuid": "5de1fdb9-ed9e-43a1-80ca-57e596a2b146" + } + } +} diff --git a/ballerina-tests/tests/resources/query-arr.json b/ballerina-tests/tests/resources/query-arr.json new file mode 100644 index 00000000..3cb30930 --- /dev/null +++ b/ballerina-tests/tests/resources/query-arr.json @@ -0,0 +1,64 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/query/arr?name=red&name=green", + "Method": "GET", + "Query": { + "name": "red,green" + }, + "Headers": { + "Connection": [ + "keep-alive" + ], + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "5de49357-5322-41f2-9550-8d85df57c66b" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "name": "\"green\"", + "Query": { + "name": "green" + }, + "Headers": { + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Host": "localhost:7071", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "5de49357-5322-41f2-9550-8d85df57c66b" + }, + "sys": { + "MethodName": "get-hello-query-arr", + "UtcNow": "2022-08-16T10:37:07.6777821Z", + "RandGuid": "3ae73282-ec8d-4f0e-8e9e-4f3e0602b175" + } + } +} diff --git a/ballerina-tests/tests/resources/query-bool.json b/ballerina-tests/tests/resources/query-bool.json new file mode 100644 index 00000000..5b5f51e5 --- /dev/null +++ b/ballerina-tests/tests/resources/query-bool.json @@ -0,0 +1,64 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/query/bool?name=false", + "Method": "GET", + "Query": { + "name": "false" + }, + "Headers": { + "Connection": [ + "keep-alive" + ], + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "1854bfc5-0e04-4793-ac51-b9dba41d0272" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "name": "\"false\"", + "Query": { + "name": "false" + }, + "Headers": { + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Host": "localhost:7071", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "1854bfc5-0e04-4793-ac51-b9dba41d0272" + }, + "sys": { + "MethodName": "get-hello-query-bool", + "UtcNow": "2022-08-16T10:26:19.8474115Z", + "RandGuid": "74449761-f557-450c-b969-a48b5647dcf1" + } + } +} diff --git a/ballerina-tests/tests/resources/query-float.json b/ballerina-tests/tests/resources/query-float.json new file mode 100644 index 00000000..59f4f2d8 --- /dev/null +++ b/ballerina-tests/tests/resources/query-float.json @@ -0,0 +1,64 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/query/bool?name=10.5", + "Method": "GET", + "Query": { + "name": "10.5" + }, + "Headers": { + "Connection": [ + "keep-alive" + ], + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "0af627a1-73fe-4990-865d-6e438249ae98" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "name": "\"10.5\"", + "Query": { + "name": "10.5" + }, + "Headers": { + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Host": "localhost:7071", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "0af627a1-73fe-4990-865d-6e438249ae98" + }, + "sys": { + "MethodName": "get-hello-query-bool", + "UtcNow": "2022-08-16T10:28:47.6485592Z", + "RandGuid": "e31b37f3-08fe-4268-aabb-ff055227f24d" + } + } +} diff --git a/ballerina-tests/tests/resources/query-optional-with.json b/ballerina-tests/tests/resources/query-optional-with.json new file mode 100644 index 00000000..7ae0d70b --- /dev/null +++ b/ballerina-tests/tests/resources/query-optional-with.json @@ -0,0 +1,113 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/optional-query?name=test1", + "Method": "GET", + "Query": { + "name": "test1" + }, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "072319b5-49e5-4c56-859c-4fcc1a714811" + ], + "CLIENT-IP": [ + "10.0.32.4:29256" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.130.212:18792" + ], + "X-Original-URL": [ + "/api/hello/optional-query?name=test1" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/optional-query?name=test1" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "name": "\"test1\"", + "Query": { + "name": "test1" + }, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "072319b5-49e5-4c56-859c-4fcc1a714811", + "CLIENT-IP": "10.0.32.4:29256", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.130.212:18792", + "X-Original-URL": "/api/hello/optional-query?name=test1", + "X-WAWS-Unencoded-URL": "/api/hello/optional-query?name=test1", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "get-hello-optional-query", + "UtcNow": "2022-06-13T10:12:25.6088199Z", + "RandGuid": "80274c51-3807-49ae-bfbf-44c5b74fa4e9" + } + } +} diff --git a/ballerina-tests/tests/resources/query-optional-without.json b/ballerina-tests/tests/resources/query-optional-without.json new file mode 100644 index 00000000..26424a66 --- /dev/null +++ b/ballerina-tests/tests/resources/query-optional-without.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello/optional-query", + "Method": "GET", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "9" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "072319b5-49e5-4c56-859c-4fcc1a714811" + ], + "CLIENT-IP": [ + "10.0.32.4:29256" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.130.212:18792" + ], + "X-Original-URL": [ + "/api/hello/optional-query" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello/optional-query" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "9", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "072319b5-49e5-4c56-859c-4fcc1a714811", + "CLIENT-IP": "10.0.32.4:29256", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.130.212:18792", + "X-Original-URL": "/api/hello/optional-query", + "X-WAWS-Unencoded-URL": "/api/hello/optional-query", + "DISGUISED-HOST": "bal-dev.azurewebsites.net" + }, + "sys": { + "MethodName": "get-hello-optional-query", + "UtcNow": "2022-06-13T10:12:25.6088199Z", + "RandGuid": "80274c51-3807-49ae-bfbf-44c5b74fa4e9" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index c4ec2ccc..b2c854cd 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -574,3 +574,25 @@ function testOptionalPayloadWithoutPayload() returns error? { json expectedResp = {"Outputs":{"resp":{"statusCode":"201","headers":{"Content-Type":"text/plain"},"body":"Hello, the payload wasn't set but all good ;)"}},"Logs":[],"ReturnValue":null}; test:assertEquals(resp, expectedResp); } + +@test:Config { } +function testHttpBlobInput() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-query-blob-input.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-blobInput", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"200","headers":{"Content-Type":"text/plain"}, + "body":"Blob from hello.txt, content is hello from byte\n"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config { } +function testHttpBlobInputOptional() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-query-blob-optional-input.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-blobInput", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"200","headers":{"Content-Type":"text/plain"}, + "body":"Blob from hello1.txt not found"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java index 8d1e234f..4781b616 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java @@ -18,7 +18,7 @@ public class BlobInputBinding extends InputBinding { private String path; private String connection = "AzureWebJobsStorage"; - private String dataType = "binary"; + private String dataType = "string"; public BlobInputBinding(AnnotationNode queueTrigger, String varName) { super("blob"); diff --git a/native/spotbugs-exclude.xml b/native/spotbugs-exclude.xml index e695b8d7..5f29de4b 100644 --- a/native/spotbugs-exclude.xml +++ b/native/spotbugs-exclude.xml @@ -20,4 +20,8 @@ + + + + diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index 371b364e..f4544b6c 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -27,8 +27,9 @@ import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.bindings.input.InputBinding; import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; -import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; +import io.ballerina.stdlib.azure.functions.builder.StringPayloadBuilder; import io.ballerina.stdlib.azure.functions.exceptions.InvalidPayloadException; import io.ballerina.stdlib.azure.functions.exceptions.PayloadNotFoundException; @@ -60,23 +61,27 @@ public HttpResource(ResourceMethodType resourceMethodType, BMap body) { this.inputBindingParameters = getInputBindingParams(resourceMethodType, body); } - private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) { + private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) throws InvalidPayloadException { Parameter[] parameters = resourceMethod.getParameters(); List inputBindingParameters = new ArrayList<>(); for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { Parameter parameter = parameters[i]; String name = parameter.name; Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); - if (!ParamHandler.isInputAnnotationParam(annotation)) { + Optional inputBindingHandler = ParamHandler.getInputBindingHandler(annotation); + if (inputBindingHandler.isEmpty()) { continue; } BString bodyValue = body.getStringValue(StringUtils.fromString(name)); + InputBinding inputBinding = inputBindingHandler.get(); Type type = parameter.type; - JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(type); - Object bValue = jsonPayloadBuilder.getValue(bodyValue, false); -// Object bValue = Utilities.convertJsonToDataBoundParamValue(bodyValue, type); - //TODO handle other types records n stuff - inputBindingParameters.add(new InputBindingParameter(i, parameter, bValue)); + try { + AbstractPayloadBuilder payloadBuilder = inputBinding.getPayloadBuilder(type); + Object bValue = payloadBuilder.getValue(bodyValue, false); + inputBindingParameters.add(new InputBindingParameter(i, parameter, bValue)); + } catch (BError error) { + throw new InvalidPayloadException(error.getMessage()); + } } return inputBindingParameters.toArray(InputBindingParameter[]::new); @@ -91,45 +96,54 @@ private QueryParameter[] getQueryParams(ResourceMethodType resourceMethod, BMap< Parameter parameter = parameters[i]; String name = parameter.name; Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); - if (annotation != null) { //Add other annotations as well + //TODO Add other annotations as well + if (isAzAnnotationExist(annotation)) { continue; } - Object arr = queryParams.get(StringUtils.fromString(name)); - //TODO Handle optional null type -// if (arr == null) { -// + Object bValue = queryParams.get(StringUtils.fromString(name)); + +// //TODO Handle optional null type +//// if (queryParamValue == null) { +//// +//// } +// int tag = parameter.type.getTag(); +// Object bValue = null; +// switch (tag) { +// case STRING_TAG: +// //TODO error +//// if (!(queryParamValue instanceof BString)) { +//// +//// } +// bValue = queryParamValue; +// break; +// case ARRAY_TAG: +// //TODO handle other cases +// BArray values = (BArray) queryParamValue; +//// Type elementType = ((ArrayType) parameter.type).getElementType(); +// bValue = values; //TODO check all types +//// if (elementType.getTag() == STRING_TAG) { +//// BString[] bString = new BString[values.length]; +//// for (int j = 0, valuesLength = values.length; j < valuesLength; j++) { +//// String value = values[j]; +//// bString[j] =StringUtils.fromString(value); +//// bValue = ValueCreator.createArrayValue(bString); +//// } +//// } +// break; +// default: +// //TODO unsupported // } - int tag = parameter.type.getTag(); - Object bValue = null; - switch (tag) { - case STRING_TAG: - //TODO error -// if (!(arr instanceof BString)) { -// -// } - bValue = arr; - break; - case ARRAY_TAG: - //TODO handle other cases - BArray values = (BArray) arr; -// Type elementType = ((ArrayType) parameter.type).getElementType(); - bValue = values; //TODO check all types -// if (elementType.getTag() == STRING_TAG) { -// BString[] bString = new BString[values.length]; -// for (int j = 0, valuesLength = values.length; j < valuesLength; j++) { -// String value = values[j]; -// bString[j] =StringUtils.fromString(value); -// bValue = ValueCreator.createArrayValue(bString); -// } -// } - break; - default: - //TODO unsupported - } queryParameters.add(new QueryParameter(i, parameter, bValue)); } return queryParameters.toArray(QueryParameter[]::new); } + + private boolean isAzAnnotationExist(Object annotation) { + if (annotation == null) { + return false; + } + return true; + } private PathParameter[] getPathParams(ResourceMethodType resourceMethod, BMap body) { String[] resourcePath = resourceMethod.getResourcePath(); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index 127cb0ab..bd7a801f 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -32,6 +32,8 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.bindings.input.InputBinding; +import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; import io.ballerina.stdlib.azure.functions.builder.BinaryPayloadBuilder; import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; @@ -74,10 +76,7 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, Object bValue = getDataboundValue(data, parameter, serviceType); argList.add(bValue); argList.add(true); - continue; - } - - if (ParamHandler.isBindingNameParam(annotation)) { + } else if (ParamHandler.isBindingNameParam(annotation)) { BString nameParam = body.getMapValue(StringUtils.fromString("Metadata")).getStringValue(StringUtils.fromString( "name")); @@ -86,19 +85,16 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, Object bValue = jsonPayloadBuilder.getValue(nameParam, false); argList.add(bValue); argList.add(true); - continue; - } - - - //TODO check and process input binding variable - if (ParamHandler.isInputAnnotationParam(annotation)) { - BString bodyValue = data.getStringValue(StringUtils.fromString(name)); - Type type = parameter.type; - JsonPayloadBuilder jsonPayloadBuilder = new JsonPayloadBuilder(type); - Object bValue = jsonPayloadBuilder.getValue(bodyValue, false); - argList.add(bValue); - argList.add(true); - continue; + } else { + Optional inputBindingHandler = ParamHandler.getInputBindingHandler(annotation); + if (inputBindingHandler.isPresent()) { + BString bodyValue = data.getStringValue(StringUtils.fromString(name)); + Type type = parameter.type; + AbstractPayloadBuilder payloadBuilder = inputBindingHandler.get().getPayloadBuilder(type); + Object bValue = payloadBuilder.getValue(bodyValue, false); + argList.add(bValue); + argList.add(true); + } } } Object[] args = argList.toArray(); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 285b3a1b..db4093e6 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -20,22 +20,13 @@ import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.bindings.input.BlobInput; +import io.ballerina.stdlib.azure.functions.bindings.input.CosmosInput; +import io.ballerina.stdlib.azure.functions.bindings.input.InputBinding; -import java.util.Arrays; - -enum InputBindings { - COSMOS("CosmosDBInput"); - - private String annotation; - - InputBindings(String annotation) { - this.annotation = annotation; - } - - public String getAnnotation() { - return annotation; - } -} +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; /** * Represents the input binding handler. @@ -55,23 +46,29 @@ public static boolean isPayloadAnnotationParam(Object annotation) { Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:Payload")); return value instanceof Boolean; } - - public static boolean isInputAnnotationParam(Object annotation) { + + public static Optional getInputBindingHandler(Object annotation) { if (annotation == null) { - return false; + return Optional.empty(); } if (!(annotation instanceof BMap)) { - return false; + return Optional.empty(); } - for (Object key : ((BMap) annotation).getKeys()) { - if (key instanceof BString) { - String annotationKey = ((BString) key).getValue(); - String annotationName = annotationKey.substring(annotationKey.lastIndexOf(':') + 1); - return Arrays.stream(InputBindings.values()) - .anyMatch(name -> name.getAnnotation().equals(annotationName)); + for (BString key : ((BMap) annotation).getKeys()) { + String annotationKey = key.getValue(); + String annotationName = annotationKey.substring(annotationKey.lastIndexOf(':') + 1); + + List inputBindings = new ArrayList<>(); + inputBindings.add(new BlobInput()); + inputBindings.add(new CosmosInput()); + + for (InputBinding inputBinding : inputBindings) { + if (inputBinding.getName().equals(annotationName)) { + return Optional.of(inputBinding); + } } } - return false; + return Optional.empty(); } public static boolean isBindingNameParam(Object annotation) { diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/BlobInput.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/BlobInput.java new file mode 100644 index 00000000..77443a75 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/BlobInput.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.bindings.input; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; +import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; + +/** + * Represents the BlobInput Input Binding of azure functions. + * + * @since 2.0.0 + */ +public class BlobInput extends InputBinding { + + public BlobInput() { + super("BlobInput"); + } + + @Override + public AbstractPayloadBuilder getPayloadBuilder(Type type) { + return new JsonPayloadBuilder(type); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/CosmosInput.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/CosmosInput.java new file mode 100644 index 00000000..f51c2a09 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/CosmosInput.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.bindings.input; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; +import io.ballerina.stdlib.azure.functions.builder.JsonPayloadBuilder; + +/** + * Represents the CosmosDB Input in Azure functions input binding. + * + * @since 2.0.0 + */ +public class CosmosInput extends InputBinding { + + public CosmosInput() { + super("CosmosDBInput"); + } + + @Override + public AbstractPayloadBuilder getPayloadBuilder(Type type) { + return new JsonPayloadBuilder(type); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/InputBinding.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/InputBinding.java new file mode 100644 index 00000000..6ec2156b --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/bindings/input/InputBinding.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.bindings.input; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; + +/** + * Represents the base class for Input bindings in azure functions. + * + * @since 2.0.0 + */ +public abstract class InputBinding { + + private final String name; + + public InputBinding(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public abstract AbstractPayloadBuilder getPayloadBuilder(Type type); +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java index 666a6e1f..c9f59b6d 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java @@ -20,14 +20,19 @@ 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.Type; +import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BRefValue; import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.azure.functions.converter.JsonToRecordConverter; +import io.ballerina.stdlib.azure.functions.converter.StringToByteArrayConverter; import org.ballerinalang.langlib.value.FromJsonString; import org.ballerinalang.langlib.value.FromJsonWithType; +import java.util.List; + /** * The json type payload builder. * @@ -48,10 +53,34 @@ public Object getValue(BString dataSource, boolean readonly) { if (isSubtypeOfAllowedType(payloadType, TypeTags.RECORD_TYPE_TAG)) { return JsonToRecordConverter.convert(payloadType, obj, readonly); } + return createValue(this.payloadType, readonly, obj); + } + + public Object createValue(Type payloadType, boolean readonly, Object dataSource) { + // Object bjson = EntityBodyHandler.constructJsonDataSource(null); // EntityBodyHandler.addJsonMessageDataSource(null, bjson); // var result = FromJsonWithType.fromJsonWithType(bjson, ValueCreator.createTypedescValue(payloadType)); - var result = FromJsonWithType.fromJsonWithType(obj, ValueCreator.createTypedescValue(payloadType)); + if (dataSource instanceof BString) { + BString datasource = (BString) dataSource; + if (payloadType.getTag() == TypeTags.UNION_TAG) { + List memberTypes = ((UnionType) payloadType).getMemberTypes(); + for (Type memberType : memberTypes) { + try { + return createValue(memberType, readonly, datasource); + } catch (BError ignored) { + // thrown errors are ignored until all the types are iterated + } + } + } else if (payloadType.getTag() == TypeTags.ARRAY_TAG) { + ArrayType arrayType = (ArrayType) payloadType; + if (arrayType.getElementType().getTag() == TypeTags.BYTE_TAG) { + return StringToByteArrayConverter.convert(arrayType, datasource, readonly); + } + } + } + + var result = FromJsonWithType.fromJsonWithType(dataSource, ValueCreator.createTypedescValue(payloadType)); if (result instanceof BError) { throw (BError) result; } From a9e5255487cd5f7bc9cbe74b7ab2dd7421138860 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Thu, 18 Aug 2022 09:32:30 +0530 Subject: [PATCH 18/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 3f0fc2f0..d5301fe7 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -9,7 +9,7 @@ dependencies-toml-version = "2" [[package]] org = "ballerina" name = "auth" -version = "2.3.1" +version = "2.3.0" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "jballerina.java"}, @@ -104,7 +104,7 @@ modules = [ [[package]] org = "ballerina" name = "jwt" -version = "2.3.1" +version = "2.3.0" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -223,7 +223,7 @@ dependencies = [ [[package]] org = "ballerina" name = "oauth2" -version = "2.3.1" +version = "2.3.0" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -235,7 +235,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.0.5" +version = "1.0.4" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From 9e180ec897721a8b827843208a2ba6c9cc0701a8 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Thu, 18 Aug 2022 10:03:07 +0530 Subject: [PATCH 19/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index d5301fe7..3f0fc2f0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -9,7 +9,7 @@ dependencies-toml-version = "2" [[package]] org = "ballerina" name = "auth" -version = "2.3.0" +version = "2.3.1" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "jballerina.java"}, @@ -104,7 +104,7 @@ modules = [ [[package]] org = "ballerina" name = "jwt" -version = "2.3.0" +version = "2.3.1" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -223,7 +223,7 @@ dependencies = [ [[package]] org = "ballerina" name = "oauth2" -version = "2.3.0" +version = "2.3.1" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -235,7 +235,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.0.4" +version = "1.0.5" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From 28f15ac6d2ca597c95b9ccca9b28214338ae5dae Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Thu, 18 Aug 2022 14:23:40 +0530 Subject: [PATCH 20/59] Add query types support --- ballerina-tests/main.bal | 65 ++-- .../resources/query-arr-optional-with.json | 64 +++ .../resources/query-arr-optional-without.json | 4 +- .../tests/resources/query-float.json | 4 +- .../tests/resources/query-optional-with.json | 12 +- .../resources/query-optional-without.json | 19 +- ballerina-tests/tests/test.bal | 363 ++++++++++++++---- native/build.gradle | 5 + .../stdlib/azure/functions/HttpResource.java | 137 +++++-- native/src/main/java/module-info.java | 5 + 10 files changed, 509 insertions(+), 169 deletions(-) create mode 100644 ballerina-tests/tests/resources/query-arr-optional-with.json diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 2ec0dcf4..6c30d107 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -15,18 +15,18 @@ service /hello on ep { resource function default all() returns @af:HttpOutput string { return "Hello from all"; } - + resource function post optional/out(@af:Payload string greeting) returns string { return "Hello from optional output binding"; } - + resource function post optional/payload(@af:Payload string? greeting) returns string { if (greeting is string) { return "Hello, the payload found " + greeting; } return "Hello, the payload wasn't set but all good ;)"; } - + resource function post .(@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from . path "; } @@ -106,14 +106,14 @@ service /hello on ep { return string:fromBytes(greeting); } - resource function get err/empty/payload(@af:Payload string greeting) returns @af:HttpOutput string { + resource function get err/empty/payload(@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from get empty payload"; } - resource function post err/invalid/payload(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post err/invalid/payload(@af:Payload string greeting) returns @af:HttpOutput string { return "Hello from get invalid payload " + greeting; } - + resource function get httpAccessorTest() returns @af:HttpOutput string { return "Hello from all"; } @@ -137,7 +137,7 @@ service /hello on ep { resource function options httpAccessorTest() returns @af:HttpOutput string { return "Hello from all"; } - + resource function post httpResTest5() returns af:StatusCodeResponse { af:InternalServerError err = {}; return err; @@ -214,49 +214,53 @@ service /hello on ep { resource function post nonReturnTest1() { } - - resource function get blobInput(@af:Payload string greeting, string name, @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) returns string|error { + + resource function get blobInput(@af:Payload string greeting, string name, @af:BlobInput {path: "bpath1/{Query.name}"} byte[]? blobIn) returns string|error { if blobIn is byte[] { - string content = check string:fromBytes(blobIn); + string content = check string:fromBytes(blobIn); return "Blob from " + name + ", content is " + content; } else { - return "Blob from "+ name + " not found"; + return "Blob from " + name + " not found"; } } - + resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { return "Hello from the query " + greeting + " " + name; } resource function get query/optional(string? name) returns string|error { if (name is string) { - return "Hello from the query " + name; + return "Hello from the optional query " + name; } else { return "Query not found but all good ;)"; } } - + resource function get query/bool(boolean name) returns string|error { - if (name is string) { - return "Hello from the query " + name; - } else { - return "Query not found but all good ;)"; - } + return "Hello from the bool query " + name.toString(); + } + + resource function get query/floatt(float name) returns string|error { + return "Hello from the float query " + name.toString(); } - + resource function get query/arr(string[] name) returns string|error { - if (name is string) { - return "Hello from the query " + name; - } else { - return "Query not found but all good ;)"; + string out = ""; + foreach string i in name { + out += i + " "; } + return "Hello from the arr query " + out; } - + resource function get query/arrOrNil(string[]|() name) returns string|error { - if (name is string) { - return "Hello from the query " + name; + if (name is string[]) { + string out = ""; + foreach string i in name { + out += i + " "; + } + return "Hello from the arr or nil query " + out; } else { - return "Query not found but all good ;)"; + return "Query arr not found but all good ;)"; } } } @@ -312,8 +316,9 @@ service "queue-input" on queueListener1 { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated (@af:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { - path: "bpath1/newBlob" } byte[]|error { + remote function onUpdated(@af:Payload byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { + path: "bpath1/newBlob" + } byte[]|error { return blobIn; } } diff --git a/ballerina-tests/tests/resources/query-arr-optional-with.json b/ballerina-tests/tests/resources/query-arr-optional-with.json new file mode 100644 index 00000000..02c91495 --- /dev/null +++ b/ballerina-tests/tests/resources/query-arr-optional-with.json @@ -0,0 +1,64 @@ +{ + "Data": { + "httpPayload": { + "Url": "http://localhost:7071/api/hello/query/arrOrNil?name=red&name=green", + "Method": "GET", + "Query": { + "name": "red,green" + }, + "Headers": { + "Connection": [ + "keep-alive" + ], + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate, br" + ], + "Host": [ + "localhost:7071" + ], + "User-Agent": [ + "PostmanRuntime/7.26.8" + ], + "Postman-Token": [ + "5de49357-5322-41f2-9550-8d85df57c66b" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ] + } + }, + "Metadata": { + "name": "\"green\"", + "Query": { + "name": "green" + }, + "Headers": { + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Host": "localhost:7071", + "User-Agent": "PostmanRuntime/7.26.8", + "Postman-Token": "5de49357-5322-41f2-9550-8d85df57c66b" + }, + "sys": { + "MethodName": "get-hello-query-arrOrNil", + "UtcNow": "2022-08-16T10:37:07.6777821Z", + "RandGuid": "3ae73282-ec8d-4f0e-8e9e-4f3e0602b175" + } + } +} diff --git a/ballerina-tests/tests/resources/query-arr-optional-without.json b/ballerina-tests/tests/resources/query-arr-optional-without.json index 30a814c8..00d50c13 100644 --- a/ballerina-tests/tests/resources/query-arr-optional-without.json +++ b/ballerina-tests/tests/resources/query-arr-optional-without.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/query/arr", + "Url": "http://localhost:7071/api/hello/query/arrOrNil", "Method": "GET", "Query": {}, "Headers": { @@ -51,7 +51,7 @@ "Postman-Token": "49b7bbcb-bd4f-4f2c-a897-ce35236acdfe" }, "sys": { - "MethodName": "get-hello-query-arr", + "MethodName": "get-hello-query-arrOrNil", "UtcNow": "2022-08-16T10:38:59.2405064Z", "RandGuid": "5de1fdb9-ed9e-43a1-80ca-57e596a2b146" } diff --git a/ballerina-tests/tests/resources/query-float.json b/ballerina-tests/tests/resources/query-float.json index 59f4f2d8..e7e55d8b 100644 --- a/ballerina-tests/tests/resources/query-float.json +++ b/ballerina-tests/tests/resources/query-float.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/query/bool?name=10.5", + "Url": "http://localhost:7071/api/hello/floatt/bool?name=10.5", "Method": "GET", "Query": { "name": "10.5" @@ -56,7 +56,7 @@ "Postman-Token": "0af627a1-73fe-4990-865d-6e438249ae98" }, "sys": { - "MethodName": "get-hello-query-bool", + "MethodName": "get-hello-query-floatt", "UtcNow": "2022-08-16T10:28:47.6485592Z", "RandGuid": "e31b37f3-08fe-4268-aabb-ff055227f24d" } diff --git a/ballerina-tests/tests/resources/query-optional-with.json b/ballerina-tests/tests/resources/query-optional-with.json index 7ae0d70b..08b912d4 100644 --- a/ballerina-tests/tests/resources/query-optional-with.json +++ b/ballerina-tests/tests/resources/query-optional-with.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/optional-query?name=test1", + "Url": "https://bal-dev.azurewebsites.net/api/hello/query/optional?name=test1", "Method": "GET", "Query": { "name": "test1" @@ -53,10 +53,10 @@ "112.134.130.212:18792" ], "X-Original-URL": [ - "/api/hello/optional-query?name=test1" + "/api/hello/query/optional?name=test1" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/optional-query?name=test1" + "/api/hello/query/optional?name=test1" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -100,12 +100,12 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.130.212:18792", - "X-Original-URL": "/api/hello/optional-query?name=test1", - "X-WAWS-Unencoded-URL": "/api/hello/optional-query?name=test1", + "X-Original-URL": "/api/hello/query/optional?name=test1", + "X-WAWS-Unencoded-URL": "/api/hello/query/optional?name=test1", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { - "MethodName": "get-hello-optional-query", + "MethodName": "get-hello-query-optional", "UtcNow": "2022-06-13T10:12:25.6088199Z", "RandGuid": "80274c51-3807-49ae-bfbf-44c5b74fa4e9" } diff --git a/ballerina-tests/tests/resources/query-optional-without.json b/ballerina-tests/tests/resources/query-optional-without.json index 26424a66..d57a18a8 100644 --- a/ballerina-tests/tests/resources/query-optional-without.json +++ b/ballerina-tests/tests/resources/query-optional-without.json @@ -1,9 +1,10 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/optional-query", + "Url": "https://bal-dev.azurewebsites.net/api/hello/query/optional", "Method": "GET", - "Query": {}, + "Query": { + }, "Headers": { "Accept": [ "*/*" @@ -51,10 +52,10 @@ "112.134.130.212:18792" ], "X-Original-URL": [ - "/api/hello/optional-query" + "/api/hello/query/optional" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/optional-query" + "/api/hello/query/optional" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -78,7 +79,9 @@ } }, "Metadata": { - "Query": {}, + "name": "\"test1\"", + "Query": { + }, "Headers": { "Accept": "*/*", "Content-Length": "4", @@ -95,12 +98,12 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.130.212:18792", - "X-Original-URL": "/api/hello/optional-query", - "X-WAWS-Unencoded-URL": "/api/hello/optional-query", + "X-Original-URL": "/api/hello/query/optional", + "X-WAWS-Unencoded-URL": "/api/hello/query/optional", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { - "MethodName": "get-hello-optional-query", + "MethodName": "get-hello-query-optional", "UtcNow": "2022-06-13T10:12:25.6088199Z", "RandGuid": "80274c51-3807-49ae-bfbf-44c5b74fa4e9" } diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index b2c854cd..1f223cef 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -4,7 +4,6 @@ import ballerina/lang.value; import ballerina/regex; import ballerina/test; - @test:Config {} function testDefault() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); @@ -20,7 +19,7 @@ function getHttpAccessorTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpAccessorTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","get"); + string replacedString = regex:replaceAll(readString, "(ACCESSOR_NAME)", "get"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/get-hello-httpAccessorTest", readJson); json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; @@ -32,7 +31,7 @@ function putHttpAccessorTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpAccessorTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","put"); + string replacedString = regex:replaceAll(readString, "(ACCESSOR_NAME)", "put"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/put-hello-httpAccessorTest", readJson); json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; @@ -44,7 +43,7 @@ function patchHttpAccessorTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpAccessorTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","patch"); + string replacedString = regex:replaceAll(readString, "(ACCESSOR_NAME)", "patch"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/patch-hello-httpAccessorTest", readJson); json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; @@ -56,7 +55,7 @@ function deleteHttpAccessorTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpAccessorTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","delete"); + string replacedString = regex:replaceAll(readString, "(ACCESSOR_NAME)", "delete"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/delete-hello-httpAccessorTest", readJson); json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; @@ -68,7 +67,7 @@ function headHttpAccessorTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpAccessorTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","head"); + string replacedString = regex:replaceAll(readString, "(ACCESSOR_NAME)", "head"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/head-hello-httpAccessorTest", readJson); json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; @@ -80,7 +79,7 @@ function optionsHttpAccessorTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpAccessorTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(ACCESSOR_NAME)","options"); + string replacedString = regex:replaceAll(readString, "(ACCESSOR_NAME)", "options"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/options-hello-httpAccessorTest", readJson); json expectedResp = {"Outputs": {"resp": {"statusCode": "200", "headers": {"Content-Type": "text/plain"}, "body": "Hello from all"}}, "Logs": [], "ReturnValue": null}; @@ -181,7 +180,7 @@ function nonHttpResTest1() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest1"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest1"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest1", readJson); json expectedResp = { @@ -203,10 +202,10 @@ function nonHttpResTest2() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest2"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest2"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest2", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/xml"}, "body":"The Lost World"}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "application/xml"}, "body": "The Lost World"}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -215,11 +214,20 @@ function nonHttpResTest3() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest3"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest3"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest3", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", - "headers":{"Content-Type":"application/octet-stream"}, "body":"yPHaytRgJPg+QjjylUHakEwz1fWPx/wXCW41JSmqYW8="}}, "Logs":[], "ReturnValue":null}; + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "201", + "headers": {"Content-Type": "application/octet-stream"}, + "body": "yPHaytRgJPg+QjjylUHakEwz1fWPx/wXCW41JSmqYW8=" + } + }, + "Logs": [], + "ReturnValue": null + }; test:assertEquals(resp, expectedResp); } @@ -228,10 +236,10 @@ function nonHttpResTest4() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest4"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest4"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest4", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "application/json"}, "body": 100}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -240,10 +248,10 @@ function nonHttpResTest6() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest6"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest6"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest6", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":100}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "application/json"}, "body": 100}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -252,10 +260,10 @@ function nonHttpResTest7() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest7"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest7"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest7", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":true}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "application/json"}, "body": true}}, "Logs": [], "ReturnValue": null}; test:assertEquals(resp, expectedResp); } @@ -264,15 +272,15 @@ function nonHttpResTest8() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest8"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest8"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest8", readJson); json expectedResp = { "Outputs": { "resp": { "statusCode": "201", - "body":{"a":{"b":12, "c":"helloworld"}}, - "headers": {"Content-Type":"application/json"} + "body": {"a": {"b": 12, "c": "helloworld"}}, + "headers": {"Content-Type": "application/json"} } }, "Logs": [], @@ -286,15 +294,15 @@ function nonHttpResTest9() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest9"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest9"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest9", readJson); json expectedResp = { "Outputs": { "resp": { "statusCode": "201", - "body":[{"a":{"b":12, "c":"helloworld"}}, {"b":1100}], - "headers": {"Content-Type":"application/json"} + "body": [{"a": {"b": 12, "c": "helloworld"}}, {"b": 1100}], + "headers": {"Content-Type": "application/json"} } }, "Logs": [], @@ -308,15 +316,15 @@ function nonHttpResTest10() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest10"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest10"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest10", readJson); json expectedResp = { "Outputs": { "resp": { "statusCode": "201", - "body":[{"a":{"b":12, "c":"helloworld"}}, {"b":12}], - "headers":{"Content-Type":"application/json"} + "body": [{"a": {"b": 12, "c": "helloworld"}}, {"b": 12}], + "headers": {"Content-Type": "application/json"} } }, "Logs": [], @@ -330,15 +338,15 @@ function nonHttpResTest11() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/nonHttpResTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString,"(FUNC_NAME)","nonHttpResTest11"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonHttpResTest11"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-hello-nonHttpResTest11", readJson); json expectedResp = { "Outputs": { "resp": { "statusCode": "201", - "body":[[{"a":{"b":12, "c":"helloworld"}}, {"b":12}], [{"a":{"b":14, "c":"helloworld"}}, {"b":100}]], - "headers": {"Content-Type":"application/json"} + "body": [[{"a": {"b": 12, "c": "helloworld"}}, {"b": 12}], [{"a": {"b": 14, "c": "helloworld"}}, {"b": 100}]], + "headers": {"Content-Type": "application/json"} } }, "Logs": [], @@ -405,6 +413,160 @@ function testSimpleMultiQueryPath() returns error? { test:assertEquals(resp, expectedResp); } +@test:Config {} +function testOptionalQueryWithQuery() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-optional-with.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-optional", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Hello from the optional query test1" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function testQueryBool() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-bool.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-bool", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Hello from the bool query false" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function testQueryFloatt() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-float.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-floatt", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Hello from the float query 10.5" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function testQueryArr() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-arr.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-arr", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Hello from the arr query red green " + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function testQueryOptionalArrWithout() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-arr-optional-without.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-arrOrNil", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Query arr not found but all good ;)" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function testQueryOptionalArrWith() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-arr-optional-with.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-arrOrNil", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Hello from the arr or nil query red green " + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function testOptionalQueryWithoutQuery() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/query-optional-without.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-query-optional", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": { + "Content-Type": "text/plain" + }, + "body": "Query not found but all good ;)" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function testSimpleQueue() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); @@ -516,9 +678,8 @@ function testQueueInput() returns error? { test:assertEquals(resp, expectedResp); } - function replaceFuncName(string actual) { - + } @test:Config {} @@ -527,72 +688,110 @@ function testOptionalOutputBinding() returns error? { string jsonFilePath = "./tests/resources/http-optional-out.json"; json readJson = check io:fileReadJson(jsonFilePath); json resp = check clientEndpoint->post("/post-hello-optional-out", readJson); - json expectedResp = {"Outputs": {"resp": {"statusCode":"201","headers":{"Content-Type":"text/plain"},"body": "Hello from optional output binding"}}, "Logs": [], - "ReturnValue": null}; + json expectedResp = { + "Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from optional output binding"}}, + "Logs": [], + "ReturnValue": null + }; test:assertEquals(resp, expectedResp); } - -@test:Config { } +@test:Config {} function testErrorPayloadNotFound() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/error-missing-payload.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/get-hello-err-empty-payload", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400,"body":"payload not found for the variable 'greeting'", - "headers":{"Content-Type":"text/plain"}}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/error-missing-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-err-empty-payload", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": 400, + "body": "payload not found for the variable 'greeting'", + "headers": {"Content-Type": "text/plain"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testErrorInvalidPayload() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/error-invalid-payload.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-err-invalid-payload", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400,"body":"incompatible type found: 'string", - "headers":{"Content-Type":"text/plain"}}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/error-invalid-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-err-invalid-payload", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": 400, + "body": "incompatible type found: 'string", + "headers": {"Content-Type": "text/plain"} + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testOptionalPayloadWithPayload() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/http-optional-with-payload.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-optional-payload", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201","headers":{"Content-Type":"text/plain"},"body":"Hello, the payload found Jack"}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-optional-with-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-optional-payload", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello, the payload found Jack"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testOptionalPayloadWithoutPayload() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/http-optional-without-payload.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/post-hello-optional-payload", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"201","headers":{"Content-Type":"text/plain"},"body":"Hello, the payload wasn't set but all good ;)"}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-optional-without-payload.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello-optional-payload", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello, the payload wasn't set but all good ;)"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testHttpBlobInput() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/http-query-blob-input.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/get-hello-blobInput", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"200","headers":{"Content-Type":"text/plain"}, - "body":"Blob from hello.txt, content is hello from byte\n"}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-query-blob-input.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-blobInput", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": {"Content-Type": "text/plain"}, + "body": "Blob from hello.txt, content is hello from byte\n" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } -@test:Config { } +@test:Config {} function testHttpBlobInputOptional() returns error? { - final http:Client clientEndpoint = check new ("http://localhost:3000"); - string jsonFilePath = "./tests/resources/http-query-blob-optional-input.json"; - json readJson = check io:fileReadJson(jsonFilePath); - json resp = check clientEndpoint->post("/get-hello-blobInput", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":"200","headers":{"Content-Type":"text/plain"}, - "body":"Blob from hello1.txt not found"}},"Logs":[],"ReturnValue":null}; - test:assertEquals(resp, expectedResp); + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/http-query-blob-optional-input.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/get-hello-blobInput", readJson); + json expectedResp = { + "Outputs": { + "resp": { + "statusCode": "200", + "headers": {"Content-Type": "text/plain"}, + "body": "Blob from hello1.txt not found" + } + }, + "Logs": [], + "ReturnValue": null + }; + test:assertEquals(resp, expectedResp); } diff --git a/native/build.gradle b/native/build.gradle index 2a869df8..3f592d3e 100644 --- a/native/build.gradle +++ b/native/build.gradle @@ -29,7 +29,12 @@ dependencies { implementation group: 'org.ballerinalang', name: 'ballerina-lang', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-tools-api', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'value', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'bool', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'floatingpoint', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'integer', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'decimal', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'array', version: "${ballerinaLangVersion}" + implementation group: 'org.ballerinalang', name: 'string', version: "${ballerinaLangVersion}" implementation group: 'org.ballerinalang', name: 'ballerina-core', version: "${ballerinaLangVersion}" implementation (group: 'org.ballerinalang', name: 'ballerina-runtime', version: "${ballerinaLangVersion}") { transitive = false diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index f4544b6c..b185d992 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -17,7 +17,11 @@ */ package io.ballerina.stdlib.azure.functions; +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.Parameter; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; @@ -29,9 +33,9 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.azure.functions.bindings.input.InputBinding; import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; -import io.ballerina.stdlib.azure.functions.builder.StringPayloadBuilder; import io.ballerina.stdlib.azure.functions.exceptions.InvalidPayloadException; import io.ballerina.stdlib.azure.functions.exceptions.PayloadNotFoundException; +import org.ballerinalang.langlib.bool.FromString; import java.util.ArrayList; import java.util.Arrays; @@ -39,8 +43,10 @@ import java.util.List; import java.util.Optional; -import static io.ballerina.runtime.api.TypeTags.ARRAY_TAG; -import static io.ballerina.runtime.api.TypeTags.STRING_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.FLOAT_TAG; +import static io.ballerina.runtime.api.TypeTags.INT_TAG; /** * Represents a Azure Resource function property. @@ -49,6 +55,11 @@ */ public class HttpResource { + private static final ArrayType INT_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_INT); + private static final ArrayType FLOAT_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_FLOAT); + private static final ArrayType BOOLEAN_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_BOOLEAN); + private static final ArrayType DECIMAL_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_DECIMAL); + private PathParameter[] pathParams; private QueryParameter[] queryParameter; private PayloadParameter payloadParameter; @@ -61,7 +72,8 @@ public HttpResource(ResourceMethodType resourceMethodType, BMap body) { this.inputBindingParameters = getInputBindingParams(resourceMethodType, body); } - private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) throws InvalidPayloadException { + private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) + throws InvalidPayloadException { Parameter[] parameters = resourceMethod.getParameters(); List inputBindingParameters = new ArrayList<>(); for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { @@ -100,44 +112,91 @@ private QueryParameter[] getQueryParams(ResourceMethodType resourceMethod, BMap< if (isAzAnnotationExist(annotation)) { continue; } - Object bValue = queryParams.get(StringUtils.fromString(name)); - -// //TODO Handle optional null type -//// if (queryParamValue == null) { -//// -//// } -// int tag = parameter.type.getTag(); -// Object bValue = null; -// switch (tag) { -// case STRING_TAG: -// //TODO error -//// if (!(queryParamValue instanceof BString)) { -//// -//// } -// bValue = queryParamValue; -// break; -// case ARRAY_TAG: -// //TODO handle other cases -// BArray values = (BArray) queryParamValue; -//// Type elementType = ((ArrayType) parameter.type).getElementType(); -// bValue = values; //TODO check all types -//// if (elementType.getTag() == STRING_TAG) { -//// BString[] bString = new BString[values.length]; -//// for (int j = 0, valuesLength = values.length; j < valuesLength; j++) { -//// String value = values[j]; -//// bString[j] =StringUtils.fromString(value); -//// bValue = ValueCreator.createArrayValue(bString); -//// } -//// } -// break; -// default: -// //TODO unsupported -// } - queryParameters.add(new QueryParameter(i, parameter, bValue)); + BString queryValue = queryParams.getStringValue(StringUtils.fromString(name)); + try { + Object bValue = createValue(parameter.type, queryValue); + queryParameters.add(new QueryParameter(i, parameter, bValue)); + } catch (BError bError) { + throw new InvalidPayloadException(bError.getMessage()); + } } return queryParameters.toArray(QueryParameter[]::new); } + private Object createValue(Type type, BString queryValue) { + switch (type.getTag()) { + case TypeTags.STRING_TAG: + return queryValue; + case TypeTags.BOOLEAN_TAG: + return FromString.fromString(queryValue); + case TypeTags.INT_TAG: + return org.ballerinalang.langlib.integer.FromString.fromString(queryValue); + case TypeTags.FLOAT_TAG: + return org.ballerinalang.langlib.floatingpoint.FromString.fromString(queryValue); + case TypeTags.DECIMAL_TAG: + return org.ballerinalang.langlib.decimal.FromString.fromString(queryValue); + case TypeTags.UNION_TAG: + List memberTypes = ((UnionType) type).getMemberTypes(); + for (Type memberType : memberTypes) { + try { + return createValue(memberType, queryValue); + } catch (BError ignored) { + // thrown errors are ignored until all the types are iterated + } + } + return null; + case TypeTags.ARRAY_TAG: + ArrayType arrayType = (ArrayType) type; + Type elementType = arrayType.getElementType(); + if (queryValue == null) { + return null; + } + String[] values = queryValue.getValue().split(","); + return castParamArray(elementType.getTag(), values); + default: + throw new InvalidPayloadException("unsupported parameter type " + type.getName()); + } + } + + public static BArray castParamArray(int targetElementTypeTag, String[] argValueArr) { + switch (targetElementTypeTag) { + case INT_TAG: + return getBArray(argValueArr, INT_ARR, targetElementTypeTag); + case FLOAT_TAG: + return getBArray(argValueArr, FLOAT_ARR, targetElementTypeTag); + case BOOLEAN_TAG: + return getBArray(argValueArr, BOOLEAN_ARR, targetElementTypeTag); + case DECIMAL_TAG: + return getBArray(argValueArr, DECIMAL_ARR, targetElementTypeTag); + default: + return StringUtils.fromStringArray(argValueArr); + } + } + + private static BArray getBArray(String[] valueArray, ArrayType arrayType, int elementTypeTag) { + BArray arrayValue = ValueCreator.createArrayValue(arrayType); + int index = 0; + for (String element : valueArray) { + switch (elementTypeTag) { + case INT_TAG: + arrayValue.add(index++, Long.parseLong(element)); + break; + case FLOAT_TAG: + arrayValue.add(index++, Double.parseDouble(element)); + break; + case BOOLEAN_TAG: + arrayValue.add(index++, Boolean.parseBoolean(element)); + break; + case DECIMAL_TAG: + arrayValue.add(index++, ValueCreator.createDecimalValue(element)); + break; + default: + throw new InvalidPayloadException("Illegal state error: unexpected param type"); + } + } + return arrayValue; + } + private boolean isAzAnnotationExist(Object annotation) { if (annotation == null) { return false; @@ -200,7 +259,7 @@ private Optional processPayloadParam(ResourceMethodType resour } return Optional.empty(); } - + private boolean isNilType(Type type) { if (type.getTag() == TypeTags.UNION_TAG) { List memberTypes = ((UnionType) type).getMemberTypes(); diff --git a/native/src/main/java/module-info.java b/native/src/main/java/module-info.java index 144ca3e8..4e71dd9f 100644 --- a/native/src/main/java/module-info.java +++ b/native/src/main/java/module-info.java @@ -21,4 +21,9 @@ requires io.ballerina.runtime; requires io.ballerina.lang.value; requires io.ballerina.lang.array; + requires io.ballerina.lang.bool; + requires io.ballerina.lang.floatingpoint; + requires io.ballerina.lang.integer; + requires io.ballerina.lang.decimal; + requires io.ballerina.lang.string; } From 70d60f7ee7251f7866e1e87d4626143811b1e7d9 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Thu, 25 Aug 2022 15:51:52 +0530 Subject: [PATCH 21/59] Handle escape sequences --- ballerina-tests/main.bal | 9 ++ .../tests/resources/escape-seq.json | 108 ++++++++++++++++++ ballerina-tests/tests/test.bal | 10 ++ .../azurefunctions/test/HandlerTest.java | 13 ++- .../src/test/resources/handlers/main.bal | 9 ++ .../AzureFunctionNameGenerator.java | 4 +- .../service/http/HTTPTriggerBinding.java | 4 +- 7 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 ballerina-tests/tests/resources/escape-seq.json diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 6c30d107..d649c5cb 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -11,6 +11,15 @@ type Person record { int age; }; +listener af:HTTPListener ep1 = new (); + +service /hello\- on ep1 { + + resource function post hello\-query() returns string|error { + return "Hello from the hello-query"; + } +} + service /hello on ep { resource function default all() returns @af:HttpOutput string { return "Hello from all"; diff --git a/ballerina-tests/tests/resources/escape-seq.json b/ballerina-tests/tests/resources/escape-seq.json new file mode 100644 index 00000000..62b8c896 --- /dev/null +++ b/ballerina-tests/tests/resources/escape-seq.json @@ -0,0 +1,108 @@ +{ + "Data": { + "httpPayload": { + "Url": "https://bal-dev.azurewebsites.net/api/hello-/hello-query", + "Method": "POST", + "Query": {}, + "Headers": { + "Accept": [ + "*/*" + ], + "Content-Length": [ + "4" + ], + "Content-Type": [ + "application/x-www-form-urlencoded" + ], + "Host": [ + "bal-dev.azurewebsites.net" + ], + "Max-Forwards": [ + "10" + ], + "User-Agent": [ + "curl/7.78.0" + ], + "X-ARR-LOG-ID": [ + "28d97039-ef3e-4e6f-948b-680f7ff166f7" + ], + "CLIENT-IP": [ + "112.134.128.105:41856" + ], + "DISGUISED-HOST": [ + "bal-dev.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID": [ + "bal-dev" + ], + "WAS-DEFAULT-HOSTNAME": [ + "bal-dev.azurewebsites.net" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-AppService-Proto": [ + "https" + ], + "X-ARR-SSL": [ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion": [ + "1.2" + ], + "X-Forwarded-For": [ + "112.134.128.105:41856" + ], + "X-Original-URL": [ + "/api/hello-/hello-query" + ], + "X-WAWS-Unencoded-URL": [ + "/api/hello-/hello-query" + ] + }, + "Params": {}, + "Identities": [ + { + "AuthenticationType": null, + "IsAuthenticated": false, + "Actor": null, + "BootstrapContext": null, + "Claims": [], + "Label": null, + "Name": null, + "NameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body": "Jack" + } + }, + "Metadata": { + "Query": {}, + "Headers": { + "Accept": "*/*", + "Content-Length": "4", + "Content-Type": "application/x-www-form-urlencoded", + "Host": "bal-dev.azurewebsites.net", + "Max-Forwards": "10", + "User-Agent": "curl/7.78.0", + "X-ARR-LOG-ID": "28d97039-ef3e-4e6f-948b-680f7ff166f7", + "CLIENT-IP": "112.134.128.105:41856", + "DISGUISED-HOST": "bal-dev.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID": "bal-dev", + "WAS-DEFAULT-HOSTNAME": "bal-dev.azurewebsites.net", + "X-Forwarded-Proto": "https", + "X-AppService-Proto": "https", + "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion": "1.2", + "X-Forwarded-For": "112.134.128.105:41856", + "X-Original-URL": "/api/hello-/hello-query", + "X-WAWS-Unencoded-URL": "/api/hello-/hello-query" + }, + "sys": { + "MethodName": "post-hello--hello-query", + "UtcNow": "2022-06-10T07:10:30.1722785Z", + "RandGuid": "19f5a752-e046-4f7f-964f-e53207baed7b" + } + } +} diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 1f223cef..6c39fa05 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -4,6 +4,16 @@ import ballerina/lang.value; import ballerina/regex; import ballerina/test; +@test:Config {} +function testEscapeSequences() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/escape-seq.json"; + json readJson = check io:fileReadJson(jsonFilePath); + json resp = check clientEndpoint->post("/post-hello--hello-query", readJson); + json expectedResp = {"Outputs": {"resp": {"statusCode": "201", "headers": {"Content-Type": "text/plain"}, "body": "Hello from the hello-query"}}, "Logs": [], "ReturnValue": null}; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function testDefault() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java index b0a6c81d..3c824cd1 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java @@ -73,7 +73,7 @@ public void compileSample() { DiagnosticResult diagnosticResult = compilation.diagnosticResult(); Assert.assertFalse(diagnosticResult.hasErrors()); - Assert.assertEquals(generatedFunctions.size(), 18); + Assert.assertEquals(generatedFunctions.size(), 19); } @Test @@ -186,4 +186,15 @@ public void testBlobTrigger() { JsonElement parse = jsonParser.parse(str); Assert.assertEquals(actual, parse); } + + @Test + public void testEscapeSequence() { + JsonObject httpHello = generatedFunctions.get("post-hello--hello-query"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"hello-/hello-query\"}," + + "{\"type\":\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(httpHello, parse); + } } diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index 11ebb6df..46186040 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -11,6 +11,15 @@ type Person record { int age; }; +listener af:HTTPListener ep1 = new (); + +service /hello\- on ep1 { + + resource function post hello\-query() returns string|error { + return "Hello from the hello-query"; + } +} + // @af:HTTPTest service /hello on ep { resource function default all() returns @af:HttpOutput string { diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index 4a99d396..5bde8d30 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -38,10 +38,12 @@ public AzureFunctionNameGenerator(ServiceDeclarationNode serviceDeclarationNode) private String getFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { String method = functionDefinitionNode.functionName().text(); StringBuilder resourcePath = new StringBuilder(); + servicePath = servicePath.replace("\\", ""); resourcePath.append(servicePath); for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { - resourcePath.append("/").append(((IdentifierToken) pathBlock).text()); + String specialCharReplacedPathBlock = (((IdentifierToken) pathBlock).text()).replace("\\", ""); + resourcePath.append("/").append(specialCharReplacedPathBlock); } else if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { Token token = ((ResourcePathParameterNode) pathBlock).paramName(); resourcePath.append("/").append(token.text()); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index 35aa8ba7..3c80bcb6 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -57,6 +57,7 @@ public List getBindings() { Optional httpTriggerAnnot = getListenerAnnotation(this.serviceDeclarationNode, Constants.ANNOTATION_HTTP_TRIGGER); String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); + servicePath = servicePath.replace("\\", ""); List functionContexts = new ArrayList<>(); NodeList members = this.serviceDeclarationNode.members(); for (Node node : members) { @@ -76,7 +77,8 @@ public List getBindings() { resourcePath.append(servicePath); for (Node pathBlock : functionDefinitionNode.relativeResourcePath()) { if (pathBlock.kind() == SyntaxKind.IDENTIFIER_TOKEN) { - resourcePath.append("/" + ((IdentifierToken) pathBlock).text()); + String specialCharReplacedPathBlock = (((IdentifierToken) pathBlock).text()).replace("\\", ""); + resourcePath.append("/").append(specialCharReplacedPathBlock); continue; } if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { From 22c0f83f50dfc9c91fac98baac66bea53b4550f8 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 29 Aug 2022 12:46:03 +0530 Subject: [PATCH 22/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 3f0fc2f0..5882a50b 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -235,7 +235,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.0.5" +version = "1.0.4" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From ccc21f8ab984a27a6bc92b8de0d8451f76236460 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 29 Aug 2022 13:21:46 +0530 Subject: [PATCH 23/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 5882a50b..3f0fc2f0 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -235,7 +235,7 @@ dependencies = [ [[package]] org = "ballerina" name = "observe" -version = "1.0.4" +version = "1.0.5" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From deb6678072621b57d6524a52a175206d8ca0b750 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 30 Aug 2022 09:19:22 +0530 Subject: [PATCH 24/59] Reuse http typed response records and annotations --- ballerina-tests/main.bal | 69 +- ballerina/code.bal | 18 +- ballerina/http_listener.bal | 2 +- ballerina/http_status_code_types.bal | 932 ------------------ ballerina/http_status_codes.bal | 108 -- .../src/test/resources/handlers/main.bal | 39 +- .../azurefunctions/AzureFunctionModifier.java | 2 +- .../service/ServiceHandler.java | 2 +- .../service/http/HTTPTriggerBinding.java | 22 +- .../stdlib/azure/functions/Constants.java | 80 +- .../azure/functions/FunctionCallback.java | 13 +- .../stdlib/azure/functions/HttpResource.java | 21 +- .../stdlib/azure/functions/ParamHandler.java | 18 +- spec/spec.md | 22 +- 14 files changed, 163 insertions(+), 1185 deletions(-) delete mode 100644 ballerina/http_status_code_types.bal delete mode 100644 ballerina/http_status_codes.bal diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index d649c5cb..6255b35f 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -1,6 +1,7 @@ import ballerinax/azure_functions as af; +import ballerina/http; -listener af:HTTPListener ep = new (); +listener af:HttpListener ep = new (); public type DBEntry record { string id; @@ -11,7 +12,7 @@ type Person record { int age; }; -listener af:HTTPListener ep1 = new (); +listener af:HttpListener ep1 = new (); service /hello\- on ep1 { @@ -25,22 +26,22 @@ service /hello on ep { return "Hello from all"; } - resource function post optional/out(@af:Payload string greeting) returns string { + resource function post optional/out(@http:Payload string greeting) returns string { return "Hello from optional output binding"; } - resource function post optional/payload(@af:Payload string? greeting) returns string { + resource function post optional/payload(@http:Payload string? greeting) returns string { if (greeting is string) { return "Hello, the payload found " + greeting; } return "Hello, the payload wasn't set but all good ;)"; } - resource function post .(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post .(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from . path "; } - resource function post httpResTest1(@af:Payload string greeting) returns @af:HttpOutput af:Unauthorized { - af:Unauthorized unauth = { + resource function post httpResTest1(@http:Payload string greeting) returns @af:HttpOutput http:Unauthorized { + http:Unauthorized unauth = { body: "Helloworld.....", mediaType: "application/account+json", headers: { @@ -50,12 +51,12 @@ service /hello on ep { return unauth; } - resource function post httpResTest2(@af:Payload string greeting) returns @af:HttpOutput af:Ok { - af:Ok ok = {body: "Helloworld....."}; + resource function post httpResTest2(@http:Payload string greeting) returns @af:HttpOutput http:Ok { + http:Ok ok = {body: "Helloworld....."}; return ok; } - resource function post httpResTest3(@af:Payload string greeting) returns @af:HttpOutput af:InternalServerError { - af:InternalServerError err = { + resource function post httpResTest3(@http:Payload string greeting) returns @af:HttpOutput http:InternalServerError { + http:InternalServerError err = { body: "Helloworld.....", headers: { "Content-Type": "application/json+id", @@ -64,24 +65,24 @@ service /hello on ep { }; return err; } - resource function post httpResTest4(@af:Payload string greeting) returns @af:HttpOutput af:InternalServerError { - af:InternalServerError err = {}; + resource function post httpResTest4(@http:Payload string greeting) returns @af:HttpOutput http:InternalServerError { + http:InternalServerError err = {}; return err; } - resource function post foo(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post foo(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo path " + greeting; } - resource function post foo/[string bar](@af:Payload string greeting) returns @af:HttpOutput string { + resource function post foo/[string bar](@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo param " + bar; } - resource function post foo/bar(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post foo/bar(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo bar res"; } - resource function post db(@af:Payload string greeting, @af:CosmosDBInput { + resource function post db(@http:Payload string greeting, @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2", @@ -90,36 +91,36 @@ service /hello on ep { return "Hello " + greeting + input1[0].id; } - resource function post payload/jsonToRecord(@af:Payload Person greeting) returns @af:HttpOutput string|error { + resource function post payload/jsonToRecord(@http:Payload Person greeting) returns @af:HttpOutput string|error { return "Hello from json to record " + greeting.name; } - resource function post payload/jsonToJson(@af:Payload json greeting) returns @af:HttpOutput string|error { + resource function post payload/jsonToJson(@http:Payload json greeting) returns @af:HttpOutput string|error { string name = check greeting.name; return "Hello from json to json " + name; } - resource function post payload/xmlToXml(@af:Payload xml greeting) returns @af:HttpOutput string|error { + resource function post payload/xmlToXml(@http:Payload xml greeting) returns @af:HttpOutput string|error { return greeting.toJsonString(); } - resource function post payload/textToString(@af:Payload string greeting) returns @af:HttpOutput string|error { + resource function post payload/textToString(@http:Payload string greeting) returns @af:HttpOutput string|error { return greeting; } - resource function post payload/textToByte(@af:Payload byte[] greeting) returns @af:HttpOutput string|error { + resource function post payload/textToByte(@http:Payload byte[] greeting) returns @af:HttpOutput string|error { return string:fromBytes(greeting); } - resource function post payload/octaToByte(@af:Payload byte[] greeting) returns @af:HttpOutput string|error { + resource function post payload/octaToByte(@http:Payload byte[] greeting) returns @af:HttpOutput string|error { return string:fromBytes(greeting); } - resource function get err/empty/payload(@af:Payload string greeting) returns @af:HttpOutput string { + resource function get err/empty/payload(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from get empty payload"; } - resource function post err/invalid/payload(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post err/invalid/payload(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from get invalid payload " + greeting; } @@ -147,8 +148,8 @@ service /hello on ep { return "Hello from all"; } - resource function post httpResTest5() returns af:StatusCodeResponse { - af:InternalServerError err = {}; + resource function post httpResTest5() returns http:StatusCodeResponse { + http:InternalServerError err = {}; return err; } @@ -224,7 +225,7 @@ service /hello on ep { } - resource function get blobInput(@af:Payload string greeting, string name, @af:BlobInput {path: "bpath1/{Query.name}"} byte[]? blobIn) returns string|error { + resource function get blobInput(@http:Payload string greeting, string name, @af:BlobInput {path: "bpath1/{Query.name}"} byte[]? blobIn) returns string|error { if blobIn is byte[] { string content = check string:fromBytes(blobIn); return "Blob from " + name + ", content is " + content; @@ -233,7 +234,7 @@ service /hello on ep { } } - resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { + resource function post query(string name, @http:Payload string greeting) returns @af:HttpOutput string|error { return "Hello from the query " + greeting + " " + name; } @@ -278,7 +279,7 @@ service /hello on ep { queueName: "queue2" } service "queue" on new af:QueueListener() { - remote function onMessage(@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onMessage(@http:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo " + inMsg; } } @@ -287,7 +288,7 @@ service "queue" on new af:QueueListener() { listener af:CosmosDBListener cosmosEp = new (); service "cosmos" on cosmosEp { - remote function onUpdated(@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated(@http:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo " + id; } @@ -297,7 +298,7 @@ service "cosmos" on cosmosEp { listener af:TimerListener timerListener = new af:TimerListener(); service "timer" on timerListener { - remote function onTrigger(@af:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onTrigger(@http:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo " + inMsg.IsPastDue.toString(); } } @@ -309,7 +310,7 @@ service "timer" on timerListener { listener af:QueueListener queueListener1 = new af:QueueListener(); service "queue-input" on queueListener1 { - remote function onMessage(@af:Payload string inMsg, @af:CosmosDBInput { + remote function onMessage(@http:Payload string inMsg, @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2", @@ -325,7 +326,7 @@ service "queue-input" on queueListener1 { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated(@af:Payload byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { + remote function onUpdated(@http:Payload byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { path: "bpath1/newBlob" } byte[]|error { return blobIn; diff --git a/ballerina/code.bal b/ballerina/code.bal index 900e97a1..0daa3b53 100644 --- a/ballerina/code.bal +++ b/ballerina/code.bal @@ -61,15 +61,15 @@ type HttpPayload record { }; type Identity record { - anydata? AuthenticationType?; - boolean IsAuthenticated?; - anydata? Actor?; - anydata BootstrapContext?; - anydata[] Claims?; - anydata? Label?; - anydata? Name?; - string NameClaimType?; - string RoleClaimType?; + // anydata? AuthenticationType?; + // boolean IsAuthenticated?; + // anydata? Actor?; + // anydata BootstrapContext?; + // anydata[] Claims?; + // anydata? Label?; + // anydata? Name?; + // string NameClaimType?; + // string RoleClaimType?; }; # HTTP binding data. diff --git a/ballerina/http_listener.bal b/ballerina/http_listener.bal index 24d5f7ee..14ce876b 100644 --- a/ballerina/http_listener.bal +++ b/ballerina/http_listener.bal @@ -16,7 +16,7 @@ import ballerina/io; -public class HTTPListener { +public class HttpListener { ResourceService[] httpServices; public isolated function init() returns error? { diff --git a/ballerina/http_status_code_types.bal b/ballerina/http_status_code_types.bal deleted file mode 100644 index 821a5cbf..00000000 --- a/ballerina/http_status_code_types.bal +++ /dev/null @@ -1,932 +0,0 @@ -// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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. - -// Remove the union once https://github.com/ballerina-platform/ballerina-lang/issues/30490 is fixed. -# Defines the possible status code response record types. -public type StatusCodeResponse Continue|SwitchingProtocols|Ok|Created|Accepted|NonAuthoritativeInformation|NoContent| - ResetContent|PartialContent|MultipleChoices|MovedPermanently|Found|SeeOther|NotModified|UseProxy|TemporaryRedirect| - PermanentRedirect|BadRequest|Unauthorized|PaymentRequired|Forbidden|NotFound|MethodNotAllowed|NotAcceptable| - ProxyAuthenticationRequired|RequestTimeout|Conflict|Gone|LengthRequired|PreconditionFailed|PayloadTooLarge| - UriTooLong|UnsupportedMediaType|RangeNotSatisfiable|ExpectationFailed|UpgradeRequired|TooManyRequests| - RequestHeaderFieldsTooLarge|InternalServerError|NotImplemented|BadGateway|ServiceUnavailable|GatewayTimeout| - HttpVersionNotSupported; - -# Defines the possible success status code response record types. -type SuccessStatusCodeResponse Ok|Created|Accepted|NonAuthoritativeInformation|NoContent|ResetContent| - PartialContent; - -# The `Status` object creates the distinction for the different response status code types. -# -# + code - The response status code -public type Status distinct object { - public int code; -}; - -# The common attributed of response status code record type. -# -# + mediaType - The value of response `Content-type` header -# + headers - The response headers -# + body - The response payload -public type CommonResponse record {| - string mediaType?; - map headers?; - anydata body?; -|}; - -// Status code class declarations -# Represents the status code of `STATUS_CONTINUE`. -# -# + code - The response status code -public readonly class StatusContinue { - *Status; - public STATUS_CONTINUE code = STATUS_CONTINUE; -} - -# Represents the status code of `STATUS_SWITCHING_PROTOCOLS`. -# -# + code - The response status code -public readonly class StatusSwitchingProtocols { - *Status; - public STATUS_SWITCHING_PROTOCOLS code = STATUS_SWITCHING_PROTOCOLS; -} - -# Represents the status code of `STATUS_OK`. -# -# + code - The response status code -public readonly class StatusOK { - *Status; - public STATUS_OK code = STATUS_OK; -} - -# Represents the status code of `STATUS_CREATED`. -# -# + code - The response status code -public readonly class StatusCreated { - *Status; - public STATUS_CREATED code = STATUS_CREATED; -} - -# Represents the status code of `STATUS_ACCEPTED`. -# -# + code - The response status code -public readonly class StatusAccepted { - *Status; - public STATUS_ACCEPTED code = STATUS_ACCEPTED; -} - -# Represents the status code of `STATUS_NON_AUTHORITATIVE_INFORMATION`. -# -# + code - The response status code -public readonly class StatusNonAuthoritativeInformation { - *Status; - public STATUS_NON_AUTHORITATIVE_INFORMATION code = STATUS_NON_AUTHORITATIVE_INFORMATION; -} - -# Represents the status code of `STATUS_NO_CONTENT`. -# -# + code - The response status code -public readonly class StatusNoContent { - *Status; - public STATUS_NO_CONTENT code = STATUS_NO_CONTENT; -} - -# Represents the status code of `STATUS_RESET_CONTENT`. -# -# + code - The response status code -public readonly class StatusResetContent { - *Status; - public STATUS_RESET_CONTENT code = STATUS_RESET_CONTENT; -} - -# Represents the status code of `STATUS_PARTIAL_CONTENT`. -# -# + code - The response status code -public readonly class StatusPartialContent { - *Status; - public STATUS_PARTIAL_CONTENT code = STATUS_PARTIAL_CONTENT; -} - -# Represents the status code of `STATUS_MULTIPLE_CHOICES`. -# -# + code - The response status code -public readonly class StatusMultipleChoices { - *Status; - public STATUS_MULTIPLE_CHOICES code = STATUS_MULTIPLE_CHOICES; -} - -# Represents the status code of `STATUS_MOVED_PERMANENTLY`. -# -# + code - The response status code -public readonly class StatusMovedPermanently { - *Status; - public STATUS_MOVED_PERMANENTLY code = STATUS_MOVED_PERMANENTLY; -} - -# Represents the status code of `STATUS_FOUND`. -# -# + code - The response status code -public readonly class StatusFound { - *Status; - public STATUS_FOUND code = STATUS_FOUND; -} - -# Represents the status code of `STATUS_SEE_OTHER`. -# -# + code - The response status code -public readonly class StatusSeeOther { - *Status; - public STATUS_SEE_OTHER code = STATUS_SEE_OTHER; -} - -# Represents the status code of `STATUS_NOT_MODIFIED`. -# -# + code - The response status code -public readonly class StatusNotModified { - *Status; - public STATUS_NOT_MODIFIED code = STATUS_NOT_MODIFIED; -} - -# Represents the status code of `STATUS_USE_PROXY`. -# -# + code - The response status code -public readonly class StatusUseProxy { - *Status; - public STATUS_USE_PROXY code = STATUS_USE_PROXY; -} - -# Represents the status code of `STATUS_TEMPORARY_REDIRECT`. -# -# + code - The response status code -public readonly class StatusTemporaryRedirect { - *Status; - public STATUS_TEMPORARY_REDIRECT code = STATUS_TEMPORARY_REDIRECT; -} - -# Represents the status code of `STATUS_PERMANENT_REDIRECT`. -# -# + code - The response status code -public readonly class StatusPermanentRedirect { - *Status; - public STATUS_PERMANENT_REDIRECT code = STATUS_PERMANENT_REDIRECT; -} - -# Represents the status code of `STATUS_BAD_REQUEST`. -# -# + code - The response status code -public readonly class StatusBadRequest { - *Status; - public STATUS_BAD_REQUEST code = STATUS_BAD_REQUEST; -} - -# Represents the status code of `STATUS_UNAUTHORIZED`. -# -# + code - The response status code -public readonly class StatusUnauthorized { - *Status; - public STATUS_UNAUTHORIZED code = STATUS_UNAUTHORIZED; -} - -# Represents the status code of `STATUS_PAYMENT_REQUIRED`. -# -# + code - The response status code -public readonly class StatusPaymentRequired { - *Status; - public STATUS_PAYMENT_REQUIRED code = STATUS_PAYMENT_REQUIRED; -} - -# Represents the status code of `STATUS_FORBIDDEN`. -# -# + code - The response status code -public readonly class StatusForbidden { - *Status; - public STATUS_FORBIDDEN code = STATUS_FORBIDDEN; -} - -# Represents the status code of `STATUS_NOT_FOUND`. -# -# + code - The response status code -public readonly class StatusNotFound { - *Status; - public STATUS_NOT_FOUND code = STATUS_NOT_FOUND; -} - -# Represents the status code of `STATUS_METHOD_NOT_ALLOWED`. -# -# + code - The response status code -public readonly class StatusMethodNotAllowed { - *Status; - public STATUS_METHOD_NOT_ALLOWED code = STATUS_METHOD_NOT_ALLOWED; -} - -# Represents the status code of `STATUS_NOT_ACCEPTABLE`. -# -# + code - The response status code -public readonly class StatusNotAcceptable { - *Status; - public STATUS_NOT_ACCEPTABLE code = STATUS_NOT_ACCEPTABLE; -} - -# Represents the status code of `STATUS_PROXY_AUTHENTICATION_REQUIRED`. -# -# + code - The response status code -public readonly class StatusProxyAuthenticationRequired { - *Status; - public STATUS_PROXY_AUTHENTICATION_REQUIRED code = STATUS_PROXY_AUTHENTICATION_REQUIRED; -} - -# Represents the status code of `STATUS_REQUEST_TIMEOUT`. -# -# + code - The response status code -public readonly class StatusRequestTimeout { - *Status; - public STATUS_REQUEST_TIMEOUT code = STATUS_REQUEST_TIMEOUT; -} - -# Represents the status code of `STATUS_CONFLICT`. -# -# + code - The response status code -public readonly class StatusConflict { - *Status; - public STATUS_CONFLICT code = STATUS_CONFLICT; -} - -# Represents the status code of `STATUS_GONE`. -# -# + code - The response status code -public readonly class StatusGone { - *Status; - public STATUS_GONE code = STATUS_GONE; -} - -# Represents the status code of `STATUS_LENGTH_REQUIRED`. -# -# + code - The response status code -public readonly class StatusLengthRequired { - *Status; - public STATUS_LENGTH_REQUIRED code = STATUS_LENGTH_REQUIRED; -} - -# Represents the status code of `STATUS_PRECONDITION_FAILED`. -# -# + code - The response status code -public readonly class StatusPreconditionFailed { - *Status; - public STATUS_PRECONDITION_FAILED code = STATUS_PRECONDITION_FAILED; -} - -# Represents the status code of `STATUS_PAYLOAD_TOO_LARGE`. -# -# + code - The response status code -public readonly class StatusPayloadTooLarge { - *Status; - public STATUS_PAYLOAD_TOO_LARGE code = STATUS_PAYLOAD_TOO_LARGE; -} - -# Represents the status code of `STATUS_URI_TOO_LONG`. -# -# + code - The response status code -public readonly class StatusUriTooLong { - *Status; - public STATUS_URI_TOO_LONG code = STATUS_URI_TOO_LONG; -} - -# Represents the status code of `STATUS_UNSUPPORTED_MEDIA_TYPE`. -# -# + code - The response status code -public readonly class StatusUnsupportedMediaType { - *Status; - public STATUS_UNSUPPORTED_MEDIA_TYPE code = STATUS_UNSUPPORTED_MEDIA_TYPE; -} - -# Represents the status code of `STATUS_RANGE_NOT_SATISFIABLE`. -# -# + code - The response status code -public readonly class StatusRangeNotSatisfiable { - *Status; - public STATUS_RANGE_NOT_SATISFIABLE code = STATUS_RANGE_NOT_SATISFIABLE; -} - -# Represents the status code of `STATUS_EXPECTATION_FAILED`. -# -# + code - The response status code -public readonly class StatusExpectationFailed { - *Status; - public STATUS_EXPECTATION_FAILED code = STATUS_EXPECTATION_FAILED; -} - -# Represents the status code of `STATUS_UPGRADE_REQUIRED`. -# -# + code - The response status code -public readonly class StatusUpgradeRequired { - *Status; - public STATUS_UPGRADE_REQUIRED code = STATUS_UPGRADE_REQUIRED; -} - -# Represents the status code of `STATUS_TOO_MANY_REQUESTS`. -# -# + code - The response status code -public readonly class StatusTooManyRequests { - *Status; - public STATUS_TOO_MANY_REQUESTS code = STATUS_TOO_MANY_REQUESTS; -} - -# Represents the status code of `STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE`. -# -# + code - The response status code -public readonly class StatusRequestHeaderFieldsTooLarge { - *Status; - public STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE code = STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; -} - -# Represents the status code of `STATUS_INTERNAL_SERVER_ERROR`. -# -# + code - The response status code -public readonly class StatusInternalServerError { - *Status; - public STATUS_INTERNAL_SERVER_ERROR code = STATUS_INTERNAL_SERVER_ERROR; -} - -# Represents the status code of `STATUS_NOT_IMPLEMENTED`. -# -# + code - The response status code -public readonly class StatusNotImplemented { - *Status; - public STATUS_NOT_IMPLEMENTED code = STATUS_NOT_IMPLEMENTED; -} - -# Represents the status code of `STATUS_BAD_GATEWAY`. -# -# + code - The response status code -public readonly class StatusBadGateway { - *Status; - public STATUS_BAD_GATEWAY code = STATUS_BAD_GATEWAY; -} - -# Represents the status code of `STATUS_SERVICE_UNAVAILABLE`. -# -# + code - The response status code -public readonly class StatusServiceUnavailable { - *Status; - public STATUS_SERVICE_UNAVAILABLE code = STATUS_SERVICE_UNAVAILABLE; -} - -# Represents the status code of `STATUS_GATEWAY_TIMEOUT`. -# -# + code - The response status code -public readonly class StatusGatewayTimeout { - *Status; - public STATUS_GATEWAY_TIMEOUT code = STATUS_GATEWAY_TIMEOUT; -} - -# Represents the status code of `STATUS_HTTP_VERSION_NOT_SUPPORTED`. -# -# + code - The response status code -public readonly class StatusHttpVersionNotSupported { - *Status; - public STATUS_HTTP_VERSION_NOT_SUPPORTED code = STATUS_HTTP_VERSION_NOT_SUPPORTED; -} - -// Status code object initialization -final StatusContinue STATUS_CONTINUE_OBJ = new; -final StatusSwitchingProtocols STATUS_SWITCHING_PROTOCOLS_OBJ = new; -final StatusOK STATUS_OK_OBJ = new; -final StatusCreated STATUS_CREATED_OBJ = new; -final StatusAccepted STATUS_ACCEPTED_OBJ = new; -final StatusNonAuthoritativeInformation STATUS_NON_AUTHORITATIVE_INFORMATION_OBJ = new; -final StatusNoContent STATUS_NO_CONTENT_OBJ = new; -final StatusResetContent STATUS_RESET_CONTENT_OBJ = new; -final StatusPartialContent STATUS_PARTIAL_CONTENT_OBJ = new; -final StatusMultipleChoices STATUS_MULTIPLE_CHOICES_OBJ = new; -final StatusMovedPermanently STATUS_MOVED_PERMANENTLY_OBJ = new; -final StatusFound STATUS_FOUND_OBJ = new; -final StatusSeeOther STATUS_SEE_OTHER_OBJ = new; -final StatusNotModified STATUS_NOT_MODIFIED_OBJ = new; -final StatusUseProxy STATUS_USE_PROXY_OBJ = new; -final StatusTemporaryRedirect STATUS_TEMPORARY_REDIRECT_OBJ = new; -final StatusPermanentRedirect STATUS_PERMANENT_REDIRECT_OBJ = new; -final StatusBadRequest STATUS_BAD_REQUEST_OBJ = new; -final StatusUnauthorized STATUS_UNAUTHORIZED_OBJ = new; -final StatusPaymentRequired STATUS_PAYMENT_REQUIRED_OBJ = new; -final StatusForbidden STATUS_FORBIDDEN_OBJ = new; -final StatusNotFound STATUS_NOT_FOUND_OBJ = new; -final StatusMethodNotAllowed STATUS_METHOD_NOT_ALLOWED_OBJ = new; -final StatusNotAcceptable STATUS_NOT_ACCEPTABLE_OBJ = new; -final StatusProxyAuthenticationRequired STATUS_PROXY_AUTHENTICATION_REQUIRED_OBJ = new; -final StatusRequestTimeout STATUS_REQUEST_TIMEOUT_OBJ = new; -final StatusConflict STATUS_CONFLICT_OBJ = new; -final StatusGone STATUS_GONE_OBJ = new; -final StatusLengthRequired STATUS_LENGTH_REQUIRED_OBJ = new; -final StatusPreconditionFailed STATUS_PRECONDITION_FAILED_OBJ = new; -final StatusPayloadTooLarge STATUS_PAYLOAD_TOO_LARGE_OBJ = new; -final StatusUriTooLong STATUS_URI_TOO_LONG_OBJ = new; -final StatusUnsupportedMediaType STATUS_UNSUPPORTED_MEDIA_TYPE_OBJ = new; -final StatusRangeNotSatisfiable STATUS_RANGE_NOT_SATISFIABLE_OBJ = new; -final StatusExpectationFailed STATUS_EXPECTATION_FAILED_OBJ = new; -final StatusUpgradeRequired STATUS_UPGRADE_REQUIRED_OBJ = new; -final StatusTooManyRequests STATUS_TOO_MANY_REQUESTS_OBJ = new; -final StatusRequestHeaderFieldsTooLarge STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_OBJ = new; -final StatusInternalServerError STATUS_INTERNAL_SERVER_ERROR_OBJ = new; -final StatusNotImplemented STATUS_NOT_IMPLEMENTED_OBJ = new; -final StatusBadGateway STATUS_BAD_GATEWAY_OBJ = new; -final StatusServiceUnavailable STATUS_SERVICE_UNAVAILABLE_OBJ = new; -final StatusGatewayTimeout STATUS_GATEWAY_TIMEOUT_OBJ = new; -final StatusHttpVersionNotSupported STATUS_HTTP_VERSION_NOT_SUPPORTED_OBJ = new; - -// Status code record types -# The status code response record of `Continue`. -# -# + status - The response status code obj -public type Continue record {| - *CommonResponse; - readonly StatusContinue status = STATUS_CONTINUE_OBJ; -|}; - -# The status code response record of `SwitchingProtocols`. -# -# + status - The response status code obj -public type SwitchingProtocols record {| - *CommonResponse; - readonly StatusSwitchingProtocols status = STATUS_SWITCHING_PROTOCOLS_OBJ; -|}; - -# The status code response record of `Ok`. -# -# + status - The response status code obj -public type Ok record {| - *CommonResponse; - readonly StatusOK status = STATUS_OK_OBJ; -|}; - -# The status code response record of `Created`. -# -# + status - The response status code obj -public type Created record {| - *CommonResponse; - readonly StatusCreated status = STATUS_CREATED_OBJ; -|}; - -# The status code response record of `Accepted`. -# -# + status - The response status code obj -public type Accepted record {| - *CommonResponse; - readonly StatusAccepted status = STATUS_ACCEPTED_OBJ; -|}; - -# The status code response record of `NonAuthoritativeInformation`. -# -# + status - The response status code obj -public type NonAuthoritativeInformation record {| - *CommonResponse; - readonly StatusNonAuthoritativeInformation status = STATUS_NON_AUTHORITATIVE_INFORMATION_OBJ; -|}; - -# The status code response record of `NoContent`. -# -# + headers - The response headers -# + status - The response status code obj -public type NoContent record {| - map headers?; - readonly StatusNoContent status = STATUS_NO_CONTENT_OBJ; -|}; - -# The status code response record of `ResetContent`. -# -# + status - The response status code obj -public type ResetContent record {| - *CommonResponse; - readonly StatusResetContent status = STATUS_RESET_CONTENT_OBJ; -|}; - -# The status code response record of `PartialContent`. -# -# + status - The response status code obj -public type PartialContent record {| - *CommonResponse; - readonly StatusPartialContent status = STATUS_PARTIAL_CONTENT_OBJ; -|}; - -# The status code response record of `MultipleChoices`. -# -# + status - The response status code obj -public type MultipleChoices record {| - *CommonResponse; - readonly StatusMultipleChoices status = STATUS_MULTIPLE_CHOICES_OBJ; -|}; - -# The status code response record of `MovedPermanently`. -# -# + status - The response status code obj -public type MovedPermanently record {| - *CommonResponse; - readonly StatusMovedPermanently status = STATUS_MOVED_PERMANENTLY_OBJ; -|}; - -# The status code response record of `Found`. -# -# + status - The response status code obj -public type Found record {| - *CommonResponse; - readonly StatusFound status = STATUS_FOUND_OBJ; -|}; - -# The status code response record of `SeeOther`. -# -# + status - The response status code obj -public type SeeOther record {| - *CommonResponse; - readonly StatusSeeOther status = STATUS_SEE_OTHER_OBJ; -|}; - -# The status code response record of `NotModified`. -# -# + status - The response status code obj -public type NotModified record {| - *CommonResponse; - readonly StatusNotModified status = STATUS_NOT_MODIFIED_OBJ; -|}; - -# The status code response record of `UseProxy`. -# -# + status - The response status code obj -public type UseProxy record {| - *CommonResponse; - readonly StatusUseProxy status = STATUS_USE_PROXY_OBJ; -|}; - -# The status code response record of `TemporaryRedirect`. -# -# + status - The response status code obj -public type TemporaryRedirect record {| - *CommonResponse; - readonly StatusTemporaryRedirect status = STATUS_TEMPORARY_REDIRECT_OBJ; -|}; - -# The status code response record of `PermanentRedirect`. -# -# + status - The response status code obj -public type PermanentRedirect record {| - *CommonResponse; - readonly StatusPermanentRedirect status = STATUS_PERMANENT_REDIRECT_OBJ; -|}; - -# The status code response record of `BadRequest`. -# -# + status - The response status code obj -public type BadRequest record {| - *CommonResponse; - readonly StatusBadRequest status = STATUS_BAD_REQUEST_OBJ; -|}; - -# The status code response record of `Unauthorized`. -# -# + status - The response status code obj -public type Unauthorized record {| - *CommonResponse; - readonly StatusUnauthorized status = STATUS_UNAUTHORIZED_OBJ; -|}; - -# The status code response record of `PaymentRequired`. -# -# + status - The response status code obj -public type PaymentRequired record {| - *CommonResponse; - readonly StatusPaymentRequired status = STATUS_PAYMENT_REQUIRED_OBJ; -|}; - -# The status code response record of `Forbidden`. -# -# + status - The response status code obj -public type Forbidden record {| - *CommonResponse; - readonly StatusForbidden status = STATUS_FORBIDDEN_OBJ; -|}; - -# The status code response record of `NotFound`. -# -# + status - The response status code obj -public type NotFound record {| - *CommonResponse; - readonly StatusNotFound status = STATUS_NOT_FOUND_OBJ; -|}; - -# The status code response record of `MethodNotAllowed`. -# -# + status - The response status code obj -public type MethodNotAllowed record {| - *CommonResponse; - readonly StatusMethodNotAllowed status = STATUS_METHOD_NOT_ALLOWED_OBJ; -|}; - -# The status code response record of `NotAcceptable`. -# -# + status - The response status code obj -public type NotAcceptable record {| - *CommonResponse; - readonly StatusNotAcceptable status = STATUS_NOT_ACCEPTABLE_OBJ; -|}; - -# The status code response record of `ProxyAuthenticationRequired`. -# -# + status - The response status code obj -public type ProxyAuthenticationRequired record {| - *CommonResponse; - readonly StatusProxyAuthenticationRequired status = STATUS_PROXY_AUTHENTICATION_REQUIRED_OBJ; -|}; - -# The status code response record of `RequestTimeout`. -# -# + status - The response status code obj -public type RequestTimeout record {| - *CommonResponse; - readonly StatusRequestTimeout status = STATUS_REQUEST_TIMEOUT_OBJ; -|}; - -# The status code response record of `Conflict`. -# -# + status - The response status code obj -public type Conflict record {| - *CommonResponse; - readonly StatusConflict status = STATUS_CONFLICT_OBJ; -|}; - -# The status code response record of `Gone`. -# -# + status - The response status code obj -public type Gone record {| - *CommonResponse; - readonly StatusGone status = STATUS_GONE_OBJ; -|}; - -# The status code response record of `LengthRequired`. -# -# + status - The response status code obj -public type LengthRequired record {| - *CommonResponse; - readonly StatusLengthRequired status = STATUS_LENGTH_REQUIRED_OBJ; -|}; - -# The status code response record of `PreconditionFailed`. -# -# + status - The response status code obj -public type PreconditionFailed record {| - *CommonResponse; - readonly StatusPreconditionFailed status = STATUS_PRECONDITION_FAILED_OBJ; -|}; - -# The status code response record of `PayloadTooLarge`. -# -# + status - The response status code obj -public type PayloadTooLarge record {| - *CommonResponse; - readonly StatusPayloadTooLarge status = STATUS_PAYLOAD_TOO_LARGE_OBJ; -|}; - -# The status code response record of `UriTooLong`. -# -# + status - The response status code obj -public type UriTooLong record {| - *CommonResponse; - readonly StatusUriTooLong status = STATUS_URI_TOO_LONG_OBJ; -|}; - -# The status code response record of `UnsupportedMediaType`. -# -# + status - The response status code obj -public type UnsupportedMediaType record {| - *CommonResponse; - readonly StatusUnsupportedMediaType status = STATUS_UNSUPPORTED_MEDIA_TYPE_OBJ; -|}; - -# The status code response record of `RangeNotSatisfiable`. -# -# + status - The response status code obj -public type RangeNotSatisfiable record {| - *CommonResponse; - readonly StatusRangeNotSatisfiable status = STATUS_RANGE_NOT_SATISFIABLE_OBJ; -|}; - -# The status code response record of `ExpectationFailed`. -# -# + status - The response status code obj -public type ExpectationFailed record {| - *CommonResponse; - readonly StatusExpectationFailed status = STATUS_EXPECTATION_FAILED_OBJ; -|}; - -# The status code response record of `UpgradeRequired`. -# -# + status - The response status code obj -public type UpgradeRequired record {| - *CommonResponse; - readonly StatusUpgradeRequired status = STATUS_UPGRADE_REQUIRED_OBJ; -|}; - -# The status code response record of `TooManyRequests`. -# -# + status - The response status code obj -public type TooManyRequests record {| - *CommonResponse; - readonly StatusTooManyRequests status = STATUS_TOO_MANY_REQUESTS_OBJ; -|}; - -# The status code response record of `RequestHeaderFieldsTooLarge`. -# -# + status - The response status code obj -public type RequestHeaderFieldsTooLarge record {| - *CommonResponse; - readonly StatusRequestHeaderFieldsTooLarge status = STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_OBJ; -|}; - -# The status code response record of `InternalServerError`. -# -# + status - The response status code obj -public type InternalServerError record {| - *CommonResponse; - readonly StatusInternalServerError status = STATUS_INTERNAL_SERVER_ERROR_OBJ; -|}; - -# The status code response record of `NotImplemented`. -# -# + status - The response status code obj -public type NotImplemented record {| - *CommonResponse; - readonly StatusNotImplemented status = STATUS_NOT_IMPLEMENTED_OBJ; -|}; - -# The status code response record of `BadGateway`. -# -# + status - The response status code obj -public type BadGateway record {| - *CommonResponse; - readonly StatusBadGateway status = STATUS_BAD_GATEWAY_OBJ; -|}; - -# The status code response record of `ServiceUnavailable`. -# -# + status - The response status code obj -public type ServiceUnavailable record {| - *CommonResponse; - readonly StatusServiceUnavailable status = STATUS_SERVICE_UNAVAILABLE_OBJ; -|}; - -# The status code response record of `GatewayTimeout`. -# -# + status - The response status code obj -public type GatewayTimeout record {| - *CommonResponse; - readonly StatusGatewayTimeout status = STATUS_GATEWAY_TIMEOUT_OBJ; -|}; - -# The status code response record of `HttpVersionNotSupported`. -# -# + status - The response status code obj -public type HttpVersionNotSupported record {| - *CommonResponse; - readonly StatusHttpVersionNotSupported status = STATUS_HTTP_VERSION_NOT_SUPPORTED_OBJ; -|}; - -# The common status code response constant of `Continue`. -public final readonly & Continue CONTINUE = {}; - -# The common status code response constant of `SwitchingProtocols`. -public final readonly & SwitchingProtocols SWITCHING_PROTOCOLS = {}; - -# The common status code response constant of `Ok`. -public final readonly & Ok OK = {}; - -# The common status code response constant of `Created`. -public final readonly & Created CREATED = {}; - -# The common status code response constant of `Accepted`. -public final readonly & Accepted ACCEPTED = {}; - -# The common status code response constant of `NonAuthoritativeInformation`. -public final readonly & NonAuthoritativeInformation NON_AUTHORITATIVE_INFORMATION = {}; - -# The common status code response constant of `NoContent`. -public final readonly & NoContent NO_CONTENT = {}; - -# The common status code response constant of `ResetContent`. -public final readonly & ResetContent RESET_CONTENT = {}; - -# The common status code response constant of `PartialContent`. -public final readonly & PartialContent PARTIAL_CONTENT = {}; - -# The common status code response constant of `MultipleChoices`. -public final readonly & MultipleChoices MULTIPLE_CHOICES = {}; - -# The common status code response constant of `MovedPermanently`. -public final readonly & MovedPermanently MOVED_PERMANENTLY = {}; - -# The common status code response constant of `Found`. -public final readonly & Found FOUND = {}; - -# The common status code response constant of `SeeOther`. -public final readonly & SeeOther SEE_OTHER = {}; - -# The common status code response constant of `NotModified`. -public final readonly & NotModified NOT_MODIFIED = {}; - -# The common status code response constant of `UseProxy`. -public final readonly & UseProxy USE_PROXY = {}; - -# The common status code response constant of `TemporaryRedirect`. -public final readonly & TemporaryRedirect TEMPORARY_REDIRECT = {}; - -# The common status code response constant of `PermanentRedirect`. -public final readonly & PermanentRedirect PERMANENT_REDIRECT = {}; - -# The common status code response constant of `BadRequest`. -public final readonly & BadRequest BAD_REQUEST = {}; - -# The common status code response constant of `Unauthorized`. -public final readonly & Unauthorized UNAUTHORIZED = {}; - -# The common status code response constant of `PaymentRequired`. -public final readonly & PaymentRequired PAYMENT_REQUIRED = {}; - -# The common status code response constant of `Forbidden`. -public final readonly & Forbidden FORBIDDEN = {}; - -# The common status code response constant of `NotFound`. -public final readonly & NotFound NOT_FOUND = {}; - -# The common status code response constant of `MethodNotAllowed`. -public final readonly & MethodNotAllowed METHOD_NOT_ALLOWED = {}; - -# The common status code response constant of `NotAcceptable`. -public final readonly & NotAcceptable NOT_ACCEPTABLE = {}; - -# The common status code response constant of `ProxyAuthenticationRequired`. -public final readonly & ProxyAuthenticationRequired PROXY_AUTHENTICATION_REQUIRED = {}; - -# The common status code response constant of `RequestTimeout`. -public final readonly & RequestTimeout REQUEST_TIMEOUT = {}; - -# The common status code response constant of `Conflict`. -public final readonly & Conflict CONFLICT = {}; - -# The common status code response constant of `Gone`. -public final readonly & Gone GONE = {}; - -# The common status code response constant of `LengthRequired`. -public final readonly & LengthRequired LENGTH_REQUIRED = {}; - -# The common status code response constant of `PreconditionFailed`. -public final readonly & PreconditionFailed PRECONDITION_FAILED = {}; - -# The common status code response constant of `PayloadTooLarge`. -public final readonly & PayloadTooLarge PAYLOAD_TOO_LARGE = {}; - -# The common status code response constant of `UriTooLong`. -public final readonly & UriTooLong URI_TOO_LONG = {}; - -# The common status code response constant of `UnsupportedMediaType`. -public final readonly & UnsupportedMediaType UNSUPPORTED_MEDIA_TYPE = {}; - -# The common status code response constant of `RangeNotSatisfiable`. -public final readonly & RangeNotSatisfiable RANGE_NOT_SATISFIABLE = {}; - -# The common status code response constant of `ExpectationFailed`. -public final readonly & ExpectationFailed EXPECTATION_FAILED = {}; - -# The common status code response constant of `UpgradeRequired`. -public final readonly & UpgradeRequired UPGRADE_REQUIRED = {}; - -# The common status code response constant of `TooManyRequests`. -public final readonly & TooManyRequests TOO_MANY_REQUESTS = {}; - -# The common status code response constant of `RequestHeaderFieldsTooLarge`. -public final readonly & RequestHeaderFieldsTooLarge REQUEST_HEADER_FIELDS_TOO_LARGE = {}; - -# The common status code response constant of `InternalServerError`. -public final readonly & InternalServerError INTERNAL_SERVER_ERROR = {}; - -# The common status code response constant of `NotImplemented`. -public final readonly & NotImplemented NOT_IMPLEMENTED = {}; - -# The common status code response constant of `BadGateway`. -public final readonly & BadGateway BAD_GATEWAY = {}; - -# The common status code response constant of `ServiceUnavailable`. -public final readonly & ServiceUnavailable SERVICE_UNAVAILABLE = {}; - -# The common status code response constant of `GatewayTimeout`. -public final readonly & GatewayTimeout GATEWAY_TIMEOUT = {}; - -# The common status code response constant of `HttpVersionNotSupported`. -public final readonly & HttpVersionNotSupported HTTP_VERSION_NOT_SUPPORTED = {}; diff --git a/ballerina/http_status_codes.bal b/ballerina/http_status_codes.bal deleted file mode 100644 index 1c999d60..00000000 --- a/ballerina/http_status_codes.bal +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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. - -# The HTTP response status code: 100 Continue -public const int STATUS_CONTINUE = 100; -# The HTTP response status code: 101 Switching Protocols -public const int STATUS_SWITCHING_PROTOCOLS = 101; - -# The HTTP response status code: 200 OK -public const int STATUS_OK = 200; -# The HTTP response status code: 201 Created -public const int STATUS_CREATED = 201; -# The HTTP response status code: 202 Accepted -public const int STATUS_ACCEPTED = 202; -# The HTTP response status code: 203 Non Authoritative Information -public const int STATUS_NON_AUTHORITATIVE_INFORMATION = 203; -# The HTTP response status code: 204 No Content -public const int STATUS_NO_CONTENT = 204; -# The HTTP response status code: 205 Reset Content -public const int STATUS_RESET_CONTENT = 205; -# The HTTP response status code: 206 Partial Content -public const int STATUS_PARTIAL_CONTENT = 206; - -# The HTTP response status code: 300 Multiple Choices -public const int STATUS_MULTIPLE_CHOICES = 300; -# The HTTP response status code: 301 Moved Permanently -public const int STATUS_MOVED_PERMANENTLY = 301; -# The HTTP response status code: 302 Found -public const int STATUS_FOUND = 302; -# The HTTP response status code: 303 See Other -public const int STATUS_SEE_OTHER = 303; -# The HTTP response status code: 304 Not Modified -public const int STATUS_NOT_MODIFIED = 304; -# The HTTP response status code: 305 Use Proxy -public const int STATUS_USE_PROXY = 305; -# The HTTP response status code: 307 Temporary Redirect -public const int STATUS_TEMPORARY_REDIRECT = 307; -# The HTTP response status code: 308 Permanent Redirect -public const int STATUS_PERMANENT_REDIRECT = 308; - -# The HTTP response status code: 400 Bad Request -public const int STATUS_BAD_REQUEST = 400; -# The HTTP response status code: 401 Unauthorized -public const int STATUS_UNAUTHORIZED = 401; -# The HTTP response status code: 402 Payment Required -public const int STATUS_PAYMENT_REQUIRED = 402; -# The HTTP response status code: 403 Forbidden -public const int STATUS_FORBIDDEN = 403; -# The HTTP response status code: 404 Not Found -public const int STATUS_NOT_FOUND = 404; -# The HTTP response status code: 405 Method Not Allowed -public const int STATUS_METHOD_NOT_ALLOWED = 405; -# The HTTP response status code: 406 Not Acceptable -public const int STATUS_NOT_ACCEPTABLE = 406; -# The HTTP response status code: 407 Proxy Authentication Required -public const int STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; -# The HTTP response status code: 408 Request Timeout -public const int STATUS_REQUEST_TIMEOUT = 408; -# The HTTP response status code: 409 Conflict -public const int STATUS_CONFLICT = 409; -# The HTTP response status code: 410 Gone -public const int STATUS_GONE = 410; -# The HTTP response status code: 411 Length Required -public const int STATUS_LENGTH_REQUIRED = 411; -# The HTTP response status code: 412 Precondition Failed -public const int STATUS_PRECONDITION_FAILED = 412; -# The HTTP response status code: 413 Payload Too Large -public const int STATUS_PAYLOAD_TOO_LARGE = 413; -# The HTTP response status code: 414 URI Too Long -public const int STATUS_URI_TOO_LONG = 414; -# The HTTP response status code: 415 Unsupported Media Type -public const int STATUS_UNSUPPORTED_MEDIA_TYPE = 415; -# The HTTP response status code: 416 Range Not Satisfiable -public const int STATUS_RANGE_NOT_SATISFIABLE = 416; -# The HTTP response status code: 417 Expectation Failed -public const int STATUS_EXPECTATION_FAILED = 417; -# The HTTP response status code: 426 Upgrade Required -public const int STATUS_UPGRADE_REQUIRED = 426; -# The HTTP response status code: 429 Too Many Requests -public const int STATUS_TOO_MANY_REQUESTS = 429; -# The HTTP response status code: 431 Request Header Fields Too Large -public const int STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; - -# The HTTP response status code: 500 Internal Server Error -public const int STATUS_INTERNAL_SERVER_ERROR = 500; -# The HTTP response status code: 501 Not Implemented -public const int STATUS_NOT_IMPLEMENTED = 501; -# The HTTP response status code: 502 Bad Gateway -public const int STATUS_BAD_GATEWAY = 502; -# The HTTP response status code: 503 Service Unavailable -public const int STATUS_SERVICE_UNAVAILABLE = 503; -# The HTTP response status code: 504 Gateway Timeout -public const int STATUS_GATEWAY_TIMEOUT = 504; -# The HTTP response status code: 505 HTTP Version Not Supported -public const int STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index 46186040..4cfac87e 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -1,6 +1,7 @@ import ballerinax/azure_functions as af; +import ballerina/http; -listener af:HTTPListener ep = new (); +listener af:HttpListener ep = new (); public type DBEntry record { string id; @@ -11,7 +12,7 @@ type Person record { int age; }; -listener af:HTTPListener ep1 = new (); +listener af:HttpListener ep1 = new (); service /hello\- on ep1 { @@ -26,58 +27,58 @@ service /hello on ep { return "Hello from all "; } - resource function post optional(@af:Payload string greeting) returns string { + resource function post optional(@http:Payload string greeting) returns string { return "Hello from optional output bindin"; } - resource function post .(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post .(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from . path "; } - resource function post foo(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post foo(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo path " + greeting; } - resource function post foo/[string bar](@af:Payload string greeting) returns @af:HttpOutput string { + resource function post foo/[string bar](@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo param " + bar; } - resource function post foo/bar(@af:Payload string greeting) returns @af:HttpOutput string { + resource function post foo/bar(@http:Payload string greeting) returns @af:HttpOutput string { return "Hello from foo bar res"; } - resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { + resource function post query(string name, @http:Payload string greeting) returns @af:HttpOutput string|error { return "Hello from the query " + greeting + " " + name; } - resource function post db(@af:Payload string greeting, @af:CosmosDBInput { + resource function post db(@http:Payload string greeting, @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection",databaseName: "db1", collectionName: "c2", sqlQuery: "SELECT * FROM Items"} DBEntry[] input1) returns @af:HttpOutput string|error { return "Hello " + greeting + input1[0].id; } - resource function post payload/jsonToRecord (@af:Payload Person greeting) returns @af:HttpOutput string|error { + resource function post payload/jsonToRecord (@http:Payload Person greeting) returns @af:HttpOutput string|error { return "Hello from json to record " + greeting.name; } - resource function post payload/jsonToJson (@af:Payload json greeting) returns @af:HttpOutput string|error { + resource function post payload/jsonToJson (@http:Payload json greeting) returns @af:HttpOutput string|error { string name = check greeting.name; return "Hello from json to json "+ name; } - resource function post payload/xmlToXml (@af:Payload xml greeting) returns @af:HttpOutput string|error { + resource function post payload/xmlToXml (@http:Payload xml greeting) returns @af:HttpOutput string|error { return greeting.toJsonString(); } - resource function post payload/textToString (@af:Payload string greeting) returns @af:HttpOutput string|error { + resource function post payload/textToString (@http:Payload string greeting) returns @af:HttpOutput string|error { return greeting; } - resource function post payload/textToByte (@af:Payload byte[] greeting) returns @af:HttpOutput string|error { + resource function post payload/textToByte (@http:Payload byte[] greeting) returns @af:HttpOutput string|error { return string:fromBytes(greeting); } - resource function post payload/octaToByte (@af:Payload byte[] greeting) returns @af:HttpOutput string|error { + resource function post payload/octaToByte (@http:Payload byte[] greeting) returns @af:HttpOutput string|error { return string:fromBytes(greeting); } } @@ -88,7 +89,7 @@ service /hello on ep { listener af:QueueListener queueListener = new af:QueueListener(); service "queue" on queueListener { - remote function onMessage (@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onMessage (@http:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo "+ inMsg; } } @@ -97,7 +98,7 @@ service "queue" on queueListener { listener af:CosmosDBListener cosmosEp = new (); service "cosmos" on cosmosEp { - remote function onUpdated (@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated (@http:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo "+ id; } @@ -106,7 +107,7 @@ service "cosmos" on cosmosEp { @af:TimerTrigger { schedule: "*/10 * * * * *" } listener af:TimerListener timerListener = new af:TimerListener(); service "timer" on timerListener { - remote function onTrigger (@af:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onTrigger (@http:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo "+ inMsg.IsPastDue.toString(); } } @@ -117,7 +118,7 @@ service "timer" on timerListener { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated (@af:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { + remote function onUpdated (@http:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { path: "bpath1/newBlob" } byte[]|error { return blobIn; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index f97dd930..84465618 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -63,7 +63,7 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio return super.transform(serviceDeclarationNode); } NodeList members = serviceDeclarationNode.members(); - if (!name.get().equals("HTTPListener")) { + if (!name.get().equals("HttpListener")) { return super.transform(serviceDeclarationNode); } AzureFunctionNameGenerator nameGen = new AzureFunctionNameGenerator(serviceDeclarationNode); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java index 7bd6a196..0dd19867 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java @@ -45,7 +45,7 @@ public static TriggerBinding getBuilder(ServiceDeclarationNode svcDeclarationNod String serviceTypeName = name.get(); switch (serviceTypeName) { - case "HTTPListener": + case "HttpListener": return new HTTPTriggerBinding(svcDeclarationNode, semanticModel); case "QueueListener": return new QueueTriggerBinding(svcDeclarationNode, semanticModel); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index 3c80bcb6..f89c4f8f 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -109,20 +109,22 @@ public List getBindings() { continue; } } - - ReturnTypeDescriptorNode returnTypeDescriptorNode = - functionDefinitionNode.functionSignature().returnTypeDesc().get(); //TODO recheck if return is must - OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); - Optional returnBinding = outputBuilder.getOutputBinding(returnTypeDescriptorNode.annotations()); - if (returnBinding.isEmpty()) { + Optional returnTypeDescriptor = + functionDefinitionNode.functionSignature().returnTypeDesc(); + if (returnTypeDescriptor.isEmpty()) { bindings.add(new HTTPOutputBinding(null)); } else { - bindings.add(returnBinding.get()); //TODO handle in code analyzer + ReturnTypeDescriptorNode returnTypeNode = + returnTypeDescriptor.get(); + OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); + Optional returnBinding = outputBuilder.getOutputBinding(returnTypeNode.annotations()); + if (returnBinding.isEmpty()) { + bindings.add(new HTTPOutputBinding(null)); + } else { + bindings.add(returnBinding.get()); //TODO handle in code analyzer + } } Optional functionName = getFunctionNameFromAnnotation(functionDefinitionNode); - //TODO Handle -// if (functionName.isEmpty()) { -// } functionContexts.add(new FunctionContext(functionName.get(), bindings)); } return functionContexts; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index 47774e4e..e01d5583 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -22,45 +22,49 @@ * {@code Constants} contains the public constants to be used. */ public interface Constants { - String PACKAGE_ORG = "ballerinax"; - String PACKAGE_NAME = "azure_functions"; + String SLASH = "/"; - String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; + String PACKAGE_ORG = "ballerinax"; + String PACKAGE_NAME = "azure_functions"; + String HTTP_PACKAGE_ORG = "ballerina"; + String HTTP_PACKAGE_NAME = "http"; - String QUEUE_OUTPUT = "QueueOutput"; - String COSMOS_DBOUTPUT = "CosmosDBOutput"; - String OUT_MSG = "outMsg"; - String HTTP_OUTPUT = "HttpOutput"; - String BLOB_OUTPUT = "BlobOutput"; - String PAYLOAD_ANNOTATAION = "Payload"; - String STATUS = "status"; - String CODE = "code"; - String STATUS_CODE = "statusCode"; - String BODY = "body"; - String HEADERS = "headers"; - String CONTENT_TYPE = "Content-Type"; - String MEDIA_TYPE = "mediaType"; - String RESP = "resp"; - String POST = "post"; - String CREATED_201 = "201"; - String GET = "get"; - String PUT = "put"; - String PATCH = "patch"; - String DELETE = "delete"; - String HEAD = "head"; - String OPTIONS = "options"; - String DEFAULT = "default"; - String OK_200 = "200"; - String TEXT_PLAIN = "text/plain"; - String APPLICATION_XML = "application/xml"; - String APPLICATION_OCTET_STREAM = "application/octet-stream"; - String APPLICATION_JSON = "application/json"; - String BYTE_TYPE = "byte"; - String MAP_TYPE = "map"; - String JSON_TYPE = "json"; - String TABLE_TYPE = "table"; + String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; - String PAYLOAD_NOT_FOUND_ERROR = "PayloadNotFoundError"; - String FUNCTION_NOT_FOUND_ERROR = "FunctionNotFoundError"; - String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; + String QUEUE_OUTPUT = "QueueOutput"; + String COSMOS_DBOUTPUT = "CosmosDBOutput"; + String OUT_MSG = "outMsg"; + String HTTP_OUTPUT = "HttpOutput"; + String BLOB_OUTPUT = "BlobOutput"; + String PAYLOAD_ANNOTATAION = "Payload"; + String STATUS = "status"; + String CODE = "code"; + String STATUS_CODE = "statusCode"; + String BODY = "body"; + String HEADERS = "headers"; + String CONTENT_TYPE = "Content-Type"; + String MEDIA_TYPE = "mediaType"; + String RESP = "resp"; + String POST = "post"; + String CREATED_201 = "201"; + String GET = "get"; + String PUT = "put"; + String PATCH = "patch"; + String DELETE = "delete"; + String HEAD = "head"; + String OPTIONS = "options"; + String DEFAULT = "default"; + String OK_200 = "200"; + String TEXT_PLAIN = "text/plain"; + String APPLICATION_XML = "application/xml"; + String APPLICATION_OCTET_STREAM = "application/octet-stream"; + String APPLICATION_JSON = "application/json"; + String BYTE_TYPE = "byte"; + String MAP_TYPE = "map"; + String JSON_TYPE = "json"; + String TABLE_TYPE = "table"; + + String PAYLOAD_NOT_FOUND_ERROR = "PayloadNotFoundError"; + String FUNCTION_NOT_FOUND_ERROR = "FunctionNotFoundError"; + String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 624d4528..50794a62 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -31,7 +31,6 @@ import io.ballerina.runtime.api.types.TableType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; -import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BDecimal; import io.ballerina.runtime.api.values.BError; @@ -148,10 +147,8 @@ private boolean isModuleDefinedError(BError error) { } private boolean isHTTPStatusCodeResponse(Object result) { - Module resultPkg = TypeUtils.getType(result).getPackage(); - return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))) && - Constants.PACKAGE_ORG.equals(resultPkg.getOrg()) && - Constants.PACKAGE_NAME.equals(resultPkg.getName()); +// Module resultPkg = TypeUtils.getType(result).getPackage(); + return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))); //TODO : Check inheritance //(https://github.com/ballerina-platform/module-ballerinax-azure.functions/issues/490) } @@ -302,8 +299,10 @@ private void handleStatusCodeResponse(BMap result, BMap mapValu // If there is mediaType replace content-type in headers if (resultMap.containsKey(StringUtils.fromString(Constants.MEDIA_TYPE))) { Object headers = resultMap.get(StringUtils.fromString(Constants.HEADERS)); - Object mediaType = resultMap.get(StringUtils.fromString(Constants.MEDIA_TYPE)); - ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), mediaType); + if (headers != null) { + Object mediaType = resultMap.get(StringUtils.fromString(Constants.MEDIA_TYPE)); + ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), mediaType); + } } mapValue.put(StringUtils.fromString(Constants.RESP), respMap); } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index b185d992..69df757c 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -230,17 +230,20 @@ private Optional processPayloadParam(ResourceMethodType resour Parameter parameter = parameters[i]; String name = parameter.name; Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); - if (annotation == null) { - continue; - } - if (!(annotation instanceof BMap)) { - continue; - } - Boolean booleanValue = ((BMap) annotation).getBooleanValue(StringUtils.fromString( - "ballerinax/azure_functions:3:Payload")); - if (!booleanValue) { + if (!ParamHandler.isPayloadAnnotationParam(annotation)) { continue; } +// if (annotation == null) { +// continue; +// } +// if (!(annotation instanceof BMap)) { +// continue; +// } +// Boolean booleanValue = ((BMap) annotation).getBooleanValue(StringUtils.fromString( +// "ballerinax/azure_functions:3:Payload")); +// if (booleanValue == null || !booleanValue) { +// continue; +// } BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); BMap headers = httpPayload.getMapValue(StringUtils.fromString("Headers")); Type type = parameter.type; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index db4093e6..12607cc2 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -43,10 +43,18 @@ public static boolean isPayloadAnnotationParam(Object annotation) { return false; } - Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:Payload")); - return value instanceof Boolean; + for (BString bKey : ((BMap) annotation).getKeys()) { + String key = bKey.getValue(); + if (key.startsWith(Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME) && + key.endsWith(Constants.PAYLOAD_ANNOTATAION)) { + return true; + } + } + return false; +// Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:Payload")); +// return value instanceof Boolean; } - + public static Optional getInputBindingHandler(Object annotation) { if (annotation == null) { return Optional.empty(); @@ -57,11 +65,11 @@ public static Optional getInputBindingHandler(Object annotation) { for (BString key : ((BMap) annotation).getKeys()) { String annotationKey = key.getValue(); String annotationName = annotationKey.substring(annotationKey.lastIndexOf(':') + 1); - + List inputBindings = new ArrayList<>(); inputBindings.add(new BlobInput()); inputBindings.add(new CosmosInput()); - + for (InputBinding inputBinding : inputBindings) { if (inputBinding.getName().equals(annotationName)) { return Optional.of(inputBinding); diff --git a/spec/spec.md b/spec/spec.md index e136c38a..79702331 100644 --- a/spec/spec.md +++ b/spec/spec.md @@ -33,7 +33,7 @@ Separate listener declaration ```ballerina import ballerinax/azure_functions as af; -listener af:HTTPListener ep = new (); +listener af:HttpListener ep = new (); service "hello" on ep { } @@ -43,7 +43,7 @@ Inline listener declaration ```ballerina import ballerinax/azure_functions as af; -service "hello" on new af:HTTPListener() { +service "hello" on new af:HttpListener() { } ``` @@ -78,13 +78,13 @@ defaulted to `/` when not defined. If the base path contains any special charact as string literals ```ballerina -service hello\-world on new af:HTTPListener() { +service hello\-world on new af:HttpListener() { resource function get foo() { } } -service http:Service "hello-world" on new af:HTTPListener() { +service http:Service "hello-world" on new af:HttpListener() { resource function get foo() { } @@ -99,7 +99,7 @@ service and it is the mostly used approach for creating a service. The declarati listener object, creating a service object, attaching the service object to the listener object. ```ballerina -service af:HttpService /foo/bar on new af:HTTPListener() { +service af:HttpService /foo/bar on new af:HttpListener() { resource function get greeting() returns string { return "hello world"; } @@ -312,7 +312,7 @@ record| |❌|❌|✅|❌|❌ | |map\| ❌ | ❌ | ✅|❌|❌ | |table\| ❌ | ❌ | ✅|❌|❌ ```bal - resource function post query(string name, @af:Payload string greeting) returns @af:HttpOutput string|error { + resource function post query(string name, @http:Payload string greeting) returns @af:HttpOutput string|error { return "Hello from the query " + greeting + " " + name; } ``` @@ -539,7 +539,7 @@ response when returning `anydata` directly from a resource function. } listener af:QueueListener queueListener = new af:QueueListener(); service "queue" on queueListener { - remote function onMessage (@af:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onMessage (@http:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo "+ inMsg; } } @@ -553,7 +553,7 @@ service "queue" on queueListener { listener af:CosmosDBListener cosmosEp = new (); service "cosmos" on cosmosEp { - remote function onUpdated (@af:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated (@http:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo "+ id; } @@ -568,7 +568,7 @@ service "cosmos" on cosmosEp { listener af:BlobListener blobEp = new (); service "blob" on blobEp { - remote function onUpdated (@af:Payload byte[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated (@http:Payload byte[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo "+ id; } @@ -582,7 +582,7 @@ service "blob" on blobEp { listener af:TimerListener timerEp = new (); service "timer" on timerEp { - remote function onTriggered (@af:Payload json inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onTriggered (@http:Payload json inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo "+ id; } @@ -598,7 +598,7 @@ service "timer" on timerEp { listener af:TimerListener timerEp = new (); service "timer" on timerEp { - remote function onTriggered (@af:Payload json inMsg) returns @af:QueueOutput @af:TwilioSmsOutput { fromNumber: "+12069845840" } string|error { + remote function onTriggered (@http:Payload json inMsg) returns @af:QueueOutput @af:TwilioSmsOutput { fromNumber: "+12069845840" } string|error { string id = inMsg[0].id; return "helloo "+ id; } From e2fa557cda4209756436118ed5db3c549cd2b1f6 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Wed, 31 Aug 2022 14:55:03 +0530 Subject: [PATCH 25/59] Remove Payload annotation usage from non http triggers --- ballerina-tests/main.bal | 10 +++++----- .../stdlib/azure/functions/HttpResource.java | 11 ----------- .../azure/functions/NativeRemoteAdapter.java | 3 +-- .../stdlib/azure/functions/ParamHandler.java | 17 +++++++++++++++++ 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 6255b35f..136f9330 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -279,7 +279,7 @@ service /hello on ep { queueName: "queue2" } service "queue" on new af:QueueListener() { - remote function onMessage(@http:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onMessage(string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo " + inMsg; } } @@ -288,7 +288,7 @@ service "queue" on new af:QueueListener() { listener af:CosmosDBListener cosmosEp = new (); service "cosmos" on cosmosEp { - remote function onUpdated(@http:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated(DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo " + id; } @@ -298,7 +298,7 @@ service "cosmos" on cosmosEp { listener af:TimerListener timerListener = new af:TimerListener(); service "timer" on timerListener { - remote function onTrigger(@http:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onTrigger(af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo " + inMsg.IsPastDue.toString(); } } @@ -310,7 +310,7 @@ service "timer" on timerListener { listener af:QueueListener queueListener1 = new af:QueueListener(); service "queue-input" on queueListener1 { - remote function onMessage(@http:Payload string inMsg, @af:CosmosDBInput { + remote function onMessage(string inMsg, @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2", @@ -326,7 +326,7 @@ service "queue-input" on queueListener1 { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated(@http:Payload byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { + remote function onUpdated(byte[] blobIn, @af:BindingName {} string name) returns @af:BlobOutput { path: "bpath1/newBlob" } byte[]|error { return blobIn; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index 69df757c..f3370f35 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -233,17 +233,6 @@ private Optional processPayloadParam(ResourceMethodType resour if (!ParamHandler.isPayloadAnnotationParam(annotation)) { continue; } -// if (annotation == null) { -// continue; -// } -// if (!(annotation instanceof BMap)) { -// continue; -// } -// Boolean booleanValue = ((BMap) annotation).getBooleanValue(StringUtils.fromString( -// "ballerinax/azure_functions:3:Payload")); -// if (booleanValue == null || !booleanValue) { -// continue; -// } BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); BMap headers = httpPayload.getMapValue(StringUtils.fromString("Headers")); Type type = parameter.type; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index bd7a801f..6e85dd61 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -71,8 +71,7 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, for (Parameter parameter : parameters) { String name = parameter.name; Object annotation = methodType.getAnnotation(StringUtils.fromString("$param$." + name)); - //TODO check and process Payload variable - if (ParamHandler.isPayloadAnnotationParam(annotation)) { + if (!ParamHandler.isAzureAnnotationExist(annotation)) { Object bValue = getDataboundValue(data, parameter, serviceType); argList.add(bValue); argList.add(true); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 12607cc2..1c523841 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -35,6 +35,23 @@ */ public class ParamHandler { + public static boolean isAzureAnnotationExist(Object annotation) { + if (annotation == null) { + return false; + } + if (!(annotation instanceof BMap)) { + return false; + } + + for (BString bKey : ((BMap) annotation).getKeys()) { + String key = bKey.getValue(); + if (key.startsWith(Constants.PACKAGE_ORG + Constants.SLASH + Constants.PACKAGE_NAME)) { + return true; + } + } + return false; + } + public static boolean isPayloadAnnotationParam(Object annotation) { if (annotation == null) { return false; From dcac2267c27bf2976e9b4fc68efa4b47957ff2c1 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 13 Sep 2022 15:05:42 +0530 Subject: [PATCH 26/59] Add runtime header annotation support --- ballerina/annotation.bal | 10 ++ ballerina/errors.bal | 2 + ballerina/http_service.bal | 2 +- .../stdlib/azure/functions/Constants.java | 88 ++++++------ .../azure/functions/HeaderParameter.java | 40 ++++++ .../stdlib/azure/functions/HttpResource.java | 130 ++++++++++++++++-- .../functions/NativeHttpToAzureAdaptor.java | 3 +- .../stdlib/azure/functions/ParamHandler.java | 43 +++++- .../exceptions/HeaderNotFoundException.java | 33 +++++ 9 files changed, 296 insertions(+), 55 deletions(-) create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/HeaderParameter.java create mode 100644 native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/HeaderNotFoundException.java diff --git a/ballerina/annotation.bal b/ballerina/annotation.bal index 953e2449..04662446 100644 --- a/ballerina/annotation.bal +++ b/ballerina/annotation.bal @@ -34,6 +34,16 @@ public type HTTPTriggerConfiguration record {| public annotation Payload on parameter, return; +# Defines the Header resource signature parameter. +# +# + name - Specifies the name of the required header +public type HttpHeader record {| + string name?; +|}; + +# The annotation which is used to define the Header resource signature parameter. +public annotation HttpHeader Header on parameter; + # @azurefunctions:HttpOutput annotation public const annotation HttpOutput on parameter, return; diff --git a/ballerina/errors.bal b/ballerina/errors.bal index 4ef1da1c..73015e0d 100644 --- a/ballerina/errors.bal +++ b/ballerina/errors.bal @@ -5,3 +5,5 @@ public type FunctionNotFoundError distinct Error; public type PayloadNotFoundError distinct Error; public type InvalidPayloadError distinct Error; + +public type HeaderNotFoundError distinct Error; diff --git a/ballerina/http_service.bal b/ballerina/http_service.bal index 0f88be96..4be9bb78 100644 --- a/ballerina/http_service.bal +++ b/ballerina/http_service.bal @@ -41,7 +41,7 @@ isolated service class ResourceService { isolated function getResponsePayload (map|error nativeResponse) returns json { - if (nativeResponse is PayloadNotFoundError || nativeResponse is InvalidPayloadError ) { + if (nativeResponse is PayloadNotFoundError || nativeResponse is InvalidPayloadError || nativeResponse is HeaderNotFoundError) { return {"Outputs": {"resp": {"statusCode": 400, "body": nativeResponse.message(),"headers": {"Content-Type": "text/plain"}}}, "Logs": [], "ReturnValue": null}; } else if (nativeResponse is error) { return {"Outputs": {"resp": {"statusCode": 500}}, "Logs": [], "ReturnValue": null}; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index e01d5583..3746c6c1 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -22,49 +22,55 @@ * {@code Constants} contains the public constants to be used. */ public interface Constants { - String SLASH = "/"; + String SLASH = "/"; - String PACKAGE_ORG = "ballerinax"; - String PACKAGE_NAME = "azure_functions"; - String HTTP_PACKAGE_ORG = "ballerina"; - String HTTP_PACKAGE_NAME = "http"; + String PACKAGE_ORG = "ballerinax"; + String PACKAGE_NAME = "azure_functions"; + String HTTP_PACKAGE_ORG = "ballerina"; + String HTTP_PACKAGE_NAME = "http"; - String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; + String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; + + String QUEUE_OUTPUT = "QueueOutput"; + String COSMOS_DBOUTPUT = "CosmosDBOutput"; + String OUT_MSG = "outMsg"; + String HTTP_OUTPUT = "HttpOutput"; + String BLOB_OUTPUT = "BlobOutput"; + String PAYLOAD_ANNOTATAION = "Payload"; + String HEADER_ANNOTATION = "Header"; + String SERVICE_CONF_ANNOTATION = "ServiceConfig"; + String STATUS = "status"; + String CODE = "code"; + String STATUS_CODE = "statusCode"; + String BODY = "body"; + String HEADERS = "headers"; + String CONTENT_TYPE = "Content-Type"; + String MEDIA_TYPE = "mediaType"; + String RESP = "resp"; + String POST = "post"; + String CREATED_201 = "201"; + String GET = "get"; + String PUT = "put"; + String PATCH = "patch"; + String DELETE = "delete"; + String HEAD = "head"; + String OPTIONS = "options"; + String DEFAULT = "default"; + String OK_200 = "200"; + String TEXT_PLAIN = "text/plain"; + String APPLICATION_XML = "application/xml"; + String APPLICATION_OCTET_STREAM = "application/octet-stream"; + String APPLICATION_JSON = "application/json"; + String BYTE_TYPE = "byte"; + String MAP_TYPE = "map"; + String JSON_TYPE = "json"; + String TABLE_TYPE = "table"; + + String PAYLOAD_NOT_FOUND_ERROR = "PayloadNotFoundError"; + String FUNCTION_NOT_FOUND_ERROR = "FunctionNotFoundError"; + String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; + String HEADER_NOT_FOUND_ERROR = "HeaderNotFoundError"; + String HTTP_PACKAGE_VERSION = "2"; - String QUEUE_OUTPUT = "QueueOutput"; - String COSMOS_DBOUTPUT = "CosmosDBOutput"; - String OUT_MSG = "outMsg"; - String HTTP_OUTPUT = "HttpOutput"; - String BLOB_OUTPUT = "BlobOutput"; - String PAYLOAD_ANNOTATAION = "Payload"; - String STATUS = "status"; - String CODE = "code"; - String STATUS_CODE = "statusCode"; - String BODY = "body"; - String HEADERS = "headers"; - String CONTENT_TYPE = "Content-Type"; - String MEDIA_TYPE = "mediaType"; - String RESP = "resp"; - String POST = "post"; - String CREATED_201 = "201"; - String GET = "get"; - String PUT = "put"; - String PATCH = "patch"; - String DELETE = "delete"; - String HEAD = "head"; - String OPTIONS = "options"; - String DEFAULT = "default"; - String OK_200 = "200"; - String TEXT_PLAIN = "text/plain"; - String APPLICATION_XML = "application/xml"; - String APPLICATION_OCTET_STREAM = "application/octet-stream"; - String APPLICATION_JSON = "application/json"; - String BYTE_TYPE = "byte"; - String MAP_TYPE = "map"; - String JSON_TYPE = "json"; - String TABLE_TYPE = "table"; - String PAYLOAD_NOT_FOUND_ERROR = "PayloadNotFoundError"; - String FUNCTION_NOT_FOUND_ERROR = "FunctionNotFoundError"; - String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HeaderParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HeaderParameter.java new file mode 100644 index 00000000..99b1d0c1 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HeaderParameter.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions; + +import io.ballerina.runtime.api.types.Parameter; + +/** + * Represents the header parameter in azure functions. + * + * @since 2.0.0 + */ +public class HeaderParameter extends AZFParameter { + + private Object value; + + public HeaderParameter(int index, Parameter parameter, Object value) { + super(index, parameter); + this.value = value; + } + + @Override + public Object getValue() { + return value; + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index f3370f35..5488d5b6 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -22,7 +22,9 @@ 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.Field; import io.ballerina.runtime.api.types.Parameter; +import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; @@ -33,14 +35,18 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.azure.functions.bindings.input.InputBinding; import io.ballerina.stdlib.azure.functions.builder.AbstractPayloadBuilder; +import io.ballerina.stdlib.azure.functions.exceptions.HeaderNotFoundException; import io.ballerina.stdlib.azure.functions.exceptions.InvalidPayloadException; import io.ballerina.stdlib.azure.functions.exceptions.PayloadNotFoundException; import org.ballerinalang.langlib.bool.FromString; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Optional; import static io.ballerina.runtime.api.TypeTags.BOOLEAN_TAG; @@ -64,12 +70,14 @@ public class HttpResource { private QueryParameter[] queryParameter; private PayloadParameter payloadParameter; private InputBindingParameter[] inputBindingParameters; + private HeaderParameter headerParameter; - public HttpResource(ResourceMethodType resourceMethodType, BMap body) { + public HttpResource(ResourceMethodType resourceMethodType, BMap body, BMap serviceAnnotations) { this.pathParams = getPathParams(resourceMethodType, body); this.payloadParameter = processPayloadParam(resourceMethodType, body).orElse(null); this.queryParameter = getQueryParams(resourceMethodType, body); this.inputBindingParameters = getInputBindingParams(resourceMethodType, body); + this.headerParameter = processHeaderParam(resourceMethodType, body, serviceAnnotations).orElse(null);; } private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) @@ -122,24 +130,24 @@ private QueryParameter[] getQueryParams(ResourceMethodType resourceMethod, BMap< } return queryParameters.toArray(QueryParameter[]::new); } - - private Object createValue(Type type, BString queryValue) { + + private Object createValue(Type type, BString strValue) { switch (type.getTag()) { case TypeTags.STRING_TAG: - return queryValue; + return strValue; case TypeTags.BOOLEAN_TAG: - return FromString.fromString(queryValue); + return FromString.fromString(strValue); case TypeTags.INT_TAG: - return org.ballerinalang.langlib.integer.FromString.fromString(queryValue); + return org.ballerinalang.langlib.integer.FromString.fromString(strValue); case TypeTags.FLOAT_TAG: - return org.ballerinalang.langlib.floatingpoint.FromString.fromString(queryValue); + return org.ballerinalang.langlib.floatingpoint.FromString.fromString(strValue); case TypeTags.DECIMAL_TAG: - return org.ballerinalang.langlib.decimal.FromString.fromString(queryValue); + return org.ballerinalang.langlib.decimal.FromString.fromString(strValue); case TypeTags.UNION_TAG: List memberTypes = ((UnionType) type).getMemberTypes(); for (Type memberType : memberTypes) { try { - return createValue(memberType, queryValue); + return createValue(memberType, strValue); } catch (BError ignored) { // thrown errors are ignored until all the types are iterated } @@ -148,10 +156,10 @@ private Object createValue(Type type, BString queryValue) { case TypeTags.ARRAY_TAG: ArrayType arrayType = (ArrayType) type; Type elementType = arrayType.getElementType(); - if (queryValue == null) { + if (strValue == null) { return null; } - String[] values = queryValue.getValue().split(","); + String[] values = strValue.getValue().split(","); return castParamArray(elementType.getTag(), values); default: throw new InvalidPayloadException("unsupported parameter type " + type.getName()); @@ -252,6 +260,102 @@ private Optional processPayloadParam(ResourceMethodType resour return Optional.empty(); } + private Optional processHeaderParam(ResourceMethodType resourceMethod, BMap body, + BMap serviceAnnotations) { + Parameter[] parameters = resourceMethod.getParameters(); + Object headerParam = null; + Boolean treatNilableAsOptional = true; + String serviceConfig = Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME + ":" + + Constants.HTTP_PACKAGE_VERSION + ":" + Constants.SERVICE_CONF_ANNOTATION; + Boolean isServiceConfExist = ParamHandler.isHttpServiceConfExist(serviceAnnotations); + if (isServiceConfExist) { + treatNilableAsOptional = serviceAnnotations.getMapValue(StringUtils.fromString(serviceConfig)). + getBooleanValue(StringUtils.fromString("treatNilableAsOptional")); + } + for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { + Parameter parameter = parameters[i]; + String name = parameter.name; + Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + if (annotation == null) { + continue; + } + Boolean isHeaderAnnotation = ParamHandler.isHeaderAnnotationParam(annotation); + if (!isHeaderAnnotation) { + continue; + } + BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); + BMap headers = httpPayload.getMapValue(StringUtils.fromString("Headers")); + + String headerAnnotation = Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME + + ":" + Constants.HTTP_PACKAGE_VERSION + ":" + Constants.HEADER_ANNOTATION; + BMap headerAnnotationField = (BMap) ((BMap) annotation).get(StringUtils.fromString(headerAnnotation)); + if (headerAnnotationField.size() == 0) { + //No annotation field defined {name: ....} + if ((parameter.type).getTag() == TypeTags.RECORD_TYPE_TAG) { + headerParam = processHeaderRecordParam(headers, parameter, treatNilableAsOptional); + return Optional.of(new HeaderParameter(i, parameter, headerParam)); + } + headerParam = getHeaderValue(headers, parameter.type, name, treatNilableAsOptional); + return Optional.of(new HeaderParameter(i, parameter, headerParam)); + } else if (headerAnnotationField.size() == 1) { + // Annotation field is defined + BString headerName = ((BMap) headerAnnotationField).getStringValue(StringUtils.fromString("name")); + headerParam = getHeaderValue(headers, parameter.type, headerName.getValue(), treatNilableAsOptional); + return Optional.of(new HeaderParameter(i, parameter, headerParam)); + } else { + throw new RuntimeException("Header annotation can have only one name field."); + //TODO :- add proper exception + } + + } + return Optional.empty(); + } + + private Object getHeaderValue(BMap headers, Type type, String fieldName, + Boolean treatNilableAsOptional) { + BString headerValue = null; + Boolean isHeaderExist = false; + for (BString headerKey : headers.getKeys()) { + if (headerKey.getValue().toLowerCase(Locale.ROOT).equals(fieldName.toLowerCase(Locale.ROOT))) { + isHeaderExist = true; + if (((BArray) (headers.get(headerKey))).size() == 0) { + break; + } + headerValue = (BString) ((BArray) (headers.get(headerKey))).get(0); + } + } + if (isHeaderExist == false) { + //Header name not exist case + if (isNilType(type) && treatNilableAsOptional) { + return null; + } + throw new HeaderNotFoundException("no header value found for '" + fieldName + "'"); + } else if (headerValue == null) { + //Handle header value not exist case + if (isNilType(type)) { + return null; + } + throw new HeaderNotFoundException("no header value found for '" + fieldName + "'"); + + } + return createValue(type, headerValue); + } + + private Object processHeaderRecordParam(BMap headers, Parameter parameter, + Boolean treatNilableAsOptional) { + RecordType recordType = (RecordType) parameter.type; + Map fields = recordType.getFields(); + BMap recordValue = ValueCreator.createRecordValue(recordType); + for (Map.Entry field : fields.entrySet()) { + String fieldName = field.getKey(); + Type fieldType = field.getValue().getFieldType(); + Object headerValue = getHeaderValue(headers, fieldType, fieldName, treatNilableAsOptional); + recordValue.put(StringUtils.fromString(fieldName), headerValue); + } + return recordValue; + } + + private boolean isNilType(Type type) { if (type.getTag() == TypeTags.UNION_TAG) { List memberTypes = ((UnionType) type).getMemberTypes(); @@ -266,6 +370,7 @@ private boolean isNilType(Type type) { return false; } + private BString getRequestBody(BMap httpPayload, String name, Type type) throws PayloadNotFoundException { BString bBody = StringUtils.fromString("Body"); if (httpPayload.containsKey(bBody)) { @@ -294,6 +399,9 @@ public Object[] getArgList() { if (payloadParameter != null) { parameters.add(payloadParameter); } + if (headerParameter != null) { + parameters.add(headerParameter); + } //TODO add more input output binding params Collections.sort(parameters); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index 519037dc..9a8491f5 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -85,7 +85,8 @@ private static Object invokeResourceFunction(Environment env, BObject bHubServic } ResourceMethodType resourceMethod = resourceMethodType.get(); try { - HttpResource httpResource = new HttpResource(resourceMethod, body); + BMap serviceAnnotations = serviceType.getAnnotations(); + HttpResource httpResource = new HttpResource(resourceMethod, body, serviceAnnotations); Object[] args = httpResource.getArgList(); if (serviceType.isIsolated() && resourceMethod.isIsolated()) { env.getRuntime().invokeMethodAsyncConcurrently( diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 1c523841..0886e34d 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -28,6 +28,11 @@ import java.util.List; import java.util.Optional; +import static io.ballerina.stdlib.azure.functions.Constants.HEADER_ANNOTATION; +import static io.ballerina.stdlib.azure.functions.Constants.HTTP_PACKAGE_NAME; +import static io.ballerina.stdlib.azure.functions.Constants.HTTP_PACKAGE_ORG; +import static io.ballerina.stdlib.azure.functions.Constants.SERVICE_CONF_ANNOTATION; + /** * Represents the input binding handler. * @@ -62,7 +67,7 @@ public static boolean isPayloadAnnotationParam(Object annotation) { for (BString bKey : ((BMap) annotation).getKeys()) { String key = bKey.getValue(); - if (key.startsWith(Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME) && + if (key.startsWith(HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME) && key.endsWith(Constants.PAYLOAD_ANNOTATAION)) { return true; } @@ -72,6 +77,42 @@ public static boolean isPayloadAnnotationParam(Object annotation) { // return value instanceof Boolean; } + public static boolean isHeaderAnnotationParam(Object annotation) { + if (annotation == null) { + return false; + } + if (!(annotation instanceof BMap)) { + return false; + } + + for (BString bKey : ((BMap) annotation).getKeys()) { + String[] keySegments = (bKey.getValue()).split("[/:]"); + if ((keySegments.length == 4) && HTTP_PACKAGE_ORG.equals(keySegments[0]) && + HTTP_PACKAGE_NAME.equals(keySegments[1]) && HEADER_ANNOTATION.equals(keySegments[3])) { + return true; + } + } + return false; + } + + public static boolean isHttpServiceConfExist(Object annotation) { + if (annotation == null) { + return false; + } + if (!(annotation instanceof BMap)) { + return false; + } + + for (BString bKey : ((BMap) annotation).getKeys()) { + String[] keySegments = (bKey.getValue()).split("[/:]"); + if ((keySegments.length == 4) && HTTP_PACKAGE_ORG.equals(keySegments[0]) && + HTTP_PACKAGE_NAME.equals(keySegments[1]) && SERVICE_CONF_ANNOTATION.equals(keySegments[3])) { + return true; + } + } + return false; + } + public static Optional getInputBindingHandler(Object annotation) { if (annotation == null) { return Optional.empty(); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/HeaderNotFoundException.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/HeaderNotFoundException.java new file mode 100644 index 00000000..9f9c3f50 --- /dev/null +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/exceptions/HeaderNotFoundException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.azure.functions.exceptions; + +import io.ballerina.stdlib.azure.functions.Constants; + +/** + * Represents when the expected header is not found. + * + * @since 2.0.0 + */ +public class HeaderNotFoundException extends BadRequestException { + + public HeaderNotFoundException(String message) { + super(message, Constants.HEADER_NOT_FOUND_ERROR); + } +} From 7a802cbc8696a455519476176d9b713d7be7ca7b Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 13 Sep 2022 15:07:26 +0530 Subject: [PATCH 27/59] Add code analyzer for header annotation --- .../azurefunctions/AzureCompilerPlugin.java | 2 +- .../azurefunctions/AzureDiagnosticCodes.java | 63 ++++ .../AzureFunctionsCodeAnalyzer.java | 36 ++ .../ballerinax/azurefunctions/Constants.java | 4 + .../org/ballerinax/azurefunctions/Util.java | 30 ++ .../AzureFunctionsCodeAnalyzerTask.java | 98 ++++++ .../validators/HttpListenerValidator.java | 322 ++++++++++++++++++ 7 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsCodeAnalyzer.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java create mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java index 301b849b..5a7e8f8a 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCompilerPlugin.java @@ -28,7 +28,7 @@ public class AzureCompilerPlugin extends CompilerPlugin { @Override public void init(CompilerPluginContext pluginContext) { -// pluginContext.addCodeAnalyzer(new AzureFunctionsCodeAnalyzer()); + pluginContext.addCodeAnalyzer(new AzureFunctionsCodeAnalyzer()); // pluginContext.addCodeGenerator(new AzureCodeGenerator()); //TODO add code anaylzer to validate listener annotations pluginContext.addCodeModifier(new AzureCodeModifier()); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java new file mode 100644 index 00000000..b2e84a18 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; + +import io.ballerina.tools.diagnostics.DiagnosticSeverity; + +import static io.ballerina.tools.diagnostics.DiagnosticSeverity.ERROR; + +/** + * {@code DiagnosticCodes} is used to hold diagnostic codes. + */ +public enum AzureDiagnosticCodes { + AF_001("AF_001", "invalid annotation type on param '%s'", ERROR), + AF_002("AF_002", "invalid resource parameter '%s'", ERROR), + AF_003("AF_003", "invalid type of header param '%s': One of the following types is expected: " + + "'string','int','float','decimal','boolean', an array of the above types or a record which consists of " + + "the above types", ERROR), + AF_004("AF_004", "invalid union type of header param '%s': one of the 'string','int','float'," + + "'decimal','boolean' types, an array of the above types or a record which consists of the above types can" + + " only be union with '()'. Eg: string|() or string[]|()", ERROR), + AF_005("AF_005", "invalid intersection type : '%s'. Only readonly type is allowed", ERROR), + AF_006("AF_006", "rest fields are not allowed for header binding records. Use 'http:Headers' type to access " + + "all headers", ERROR), + AF_007("AF_007", "invalid multiple resource parameter annotations for '%s'", ERROR); + + private final String code; + private final String message; + private final DiagnosticSeverity severity; + + AzureDiagnosticCodes(String code, String message, DiagnosticSeverity severity) { + this.code = code; + this.message = message; + this.severity = severity; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public DiagnosticSeverity getSeverity() { + return severity; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsCodeAnalyzer.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsCodeAnalyzer.java new file mode 100644 index 00000000..9f9f243f --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsCodeAnalyzer.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; + +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.projects.plugins.CodeAnalysisContext; +import io.ballerina.projects.plugins.CodeAnalyzer; +import org.ballerinax.azurefunctions.validators.AzureFunctionsCodeAnalyzerTask; + +/** + * Contains the code analyzers for azure functions. + * + * @since 2.0.0 + */ +public class AzureFunctionsCodeAnalyzer extends CodeAnalyzer { + + @Override + public void init(CodeAnalysisContext codeAnalysisCtx) { + codeAnalysisCtx.addSyntaxNodeAnalysisTask(new AzureFunctionsCodeAnalyzerTask(), SyntaxKind.SERVICE_DECLARATION); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index d8f6d9a3..7450c7e3 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -29,6 +29,10 @@ public class Constants { public static final String REQUEST_PARAMS_NAME = "params"; public static final String HTTP_CALLER_PARAMS_NAME = "caller"; public static final String HTTP_REQUEST_PARAMS_NAME = "request"; + public static final String REMOTE_KEYWORD = "remote"; + public static final String HTTP = "http"; + public static final String AZURE_FUNCTIONS = "azure_functions"; + public static final String HEADER_ANNOTATION_TYPE = "HttpHeader"; public static final String AZURE_FUNCTIONS_PACKAGE_ORG = "ballerinax"; public static final String AZURE_FUNCTIONS_CONTEXT_NAME = "Context"; public static final String AZURE_FUNCS_OUTPUT_ZIP_FILENAME = "azure-functions.zip"; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index 4f56ef4b..9ecb15e8 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -13,6 +13,11 @@ import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.compiler.syntax.tree.Token; +import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.tools.diagnostics.DiagnosticFactory; +import io.ballerina.tools.diagnostics.DiagnosticInfo; +import io.ballerina.tools.diagnostics.DiagnosticProperty; +import io.ballerina.tools.diagnostics.Location; import io.ballerina.tools.text.LineRange; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextRange; @@ -23,6 +28,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.List; import java.util.Optional; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -128,4 +134,28 @@ public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) return normalizePath; } + public static void updateDiagnostic(SyntaxNodeAnalysisContext ctx, Location location, + AzureDiagnosticCodes httpDiagnosticCodes) { + DiagnosticInfo diagnosticInfo = getDiagnosticInfo(httpDiagnosticCodes); + ctx.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagnosticInfo, location)); + } + + public static void updateDiagnostic(SyntaxNodeAnalysisContext ctx, Location location, + AzureDiagnosticCodes azureDiagnosticCodes, Object... argName) { + DiagnosticInfo diagnosticInfo = getDiagnosticInfo(azureDiagnosticCodes, argName); + ctx.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagnosticInfo, location)); + } + + public static void updateDiagnostic(SyntaxNodeAnalysisContext ctx, Location location, + AzureDiagnosticCodes azureDiagnosticCodes, + List> diagnosticProperties, String argName) { + DiagnosticInfo diagnosticInfo = getDiagnosticInfo(azureDiagnosticCodes, argName); + ctx.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagnosticInfo, location, diagnosticProperties)); + } + + public static DiagnosticInfo getDiagnosticInfo(AzureDiagnosticCodes diagnostic, Object... args) { + return new DiagnosticInfo(diagnostic.getCode(), String.format(diagnostic.getMessage(), args), + diagnostic.getSeverity()); + } + } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java new file mode 100644 index 00000000..1f0575c3 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.validators; + +import io.ballerina.compiler.api.symbols.ModuleSymbol; +import io.ballerina.compiler.api.symbols.ServiceDeclarationSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.symbols.UnionTypeSymbol; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.projects.plugins.AnalysisTask; +import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.tools.diagnostics.Diagnostic; +import io.ballerina.tools.diagnostics.DiagnosticSeverity; + +import java.util.List; +import java.util.Optional; + +import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS_MODULE_NAME; +import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS_PACKAGE_ORG; +import static org.ballerinax.azurefunctions.validators.HttpListenerValidator.validate; + +/*** + * Code analyzer for azure function specific validations. + * + * @since 2.0.0 + */ +public class AzureFunctionsCodeAnalyzerTask implements AnalysisTask { + + @Override + public void perform(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext) { + List diagnostics = syntaxNodeAnalysisContext.semanticModel().diagnostics(); + boolean erroneousCompilation = diagnostics.stream() + .anyMatch(d -> DiagnosticSeverity.ERROR.equals(d.diagnosticInfo().severity())); + if (erroneousCompilation) { + return; + } + + ServiceDeclarationNode serviceDeclarationNode = (ServiceDeclarationNode) syntaxNodeAnalysisContext.node(); + Optional serviceSymOptional = syntaxNodeAnalysisContext.semanticModel().symbol(serviceDeclarationNode); + + if (serviceSymOptional.isPresent()) { + List listenerTypes = ((ServiceDeclarationSymbol) serviceSymOptional.get()).listenerTypes(); + if (listenerTypes.stream().noneMatch(this::isListenerBelongsToAzureFuncModule)) { + return; + } + if (listenerTypes.stream().anyMatch(this::isHTTPListener)) { + validate(syntaxNodeAnalysisContext, serviceDeclarationNode); + } + } + } + + + + private boolean isHTTPListener(TypeSymbol listenerType) { + if (listenerType.nameEquals("HttpListener")) { + return true; + } + return false; + } + + private boolean isListenerBelongsToAzureFuncModule(TypeSymbol listenerType) { + if (listenerType.typeKind() == TypeDescKind.UNION) { + return ((UnionTypeSymbol) listenerType).memberTypeDescriptors().stream() + .filter(typeDescriptor -> typeDescriptor instanceof TypeReferenceTypeSymbol) + .map(typeReferenceTypeSymbol -> (TypeReferenceTypeSymbol) typeReferenceTypeSymbol) + .anyMatch(typeReferenceTypeSymbol -> isAzureFuncModule(typeReferenceTypeSymbol.getModule().get())); + } + + if (listenerType.typeKind() == TypeDescKind.TYPE_REFERENCE) { + return isAzureFuncModule(((TypeReferenceTypeSymbol) listenerType).typeDescriptor().getModule().get()); + } + return false; + } + + private boolean isAzureFuncModule(ModuleSymbol moduleSymbol) { + return AZURE_FUNCTIONS_MODULE_NAME.equals(moduleSymbol.getName().get()) && + AZURE_FUNCTIONS_PACKAGE_ORG.equals(moduleSymbol.id().orgName()); + } + +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java new file mode 100644 index 00000000..010fc142 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.validators; + +import io.ballerina.compiler.api.symbols.AnnotationSymbol; +import io.ballerina.compiler.api.symbols.ArrayTypeSymbol; +import io.ballerina.compiler.api.symbols.IntersectionTypeSymbol; +import io.ballerina.compiler.api.symbols.ModuleSymbol; +import io.ballerina.compiler.api.symbols.ParameterSymbol; +import io.ballerina.compiler.api.symbols.RecordFieldSymbol; +import io.ballerina.compiler.api.symbols.RecordTypeSymbol; +import io.ballerina.compiler.api.symbols.ResourceMethodSymbol; +import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.symbols.UnionTypeSymbol; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.tools.diagnostics.Location; +import org.ballerinax.azurefunctions.AzureDiagnosticCodes; +import org.wso2.ballerinalang.compiler.diagnostic.properties.BSymbolicProperty; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS; +import static org.ballerinax.azurefunctions.Constants.HEADER_ANNOTATION_TYPE; +import static org.ballerinax.azurefunctions.Constants.HTTP; +import static org.ballerinax.azurefunctions.Util.updateDiagnostic; + + +/** + * Validates azure-function service on a HTTPListener . + */ +class HttpListenerValidator { + + static void validate(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, + ServiceDeclarationNode serviceDeclarationNode) { + extractServiceAnnotationAndValidate(syntaxNodeAnalysisContext, serviceDeclarationNode); + NodeList members = serviceDeclarationNode.members(); + for (Node member : members) { + if (member.kind() == SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + validateResourceFunction(syntaxNodeAnalysisContext, (FunctionDefinitionNode) member); + } + } + + } + private static void validateResourceFunction(SyntaxNodeAnalysisContext ctx, FunctionDefinitionNode member) { + validateInputParameters(ctx, member); + //TODO : Other necessary validation for a resource function + } + + private static void extractServiceAnnotationAndValidate(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, + ServiceDeclarationNode serviceDeclarationNode) { + //TODO : Validate service annotation fields + } + + private static void validateInputParameters(SyntaxNodeAnalysisContext ctx, FunctionDefinitionNode member) { + Optional resourceMethodSymbolOptional = ctx.semanticModel().symbol(member); + Location paramLocation = member.location(); + if (resourceMethodSymbolOptional.isEmpty()) { + return; + } + Optional> parametersOptional = + ((ResourceMethodSymbol) resourceMethodSymbolOptional.get()).typeDescriptor().params(); + if (parametersOptional.isEmpty()) { + return; + } + if (parametersOptional.get().size() == 0) { + return; + } + + for (ParameterSymbol param : parametersOptional.get()) { + Optional paramLocationOptional = param.getLocation(); + if (paramLocationOptional.isPresent()) { + paramLocation = paramLocationOptional.get(); + } + Optional nameOptional = param.getName(); + String paramName = nameOptional.isEmpty() ? "" : nameOptional.get(); + + List annotations = param.annotations().stream() + .filter(annotationSymbol -> annotationSymbol.typeDescriptor().isPresent()) + .collect(Collectors.toList()); + + if (!annotations.isEmpty()) { + validateAnnotatedInputParam(ctx, paramLocation, param, paramName, annotations); + } + + } + } + + private static void validateAnnotatedInputParam(SyntaxNodeAnalysisContext ctx, Location paramLocation, + ParameterSymbol param, String paramName, + List annotations) { + + for (AnnotationSymbol annotation : annotations) { + Optional typeSymbolOptional = annotation.typeDescriptor(); + if (typeSymbolOptional.isEmpty()) { + reportInvalidParameter(ctx, paramLocation, paramName); + continue; + } + // validate annotation module + Optional moduleSymbolOptional = typeSymbolOptional.get().getModule(); + if (moduleSymbolOptional.isEmpty()) { + reportInvalidParameter(ctx, paramLocation, paramName); + continue; + } + Optional nameSymbolOptional = moduleSymbolOptional.get().getName(); + if (nameSymbolOptional.isEmpty()) { + reportInvalidParameter(ctx, paramLocation, paramName); + continue; + } + if (!HTTP.equals(nameSymbolOptional.get()) && !AZURE_FUNCTIONS.equals(nameSymbolOptional.get())) { + reportInvalidParameterAnnotation(ctx, paramLocation, paramName); + continue; + } + + Optional annotationTypeNameOptional = typeSymbolOptional.get().getName(); + if (annotationTypeNameOptional.isEmpty()) { + reportInvalidParameter(ctx, paramLocation, paramName); + continue; + } + String typeName = annotationTypeNameOptional.get(); + TypeSymbol typeDescriptor = param.typeDescriptor(); + if (typeDescriptor.typeKind() == TypeDescKind.INTERSECTION) { + typeDescriptor = + getEffectiveTypeFromReadonlyIntersection((IntersectionTypeSymbol) typeDescriptor); + if (typeDescriptor == null) { + reportInvalidIntersectionType(ctx, paramLocation, typeName); + continue; + } + } + if (HEADER_ANNOTATION_TYPE.equals(typeName)) { + if (annotations.size() == 2) { + reportInvalidMultipleAnnotation(ctx, paramLocation, paramName); + continue; + } + validateHeaderParamType(ctx, paramLocation, param, paramName, typeDescriptor); + break; + } + } + } + + private static void validateHeaderParamType(SyntaxNodeAnalysisContext ctx, Location paramLocation, Symbol param, + String paramName, TypeSymbol paramTypeDescriptor) { + switch (paramTypeDescriptor.typeKind()) { + case STRING: + case INT: + case DECIMAL: + case FLOAT: + case BOOLEAN: + break; + case ARRAY: + TypeSymbol arrTypeSymbol = ((ArrayTypeSymbol) paramTypeDescriptor).memberTypeDescriptor(); + TypeDescKind arrElementKind = arrTypeSymbol.typeKind(); + checkAllowedHeaderParamTypes(ctx, paramLocation, param, paramName, arrElementKind); + break; + case UNION: + List symbolList = ((UnionTypeSymbol) paramTypeDescriptor).memberTypeDescriptors(); + int size = symbolList.size(); + if (size > 2) { + reportInvalidUnionHeaderType(ctx, paramLocation, paramName); + return; + } + if (symbolList.stream().noneMatch(type -> type.typeKind() == TypeDescKind.NIL)) { + reportInvalidUnionHeaderType(ctx, paramLocation, paramName); + return; + } + for (TypeSymbol type : symbolList) { + TypeDescKind elementKind = type.typeKind(); + if (elementKind == TypeDescKind.ARRAY) { + elementKind = ((ArrayTypeSymbol) type).memberTypeDescriptor().typeKind(); + checkAllowedHeaderParamUnionType(ctx, paramLocation, param, paramName, elementKind); + continue; + } + if (elementKind == TypeDescKind.TYPE_REFERENCE) { + validateHeaderParamType(ctx, paramLocation, param, paramName, type); + return; + } + checkAllowedHeaderParamTypes(ctx, paramLocation, param, paramName, elementKind); + } + break; + case TYPE_REFERENCE: + TypeSymbol typeDescriptor = ((TypeReferenceTypeSymbol) paramTypeDescriptor).typeDescriptor(); + TypeDescKind typeDescKind = typeDescriptor.typeKind(); + if (typeDescKind == TypeDescKind.RECORD) { + validateHeaderRecordFields(ctx, paramLocation, typeDescriptor); + } else { + reportInvalidHeaderParameterType(ctx, paramLocation, paramName, param); + } + break; + case RECORD: + validateHeaderRecordFields(ctx, paramLocation, paramTypeDescriptor); + break; + default: + reportInvalidHeaderParameterType(ctx, paramLocation, paramName, param); + break; + } + } + + private static void checkAllowedHeaderParamTypes(SyntaxNodeAnalysisContext ctx, Location paramLocation, + Symbol param, String paramName, TypeDescKind elementKind) { + if (!isAllowedHeaderParamPureType(elementKind)) { + reportInvalidHeaderParameterType(ctx, paramLocation, paramName, param); + } + } + + + private static void checkAllowedHeaderParamUnionType(SyntaxNodeAnalysisContext ctx, Location paramLocation, + Symbol param, String paramName, TypeDescKind elementKind) { + if (!isAllowedHeaderParamPureType(elementKind)) { + reportInvalidUnionHeaderType(ctx, paramLocation, paramName); + } + } + + private static boolean isAllowedHeaderParamPureType(TypeDescKind elementKind) { + return elementKind == TypeDescKind.NIL || elementKind == TypeDescKind.STRING || + elementKind == TypeDescKind.INT || elementKind == TypeDescKind.FLOAT || + elementKind == TypeDescKind.DECIMAL || elementKind == TypeDescKind.BOOLEAN; + } + + private static void validateHeaderRecordFields(SyntaxNodeAnalysisContext ctx, Location paramLocation, + TypeSymbol typeDescriptor) { + RecordTypeSymbol recordTypeSymbol = (RecordTypeSymbol) typeDescriptor; + Map recordFieldSymbolMap = recordTypeSymbol.fieldDescriptors(); + for (Map.Entry entry : recordFieldSymbolMap.entrySet()) { + RecordFieldSymbol value = entry.getValue(); + typeDescriptor = value.typeDescriptor(); + String typeName = typeDescriptor.signature(); + TypeDescKind typeDescKind = typeDescriptor.typeKind(); + if (typeDescKind == TypeDescKind.INTERSECTION) { + typeDescriptor = getEffectiveTypeFromReadonlyIntersection((IntersectionTypeSymbol) typeDescriptor); + if (typeDescriptor == null) { + reportInvalidIntersectionType(ctx, paramLocation, typeName); + continue; + } + } + validateHeaderParamType(ctx, paramLocation, value, entry.getKey(), typeDescriptor); + } + Optional restTypeDescriptor = recordTypeSymbol.restTypeDescriptor(); + if (restTypeDescriptor.isPresent()) { + reportInvalidHeaderRecordRestFieldType(ctx, paramLocation); + } + } + + private static TypeSymbol getEffectiveTypeFromReadonlyIntersection(IntersectionTypeSymbol intersectionTypeSymbol) { + List effectiveTypes = new ArrayList<>(); + for (TypeSymbol typeSymbol : intersectionTypeSymbol.memberTypeDescriptors()) { + if (typeSymbol.typeKind() == TypeDescKind.READONLY) { + continue; + } + effectiveTypes.add(typeSymbol); + } + if (effectiveTypes.size() == 1) { + return effectiveTypes.get(0); + } + return null; + } + + private static void reportInvalidParameterAnnotation(SyntaxNodeAnalysisContext ctx, Location location, + String paramName) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_001, paramName); + } + + private static void reportInvalidParameter(SyntaxNodeAnalysisContext ctx, Location location, + String paramName) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_002, paramName); + } + + + + private static void reportInvalidHeaderParameterType(SyntaxNodeAnalysisContext ctx, Location location, + String paramName, Symbol parameterSymbol) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_003, List.of(new BSymbolicProperty(parameterSymbol)) + , paramName); + } + + private static void reportInvalidUnionHeaderType(SyntaxNodeAnalysisContext ctx, Location location, + String paramName) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_004, paramName); + } + + private static void reportInvalidIntersectionType(SyntaxNodeAnalysisContext ctx, Location location, + String typeName) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_005, typeName); + } + + private static void reportInvalidHeaderRecordRestFieldType(SyntaxNodeAnalysisContext ctx, Location location) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_006); + } + + private static void reportInvalidMultipleAnnotation(SyntaxNodeAnalysisContext ctx, Location location, + String paramName) { + updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_007, paramName); + } +} + + From af64a53c65837f4435d5ad24c434ceecafd0a409 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 13 Sep 2022 15:08:45 +0530 Subject: [PATCH 28/59] Add testcases for header annotation --- ballerina-tests/main.bal | 104 +++++++++- .../tests/resources/httpHeaderTest.json | 119 +++++++++++ ballerina-tests/tests/test.bal | 192 ++++++++++++++++++ 3 files changed, 413 insertions(+), 2 deletions(-) create mode 100644 ballerina-tests/tests/resources/httpHeaderTest.json diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 136f9330..321c5b05 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -1,8 +1,6 @@ import ballerinax/azure_functions as af; import ballerina/http; -listener af:HttpListener ep = new (); - public type DBEntry record { string id; }; @@ -12,6 +10,17 @@ type Person record { int age; }; +public type RateLimitHeaders record {| + int Content\-Length; + string Content\-Type; +|}; + +public type NoHeaderVal record {| + int Content\-Length; + string Content\-Type; + string Content\-Type1; +|}; + listener af:HttpListener ep1 = new (); service /hello\- on ep1 { @@ -21,7 +30,98 @@ service /hello\- on ep1 { } } +listener af:HttpListener ep2 = new (); + +@http:ServiceConfig { + treatNilableAsOptional: false +} +service /hello on ep2 { + resource function post httpHeaderTest8(@http:Header string? Hoste) returns string? { + return Hoste; + } + + resource function post httpHeaderTest14(@http:Header string Hoste) returns string { + return Hoste; + + } + + resource function post httpHeaderTest15(@http:Header string Hos) returns string { + return Hos; + + } + + resource function post httpHeaderTest16(@http:Header string? Hos) returns string? { + return Hos; + + } +} + +listener af:HttpListener ep = new (); + service /hello on ep { + resource function post httpHeaderTest1(@http:Header {name: "Content-Type"} string contentType) returns string { + + return contentType; + } + + resource function post httpHeaderTest2(@http:Header string Host) returns string { + + return Host; + + } + + resource function post httpHeaderTest3(@http:Header {name: "Content-Length"} int contentLength) returns int { + + return contentLength + 10; + + } + + resource function post httpHeaderTest4(@http:Header {name: "Content-Length"} int[] contentLength) returns int { + + return contentLength[0] + 15; + + } + + resource function post httpHeaderTest5(@http:Header string[] test) returns string { + return test[0]; + + } + + resource function post httpHeaderTest6(@http:Header RateLimitHeaders rateLimiters) returns int { + return rateLimiters.Content\-Length + 100; + + } + + resource function post httpHeaderTest7(@http:Header string? Host) returns string? { + return Host; + + } + + resource function post httpHeaderTest9(@http:Header string Hoste) returns string { + return Hoste; + + } + + resource function post httpHeaderTest10(@http:Header string Hos) returns string { + return Hos; + + } + + resource function post httpHeaderTest11(@http:Header NoHeaderVal noHeaderVal) returns int { + return noHeaderVal.Content\-Length + 100; + + } + + resource function post httpHeaderTest12(@http:Header string? Hoste) returns string? { + return Hoste; + + } + + resource function post httpHeaderTest13(@http:Header string? Hos) returns string? { + return Hos; + + } + resource function default all() returns @af:HttpOutput string { return "Hello from all"; } diff --git a/ballerina-tests/tests/resources/httpHeaderTest.json b/ballerina-tests/tests/resources/httpHeaderTest.json new file mode 100644 index 00000000..abdfbab6 --- /dev/null +++ b/ballerina-tests/tests/resources/httpHeaderTest.json @@ -0,0 +1,119 @@ +{ + "Data":{ + "httpPayload":{ + "Url":"https://az-func-http-test.azurewebsites.net/api/hello/FUNC_NAME", + "Method":"POST", + "Query":{ + + }, + "Headers":{ + "Content-Length":[ + "5" + ], + "Content-Type":[ + "text/plain" + ], + "Host":[ + "az-func-http-test.azurewebsites.net" + ], + "Max-Forwards":[ + "10" + ], + "User-Agent":[ + "ballerina" + ], + "test":[ + "12, 13, 14" + ], + "Hos":[ + + ], + "X-ARR-LOG-ID":[ + "77ef8e37-520e-417b-a972-30746b719b60" + ], + "CLIENT-IP":[ + "45.121.88.92:53880" + ], + "DISGUISED-HOST":[ + "az-func-http-test.azurewebsites.net" + ], + "X-SITE-DEPLOYMENT-ID":[ + "az-func-http-test" + ], + "WAS-DEFAULT-HOSTNAME":[ + "az-func-http-test.azurewebsites.net" + ], + "X-Forwarded-Proto":[ + "https" + ], + "X-AppService-Proto":[ + "https" + ], + "X-ARR-SSL":[ + "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US" + ], + "X-Forwarded-TlsVersion":[ + "1.2" + ], + "X-Forwarded-For":[ + "45.121.88.92:53880" + ], + "X-Original-URL":[ + "/api/hello/FUNC_NAME" + ], + "X-WAWS-Unencoded-URL":[ + "/api/hello/FUNC_NAME" + ] + }, + "Params":{ + + }, + "Identities":[ + { + "AuthenticationType":null, + "IsAuthenticated":false, + "Actor":null, + "BootstrapContext":null, + "Claims":[ + + ], + "Label":null, + "Name":null, + "NameClaimType":"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", + "RoleClaimType":"http://schemas.microsoft.com/ws/2008/06/identity/claims/role" + } + ], + "Body":"hello" + } + }, + "Metadata":{ + "Query":{ + + }, + "Headers":{ + "Content-Length":"5", + "Content-Type":"text/plain", + "Host":"az-func-http-test.azurewebsites.net", + "Max-Forwards":"10", + "User-Agent":"ballerina", + "test":"12, 13, 14", + "X-ARR-LOG-ID":"77ef8e37-520e-417b-a972-30746b719b60", + "CLIENT-IP":"45.121.88.92:53880", + "DISGUISED-HOST":"az-func-http-test.azurewebsites.net", + "X-SITE-DEPLOYMENT-ID":"az-func-http-test", + "WAS-DEFAULT-HOSTNAME":"az-func-http-test.azurewebsites.net", + "X-Forwarded-Proto":"https", + "X-AppService-Proto":"https", + "X-ARR-SSL":"2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", + "X-Forwarded-TlsVersion":"1.2", + "X-Forwarded-For":"45.121.88.92:53880", + "X-Original-URL":"/api/hello/FUNC_NAME", + "X-WAWS-Unencoded-URL":"/api/hello/FUNC_NAME" + }, + "sys":{ + "MethodName":"post-hello-FUNC_NAME", + "UtcNow":"2022-09-01T10:12:54.2952774Z", + "RandGuid":"f891f95b-cc04-4e8c-b440-24b8da192b28" + } + } + } \ No newline at end of file diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 6c39fa05..53d50630 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -4,6 +4,198 @@ import ballerina/lang.value; import ballerina/regex; import ballerina/test; +@test:Config {} +function httpHeaderTest1() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest1"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest1", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"text/plain"}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest2() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest2"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest2", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"az-func-http-test.azurewebsites.net"}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest3() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest3"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest3", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":15}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest4() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest4"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest4", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":20}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest5() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest5"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest5", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"12"}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest6() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest6"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest6", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":105}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest7() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest7"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest7", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"az-func-http-test.azurewebsites.net"}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest8() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest8"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest8", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest9() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest9"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest9", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest10() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest10"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest10", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest11() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest11"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest11", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Content-Type1'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest12() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest12"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest12", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"202"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest13() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest13"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest13", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"202"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest14() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest14"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest14", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest15() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest15"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest15", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + +@test:Config {} +function httpHeaderTest16() returns error? { + final http:Client clientEndpoint = check new ("http://localhost:3000"); + string jsonFilePath = "./tests/resources/httpHeaderTest.json"; + string readString = check io:fileReadString(jsonFilePath); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest16"); + json readJson = check value:fromJsonString(replacedString); + json resp = check clientEndpoint->post("/post-hello-httpHeaderTest16", readJson); + json expectedResp = {"Outputs":{"resp":{"statusCode":"202"}},"Logs":[],"ReturnValue":null}; + test:assertEquals(resp, expectedResp); +} + @test:Config {} function testEscapeSequences() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); From 4298de295085cc21dcf064c24ee32290b5abde92 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 13 Sep 2022 15:09:24 +0530 Subject: [PATCH 29/59] Add compiler plugin test cases --- .../test/ProjectValidationTests.java | 118 +++++++++---- .../src/test/resources/testng.xml | 2 +- .../http-header-annotation/.devcontainer.json | 4 + .../http-header-annotation/.gitignore | 1 + .../http-header-annotation/Ballerina.toml | 8 + .../http-header-annotation/main.bal | 161 ++++++++++++++++++ 6 files changed, 262 insertions(+), 32 deletions(-) create mode 100644 compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.devcontainer.json create mode 100644 compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.gitignore create mode 100644 compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java index 2810f281..d1a5b73a 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java @@ -20,14 +20,13 @@ import io.ballerina.projects.DiagnosticResult; import io.ballerina.projects.PackageCompilation; import io.ballerina.projects.directory.BuildProject; -import io.ballerina.projects.directory.SingleFileProject; import io.ballerina.tools.diagnostics.Diagnostic; import org.testng.Assert; import org.testng.annotations.Test; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Iterator; + /** * Contains the project related validations of azure functions. @@ -38,39 +37,96 @@ public class ProjectValidationTests { protected static final Path RESOURCE_DIRECTORY = Paths.get("src/test/resources/validations/"); - @Test - public void mainFunctionTest() { - BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("main")); - PackageCompilation compilation = project.currentPackage().getCompilation(); - DiagnosticResult diagnosticResult = compilation.diagnosticResult(); - Assert.assertEquals(diagnosticResult.errorCount(), 1); - Diagnostic diagnostic = diagnosticResult.errors().iterator().next(); - Assert.assertEquals(diagnostic.message(), "main function is not allowed in azure functions"); - } - @Test - public void submoduleTest() { - BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("submodule")); - PackageCompilation compilation = project.currentPackage().getCompilation(); - DiagnosticResult diagnosticResult = compilation.diagnosticResult(); - Assert.assertEquals(diagnosticResult.errorCount(), 2); - Iterator iterator = diagnosticResult.errors().iterator(); - Diagnostic unusedModuleDiag = iterator.next(); - Assert.assertEquals(unusedModuleDiag.message(), "unused module prefix 'mod1'"); - Diagnostic submoduleDiag = iterator.next(); - Assert.assertEquals(submoduleDiag.message(), "azure functions is not allowed inside sub modules"); - } + +// @Test +// public void mainFunctionTest() { +// BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("main")); +// PackageCompilation compilation = project.currentPackage().getCompilation(); +// DiagnosticResult diagnosticResult = compilation.diagnosticResult(); +// Assert.assertEquals(diagnosticResult.errorCount(), 1); +// Diagnostic diagnostic = diagnosticResult.errors().iterator().next(); +// Assert.assertEquals(diagnostic.message(), "main function is not allowed in azure functions"); +// } +// +// @Test +// public void submoduleTest() { +// BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("submodule")); +// PackageCompilation compilation = project.currentPackage().getCompilation(); +// DiagnosticResult diagnosticResult = compilation.diagnosticResult(); +// Assert.assertEquals(diagnosticResult.errorCount(), 2); +// Iterator iterator = diagnosticResult.errors().iterator(); +// Diagnostic unusedModuleDiag = iterator.next(); +// Assert.assertEquals(unusedModuleDiag.message(), "unused module prefix 'mod1'"); +// Diagnostic submoduleDiag = iterator.next(); +// Assert.assertEquals(submoduleDiag.message(), "azure functions is not allowed inside sub modules"); +// } +// +// @Test +// public void singleFileTest() { +// SingleFileProject project = SingleFileProject.load(RESOURCE_DIRECTORY.resolve("single-file").resolve( +// "functions.bal")); +// PackageCompilation compilation = project.currentPackage().getCompilation(); +// DiagnosticResult diagnosticResult = compilation.diagnosticResult(); +// Assert.assertEquals(diagnosticResult.errorCount(), 1); +// Iterator iterator = diagnosticResult.errors().iterator(); +// Diagnostic unusedModuleDiag = iterator.next(); +// Assert.assertEquals(unusedModuleDiag.message(), "azure functions are only allowed in ballerina" + +// " projects"); +// } @Test - public void singleFileTest() { - SingleFileProject project = SingleFileProject.load(RESOURCE_DIRECTORY.resolve("single-file").resolve( - "functions.bal")); + public void headerAnnotationTest() { + BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("http-header-annotation")); PackageCompilation compilation = project.currentPackage().getCompilation(); DiagnosticResult diagnosticResult = compilation.diagnosticResult(); - Assert.assertEquals(diagnosticResult.errorCount(), 1); - Iterator iterator = diagnosticResult.errors().iterator(); - Diagnostic unusedModuleDiag = iterator.next(); - Assert.assertEquals(unusedModuleDiag.message(), "azure functions are only allowed in ballerina" + - " projects"); + Object [] diagnostics = diagnosticResult.errors().toArray(); + Assert.assertEquals(diagnosticResult.errorCount(), 13); + String diagnosticMessage0 = "invalid annotation type on param 'a'"; + String diagnosticMessage1 = "invalid union type of header param 'xRate': one of the 'string','int','float'," + + "'decimal','boolean' types, an array of the above types or a record which consists of the above " + + "types can only be union with '()'. Eg: string|() or string[]|()"; + String diagnosticMessage2 = "invalid type of header param 'abc': One of the following types is expected: " + + "'string','int','float','decimal','boolean', an array of the above types or a record which consists " + + "of the above types"; + String diagnosticMessage3 = "invalid union type of header param 'abc': one of the 'string','int','float'," + + "'decimal','boolean' types, an array of the above types or a record which consists of the above types " + + "can only be union with '()'. Eg: string|() or string[]|()"; + String diagnosticMessage4 = "invalid union type of header param 'abc': one of the 'string','int','float'," + + "'decimal','boolean' types, an array of the above types or a record which consists of the above " + + "types can only be union with '()'. Eg: string|() or string[]|()"; + String diagnosticMessage5 = "rest fields are not allowed for header binding records. " + + "Use 'http:Headers' type to access all headers"; + String diagnosticMessage6 = "rest fields are not allowed for header binding records. " + + "Use 'http:Headers' type to access all headers"; + String diagnosticMessage7 = "invalid type of header param 'abc': One of the following types is expected: " + + "'string','int','float','decimal','boolean', an array of the above types or a record which " + + "consists of the above types"; + String diagnosticMessage8 = "invalid multiple resource parameter annotations for 'abc'"; + String diagnosticMessage9 = "invalid type of header param 'abc': One of the following types is expected: " + + "'string','int','float','decimal','boolean', an array of the above types or a record which " + + "consists of the above types"; + String diagnosticMessage10 = "invalid union type of header param 'abc': one of the 'string','int','float'," + + "'decimal','boolean' types, an array of the above types or a record which consists of the " + + "above types can only be union with '()'. Eg: string|() or string[]|()"; + String diagnosticMessage11 = "invalid type of header param 'abc': One of the following types is expected: " + + "'string','int','float','decimal','boolean', an array of the above types or a record which " + + "consists of the above types"; + String diagnosticMessage12 = "invalid union type of header param 'abc': one of the 'string','int','float'," + + "'decimal','boolean' types, an array of the above types or a record which consists of " + + "the above types can only be union with '()'. Eg: string|() or string[]|()"; + Assert.assertEquals(((Diagnostic) diagnostics[0]).diagnosticInfo().messageFormat(), diagnosticMessage0); + Assert.assertEquals(((Diagnostic) diagnostics[1]).diagnosticInfo().messageFormat(), diagnosticMessage1); + Assert.assertEquals(((Diagnostic) diagnostics[2]).diagnosticInfo().messageFormat(), diagnosticMessage2); + Assert.assertEquals(((Diagnostic) diagnostics[3]).diagnosticInfo().messageFormat(), diagnosticMessage3); + Assert.assertEquals(((Diagnostic) diagnostics[4]).diagnosticInfo().messageFormat(), diagnosticMessage4); + Assert.assertEquals(((Diagnostic) diagnostics[5]).diagnosticInfo().messageFormat(), diagnosticMessage5); + Assert.assertEquals(((Diagnostic) diagnostics[6]).diagnosticInfo().messageFormat(), diagnosticMessage6); + Assert.assertEquals(((Diagnostic) diagnostics[7]).diagnosticInfo().messageFormat(), diagnosticMessage7); + Assert.assertEquals(((Diagnostic) diagnostics[8]).diagnosticInfo().messageFormat(), diagnosticMessage8); + Assert.assertEquals(((Diagnostic) diagnostics[9]).diagnosticInfo().messageFormat(), diagnosticMessage9); + Assert.assertEquals(((Diagnostic) diagnostics[10]).diagnosticInfo().messageFormat(), diagnosticMessage10); + Assert.assertEquals(((Diagnostic) diagnostics[11]).diagnosticInfo().messageFormat(), diagnosticMessage11); + Assert.assertEquals(((Diagnostic) diagnostics[12]).diagnosticInfo().messageFormat(), diagnosticMessage12); } } diff --git a/compiler-plugin-tests/src/test/resources/testng.xml b/compiler-plugin-tests/src/test/resources/testng.xml index a3a40529..58f6b4d1 100644 --- a/compiler-plugin-tests/src/test/resources/testng.xml +++ b/compiler-plugin-tests/src/test/resources/testng.xml @@ -22,7 +22,7 @@ - + diff --git a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.devcontainer.json b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.devcontainer.json new file mode 100644 index 00000000..ed3a4ddc --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.1.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.gitignore b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/.gitignore @@ -0,0 +1 @@ +target diff --git a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml new file mode 100644 index 00000000..a2ead863 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "luheerathan" +name = "http_header_annotation" +version = "0.1.0" +distribution = "2201.1.0" + +[build-options] +observabilityIncluded = true diff --git a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal new file mode 100644 index 00000000..c9bf2556 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal @@ -0,0 +1,161 @@ +import ballerinax/azure_functions as af; +import ballerina/http; + +listener af:HttpListener ep = new (); + +type Person record {| + readonly int id; +|}; + +public annotation Person Pp on parameter; + +public type DBEntry record { + string id; +}; + +type RateLimitHeaders record {| + string x\-rate\-limit\-id; + int? x\-rate\-limit\-remaining; + string[]? x\-rate\-limit\-types; +|}; + +type TestRecord record {| + string[]|string xRate; +|}; + +type NestedRecord record {| + RateLimitHeaders xRate; +|}; + +service "hello" on ep { + + resource function get allowedHeaderRecord(@http:Header string|() clientKey) returns string|() { + + return clientKey; + } + + resource function get unallowedAnnot(@Pp {id: 0} string a) returns string { + + return a; + } + + resource function get allowedAfAnnot(@af:CosmosDBInput { + connectionStringSetting: "CosmosDBConnection", + databaseName: "db1", + collectionName: "c2", + sqlQuery: "SELECT * FROM Items" + } DBEntry[] input1) returns string { + + return "Alpha"; + } + + resource function get headerString(@http:Header {name: "x-type"} string abc) returns string { + return "done"; + } + + resource function get headerStringArr(@http:Header {name: "x-type"} string[] abc) returns string { + return "done"; + } + + resource function get headerStringNil(@http:Header {name: "x-type"} string? abc) returns string { + return "done"; + } + + resource function get headerStringArrNil(@http:Header {name: "x-type"} string[]? abc) returns string { + return "done"; + } + + resource function get headerRecord(@http:Header {name: "x-type"} RateLimitHeaders abc) returns string { + return "done"; + } + + resource function get headerRecordReadonly(@http:Header readonly & RateLimitHeaders abc) returns string { + return "done"; + } + + resource function get headerRecordWithInvalidFieldUnion(@http:Header TestRecord abc) returns string { + return "done"; + } + + resource function get headerRecordNil(@http:Header {name: "x-type"} RateLimitHeaders? abc) returns string { + return "done"; + } + + resource function get headerRecordArr(@http:Header {name: "x-type"} RateLimitHeaders[] abc) returns string { + return "done"; + } + + resource function get headerRecordArrNil(@http:Header RateLimitHeaders[]? abc) returns string { + return "done"; + } + + resource function get headerRecordUnionStr(@http:Header RateLimitHeaders|string abc) returns string { + return "done"; + } + + resource function get headerInlineRecord(@http:Header record {|string hello;|} abc) returns string { + return "done"; + } + + resource function get headerInlineRestAndStringRecord(@http:Header record {|string hello;string...; |} abc) returns + string { + return "done"; + } + + resource function get headerInlineRestRecord(@http:Header record {|string...; |} abc) returns string { + return "done"; + } + + resource function get headerInt(@http:Header int foo, @http:Header int[] bar, @http:Header int? baz, + @http:Header int[]? daz, @http:Header readonly & int dawz) returns string { + return "done"; + } + + resource function get headerDecimal(@http:Header decimal foo, @http:Header decimal[] bar, @http:Header decimal? baz, + @http:Header decimal[]? daz, @http:Header readonly & decimal? dawz) returns string { + return "done"; + } + + resource function get headerFloat(@http:Header float foo, @http:Header float[] bar, @http:Header float? baz, + @http:Header float[]? daz, @http:Header readonly & float dawz) returns string { + return "done"; + } + + resource function get headerBool(@http:Header boolean foo, @http:Header boolean[] bar, @http:Header boolean? baz, + @http:Header boolean[]? daz, @http:Header readonly & boolean dawz) returns string { + return "done"; + } + + resource function get headerErr1(@http:Header {name: "x-type"} json abc) returns string { + return "done"; + } + + resource function post headerErr2(@http:Header @http:Payload string abc) returns string { + return "done"; + } + + resource function get headerErr3(@http:Header {name: "x-type"} http:Request abc) returns string { + return "done"; + } + + resource function get headerErr4(@http:Header {name: "x-type"} string|json abc) returns string { + return "done"; + } + + resource function get headerErr5(@http:Header {name: "x-type"} json? abc) returns string { + return "done"; + } + + resource function get headerErr6(@http:Header {name: "x-type"} string|json|xml abc) returns string { + return "done"; + } + + resource function get headerErr7(@http:Header {name: "x-type"} int[] abc) returns string { + return "done"; + } + + resource function get headerRecordWithRecordField(@http:Header NestedRecord abc) returns string { + return "done"; + } + +} From 8b6c7589d3686119332d9fed94e049bb9a4e5ba0 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 13 Sep 2022 15:13:58 +0530 Subject: [PATCH 30/59] Remove old compiler plugin tests --- .../test/ProjectValidationTests.java | 38 ------------------- .../resources/validations/main/Ballerina.toml | 7 ---- .../test/resources/validations/main/main.bal | 30 --------------- .../validations/single-file/functions.bal | 8 ---- .../validations/submodule/Ballerina.toml | 7 ---- .../resources/validations/submodule/main.bal | 27 ------------- .../submodule/modules/mod1/Package.md | 5 --- .../submodule/modules/mod1/mod1.bal | 22 ----------- 8 files changed, 144 deletions(-) delete mode 100644 compiler-plugin-tests/src/test/resources/validations/main/Ballerina.toml delete mode 100644 compiler-plugin-tests/src/test/resources/validations/main/main.bal delete mode 100644 compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal delete mode 100644 compiler-plugin-tests/src/test/resources/validations/submodule/Ballerina.toml delete mode 100644 compiler-plugin-tests/src/test/resources/validations/submodule/main.bal delete mode 100644 compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/Package.md delete mode 100644 compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java index d1a5b73a..19af87f1 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java @@ -37,44 +37,6 @@ public class ProjectValidationTests { protected static final Path RESOURCE_DIRECTORY = Paths.get("src/test/resources/validations/"); - - -// @Test -// public void mainFunctionTest() { -// BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("main")); -// PackageCompilation compilation = project.currentPackage().getCompilation(); -// DiagnosticResult diagnosticResult = compilation.diagnosticResult(); -// Assert.assertEquals(diagnosticResult.errorCount(), 1); -// Diagnostic diagnostic = diagnosticResult.errors().iterator().next(); -// Assert.assertEquals(diagnostic.message(), "main function is not allowed in azure functions"); -// } -// -// @Test -// public void submoduleTest() { -// BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("submodule")); -// PackageCompilation compilation = project.currentPackage().getCompilation(); -// DiagnosticResult diagnosticResult = compilation.diagnosticResult(); -// Assert.assertEquals(diagnosticResult.errorCount(), 2); -// Iterator iterator = diagnosticResult.errors().iterator(); -// Diagnostic unusedModuleDiag = iterator.next(); -// Assert.assertEquals(unusedModuleDiag.message(), "unused module prefix 'mod1'"); -// Diagnostic submoduleDiag = iterator.next(); -// Assert.assertEquals(submoduleDiag.message(), "azure functions is not allowed inside sub modules"); -// } -// -// @Test -// public void singleFileTest() { -// SingleFileProject project = SingleFileProject.load(RESOURCE_DIRECTORY.resolve("single-file").resolve( -// "functions.bal")); -// PackageCompilation compilation = project.currentPackage().getCompilation(); -// DiagnosticResult diagnosticResult = compilation.diagnosticResult(); -// Assert.assertEquals(diagnosticResult.errorCount(), 1); -// Iterator iterator = diagnosticResult.errors().iterator(); -// Diagnostic unusedModuleDiag = iterator.next(); -// Assert.assertEquals(unusedModuleDiag.message(), "azure functions are only allowed in ballerina" + -// " projects"); -// } - @Test public void headerAnnotationTest() { BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("http-header-annotation")); diff --git a/compiler-plugin-tests/src/test/resources/validations/main/Ballerina.toml b/compiler-plugin-tests/src/test/resources/validations/main/Ballerina.toml deleted file mode 100644 index 17734123..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/main/Ballerina.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -org = "anjana" -name = "main" -version = "0.1.0" - -[build-options] -observabilityIncluded = true diff --git a/compiler-plugin-tests/src/test/resources/validations/main/main.bal b/compiler-plugin-tests/src/test/resources/validations/main/main.bal deleted file mode 100644 index f3a85429..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/main/main.bal +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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/io; -import ballerinax/azure_functions as af; - -// HTTP request/response with no authentication -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HttpOutput string|error { - return "Hello, " + payload + "!"; -} - -public function main() { - io:println("Hello, World!"); -} - diff --git a/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal b/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal deleted file mode 100644 index 8a21e546..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/single-file/functions.bal +++ /dev/null @@ -1,8 +0,0 @@ -import ballerinax/azure_functions as af; - -// HTTP request/response with no authentication -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HttpOutput string|error { - return "Hello, " + payload + "!"; -} diff --git a/compiler-plugin-tests/src/test/resources/validations/submodule/Ballerina.toml b/compiler-plugin-tests/src/test/resources/validations/submodule/Ballerina.toml deleted file mode 100644 index 04de7108..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/submodule/Ballerina.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -org = "anjana" -name = "submodule" -version = "0.1.0" - -[build-options] -observabilityIncluded = true diff --git a/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal b/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal deleted file mode 100644 index 15aa1353..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/submodule/main.bal +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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 ballerinax/azure_functions as af; -import submodule.mod1; - -// HTTP request/response with no authentication -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HttpOutput string|error { - return "Hello, " + payload + "!"; -} - - diff --git a/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/Package.md b/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/Package.md deleted file mode 100644 index 32f95f51..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/Package.md +++ /dev/null @@ -1,5 +0,0 @@ -Prints "Hello World!" with a hello function. -[//]: # (above is the package summary) - -# Package Overview -Prints "Hello World!" as the output to the command line using a hello function. diff --git a/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal b/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal deleted file mode 100644 index 44f3f03d..00000000 --- a/compiler-plugin-tests/src/test/resources/validations/submodule/modules/mod1/mod1.bal +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2021 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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 ballerinax/azure_functions as af; - -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HttpOutput string|error { - return "Hello, " + payload + "!"; -} From 6eab2db3e89e0312f930a058c0a96f06fd387b5d Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 13 Sep 2022 17:42:50 +0530 Subject: [PATCH 31/59] Modify based on review --- ballerina-tests/main.bal | 42 ++++---- .../tests/resources/httpHeaderTest.json | 14 +-- ballerina-tests/tests/test.bal | 96 +++++++++---------- ballerina/annotation.bal | 2 +- ballerina/errors.bal | 16 ++++ .../test/ProjectValidationTests.java | 2 +- .../src/test/resources/testng.xml | 2 +- .../azurefunctions/AzureCompilerPlugin.java | 4 +- .../azurefunctions/AzureDiagnosticCodes.java | 2 +- .../AzureFunctionsCodeAnalyzer.java | 2 +- .../ballerinax/azurefunctions/Constants.java | 2 +- .../org/ballerinax/azurefunctions/Util.java | 17 ++++ .../AzureFunctionsCodeAnalyzerTask.java | 2 +- .../validators/HttpListenerValidator.java | 2 +- .../stdlib/azure/functions/Constants.java | 91 +++++++++--------- .../stdlib/azure/functions/HttpResource.java | 2 +- 16 files changed, 166 insertions(+), 132 deletions(-) diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 321c5b05..5c16e318 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -35,93 +35,97 @@ listener af:HttpListener ep2 = new (); @http:ServiceConfig { treatNilableAsOptional: false } -service /hello on ep2 { - resource function post httpHeaderTest8(@http:Header string? Hoste) returns string? { +service /httpHeader on ep2 { + resource function post nonTreatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? Hoste) returns string? { return Hoste; } - resource function post httpHeaderTest14(@http:Header string Hoste) returns string { + resource function post nonTreatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string Hoste) returns string { return Hoste; } - resource function post httpHeaderTest15(@http:Header string Hos) returns string { + resource function post nonTreatNilAsOpt\-nonNil\-HeaderTest(@http:Header string Hos) returns string { return Hos; } - resource function post httpHeaderTest16(@http:Header string? Hos) returns string? { + resource function post nonTreatNilAsOpt\-Nil\-HeaderTest(@http:Header string? Hos) returns string? { return Hos; } } -listener af:HttpListener ep = new (); +listener af:HttpListener ep3 = new (); -service /hello on ep { - resource function post httpHeaderTest1(@http:Header {name: "Content-Type"} string contentType) returns string { +service /httpHeader on ep3 { + resource function post retrFromAnnotField(@http:Header {name: "Content-Type"} string contentType) returns string { return contentType; } - resource function post httpHeaderTest2(@http:Header string Host) returns string { + resource function post retrFromParam(@http:Header string Host) returns string { return Host; } - resource function post httpHeaderTest3(@http:Header {name: "Content-Length"} int contentLength) returns int { + resource function post retrSingleVal(@http:Header {name: "Content-Length"} int contentLength) returns int { return contentLength + 10; } - resource function post httpHeaderTest4(@http:Header {name: "Content-Length"} int[] contentLength) returns int { + resource function post retrArrVal(@http:Header {name: "Content-Length"} int[] contentLength) returns int { return contentLength[0] + 15; } - resource function post httpHeaderTest5(@http:Header string[] test) returns string { + resource function post retrArrValStr(@http:Header string[] test) returns string { return test[0]; } - resource function post httpHeaderTest6(@http:Header RateLimitHeaders rateLimiters) returns int { + resource function post retrAsRecord(@http:Header RateLimitHeaders rateLimiters) returns int { return rateLimiters.Content\-Length + 100; } - resource function post httpHeaderTest7(@http:Header string? Host) returns string? { + resource function post retrNilable(@http:Header string? Host) returns string? { return Host; } - resource function post httpHeaderTest9(@http:Header string Hoste) returns string { + resource function post treatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string Hoste) returns string { return Hoste; } - resource function post httpHeaderTest10(@http:Header string Hos) returns string { + resource function post treatNilAsOpt\-nonNil\-HeaderTest(@http:Header string Hos) returns string { return Hos; } - resource function post httpHeaderTest11(@http:Header NoHeaderVal noHeaderVal) returns int { + resource function post retrAsRecordNoField(@http:Header NoHeaderVal noHeaderVal) returns int { return noHeaderVal.Content\-Length + 100; } - resource function post httpHeaderTest12(@http:Header string? Hoste) returns string? { + resource function post treatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? Hoste) returns string? { return Hoste; } - resource function post httpHeaderTest13(@http:Header string? Hos) returns string? { + resource function post treatNilAsOpt\-Nil\-HeaderTest(@http:Header string? Hos) returns string? { return Hos; } +} +listener af:HttpListener ep = new (); +service /hello on ep { + resource function default all() returns @af:HttpOutput string { return "Hello from all"; } diff --git a/ballerina-tests/tests/resources/httpHeaderTest.json b/ballerina-tests/tests/resources/httpHeaderTest.json index abdfbab6..e00c995b 100644 --- a/ballerina-tests/tests/resources/httpHeaderTest.json +++ b/ballerina-tests/tests/resources/httpHeaderTest.json @@ -1,7 +1,7 @@ { "Data":{ "httpPayload":{ - "Url":"https://az-func-http-test.azurewebsites.net/api/hello/FUNC_NAME", + "Url":"https://az-func-http-test.azurewebsites.net/api/httpHeader/FUNC_NAME", "Method":"POST", "Query":{ @@ -59,10 +59,10 @@ "45.121.88.92:53880" ], "X-Original-URL":[ - "/api/hello/FUNC_NAME" + "/api/httpHeader/FUNC_NAME" ], "X-WAWS-Unencoded-URL":[ - "/api/hello/FUNC_NAME" + "/api/httpHeader/FUNC_NAME" ] }, "Params":{ @@ -83,7 +83,7 @@ "RoleClaimType":"http://schemas.microsoft.com/ws/2008/06/identity/claims/role" } ], - "Body":"hello" + "Body":"httpHeader" } }, "Metadata":{ @@ -107,11 +107,11 @@ "X-ARR-SSL":"2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion":"1.2", "X-Forwarded-For":"45.121.88.92:53880", - "X-Original-URL":"/api/hello/FUNC_NAME", - "X-WAWS-Unencoded-URL":"/api/hello/FUNC_NAME" + "X-Original-URL":"/api/httpHeader/FUNC_NAME", + "X-WAWS-Unencoded-URL":"/api/httpHeader/FUNC_NAME" }, "sys":{ - "MethodName":"post-hello-FUNC_NAME", + "MethodName":"post-httpHeader-FUNC_NAME", "UtcNow":"2022-09-01T10:12:54.2952774Z", "RandGuid":"f891f95b-cc04-4e8c-b440-24b8da192b28" } diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 53d50630..50f4982d 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -5,193 +5,193 @@ import ballerina/regex; import ballerina/test; @test:Config {} -function httpHeaderTest1() returns error? { +function retrFromAnnotField() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest1"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrFromAnnotField"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest1", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrFromAnnotField", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"text/plain"}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest2() returns error? { +function retrFromParam() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest2"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrFromParam"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest2", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrFromParam", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"az-func-http-test.azurewebsites.net"}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest3() returns error? { +function retrSingleVal() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest3"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrSingleVal"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest3", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrSingleVal", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":15}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest4() returns error? { +function retrArrVal() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest4"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrArrVal"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest4", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrArrVal", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":20}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest5() returns error? { +function retrArrValStr() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest5"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrArrValStr"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest5", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrArrValStr", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"12"}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest6() returns error? { +function retrAsRecord() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest6"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrAsRecord"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest6", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrAsRecord", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"application/json"}, "body":105}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest7() returns error? { +function retrNilable() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest7"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrNilable"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest7", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrNilable", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"201", "headers":{"Content-Type":"text/plain"}, "body":"az-func-http-test.azurewebsites.net"}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest8() returns error? { +function nnonTreatNilAsOpt\-Nil\-noHeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest8"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-Nil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest8", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-Nil-noHeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest9() returns error? { +function treatNilAsOpt\-nonNil\-noHeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest9"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-nonNil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest9", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-nonNil-noHeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest10() returns error? { +function treatNilAsOpt\-nonNil\-HeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest10"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-nonNil-HeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest10", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-nonNil-HeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest11() returns error? { +function retrAsRecordNoField() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest11"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "retrAsRecordNoField"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest11", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-retrAsRecordNoField", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Content-Type1'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest12() returns error? { +function treatNilAsOpt\-Nil\-noHeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest12"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-Nil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest12", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-Nil-noHeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"202"}},"Logs":[],"ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest13() returns error? { +function treatNilAsOpt\-Nil\-HeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest13"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-Nil-HeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest13", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-Nil-HeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"202"}},"Logs":[],"ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest14() returns error? { +function nonTreatNilAsOpt\-nonNil\-noHeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest14"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-nonNil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest14", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-nonNil-noHeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest15() returns error? { +function nonTreatNilAsOpt\-nonNil\-HeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest15"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-nonNil-HeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest15", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-nonNil-HeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @test:Config {} -function httpHeaderTest16() returns error? { +function nonTreatNilAsOpt\-Nil\-HeaderTest() returns error? { final http:Client clientEndpoint = check new ("http://localhost:3000"); string jsonFilePath = "./tests/resources/httpHeaderTest.json"; string readString = check io:fileReadString(jsonFilePath); - string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "httpHeaderTest16"); + string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-Nil-HeaderTest"); json readJson = check value:fromJsonString(replacedString); - json resp = check clientEndpoint->post("/post-hello-httpHeaderTest16", readJson); + json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-Nil-HeaderTest", readJson); json expectedResp = {"Outputs":{"resp":{"statusCode":"202"}},"Logs":[],"ReturnValue":null}; test:assertEquals(resp, expectedResp); } diff --git a/ballerina/annotation.bal b/ballerina/annotation.bal index 04662446..1bb72981 100644 --- a/ballerina/annotation.bal +++ b/ballerina/annotation.bal @@ -1,4 +1,4 @@ -// Copyright (c) 2020 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/ballerina/errors.bal b/ballerina/errors.bal index 73015e0d..b9779d38 100644 --- a/ballerina/errors.bal +++ b/ballerina/errors.bal @@ -1,3 +1,19 @@ +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. + public type Error distinct error; public type FunctionNotFoundError distinct Error; diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java index 19af87f1..1a764cdd 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except diff --git a/compiler-plugin-tests/src/test/resources/testng.xml b/compiler-plugin-tests/src/test/resources/testng.xml index 58f6b4d1..fe3d9371 100644 --- a/compiler-plugin-tests/src/test/resources/testng.xml +++ b/compiler-plugin-tests/src/test/resources/testng.xml @@ -1,6 +1,6 @@ + + + + + diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java index 2ddaba71..6b558096 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureCodeGeneratedTask.java @@ -23,7 +23,6 @@ import io.ballerina.projects.plugins.CompilerLifecycleTask; import org.ballerinax.azurefunctions.service.Binding; -import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Path; @@ -48,7 +47,7 @@ public void perform(CompilerLifecycleEventContext compilerLifecycleEventContext) new AzureFunctionServiceExtractor(compilerLifecycleEventContext.currentPackage()); List functionContexts = azureFunctionServiceExtractor.extractFunctions(); Map generatedFunctions = new HashMap<>(); - + for (FunctionContext ctx : functionContexts) { JsonObject functions = new JsonObject(); JsonArray bindings = new JsonArray(); @@ -72,20 +71,19 @@ public void perform(CompilerLifecycleEventContext compilerLifecycleEventContext) } OUT.println("\n\tExecute the below command to deploy the function locally:"); OUT.println( - "\tfunc start --script-root target/bin/azf-local --java"); + "\tfunc start --script-root " + Constants.ARTIFACT_PATH + " --java"); OUT.println("\n\tExecute the below command to deploy Ballerina Azure Functions:"); Path parent = path.getParent(); if (parent != null) { OUT.println( - "\taz functionapp deployment source config-zip -g -n " + - " --src " + parent.toString() + File.separator + - Constants.AZURE_FUNCS_OUTPUT_ZIP_FILENAME + "\n\n"); + "\tfunc azure functionapp publish --script-root " + + Constants.ARTIFACT_PATH + " \n\n"); } }); } private void generateFunctionsArtifact(Map functions, Path binaryPath) throws IOException { - new FunctionsArtifact(functions, binaryPath).generate(Constants.AZURE_FUNCS_OUTPUT_ZIP_FILENAME); + new FunctionsArtifact(functions, binaryPath).generate(); } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index 019efb99..f89e962e 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -58,5 +58,12 @@ public class Constants { public static final String DIRECTION_IN = "in"; public static final String DIRECTION_OUT = "out"; - public static final String AF_LOCAL_DIR = "azf-local"; + public static final String FUNCTION_DIRECTORY = "azure_functions"; + + public static final String ARTIFACT_PATH = "target/" + FUNCTION_DIRECTORY; + + public static final String SETTINGS_LOCAL_FILE_NAME = "local.settings.json"; + public static final String EXTENSIONS_FILE_NAME = "extensions.json"; + public static final String SETTINGS_FILE_NAME = "settings.json"; + public static final String TASKS_FILE_NAME = "tasks.json"; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index 7673691c..e1603156 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -20,10 +20,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; +import org.ballerinax.azurefunctions.tooling.Extensions; +import org.ballerinax.azurefunctions.tooling.LocalSettings; +import org.ballerinax.azurefunctions.tooling.Settings; +import org.ballerinax.azurefunctions.tooling.Tasks; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -33,14 +36,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.HashMap; +import java.nio.file.StandardOpenOption; import java.util.Map; /** @@ -49,9 +49,11 @@ public class FunctionsArtifact { private static final String HOST_JSON_NAME = "host.json"; - private static final String FUNCTION_JSON_NAME = "function.json"; + private static final String VSCODE_DIRECTORY = ".vscode"; + private static final String GITIGNORE = ".gitignore"; + private Map functions; private Path binaryPath; @@ -111,8 +113,8 @@ private void generateHostJson() throws IOException { extensionBundle.add("id", new JsonPrimitive("Microsoft.Azure.Functions.ExtensionBundle")); extensionBundle.add("version", new JsonPrimitive("[2.*, 3.0.0)")); } - - private InputStream jtos(JsonElement element) { + + private InputStream jtos(Object element) { try { return new ByteArrayInputStream(this.gson.toJson(element).getBytes(Constants.CHARSET)); } catch (UnsupportedEncodingException e) { @@ -120,12 +122,9 @@ private InputStream jtos(JsonElement element) { } } - public void generate(String outputFileName) throws IOException { + public void generate() throws IOException { // if an earlier generated file is there, delete it, or else // this will merge content to the earlier artifact - Files.deleteIfExists(Paths.get(outputFileName)); - Map env = new HashMap<>(); - env.put("create", "true"); if (this.binaryPath == null) { return; } @@ -133,20 +132,61 @@ public void generate(String outputFileName) throws IOException { if (parent == null) { return; } - URI uri = URI.create("jar:file:" + parent.resolve(outputFileName).toUri().getPath()); - try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) { - Files.copy(this.binaryPath, zipfs.getPath("/" + this.binaryPath.getFileName()), + Path targetDir = parent.getParent(); + if (targetDir == null) { + return; + } + + Path projectDir = targetDir.getParent(); + if (projectDir == null) { + return; + } + generateVsCodeConfigs(projectDir); + + Path functionsDir = targetDir.resolve(Constants.FUNCTION_DIRECTORY); + Files.createDirectory(functionsDir); + Files.copy(this.binaryPath, functionsDir.resolve(this.binaryPath.getFileName()), + StandardCopyOption.REPLACE_EXISTING); + Files.copy(this.jtos(this.hostJson), functionsDir.resolve(HOST_JSON_NAME), StandardCopyOption.REPLACE_EXISTING); - Files.copy(this.jtos(this.hostJson), zipfs.getPath("/" + HOST_JSON_NAME), + generateLocalSettings(functionsDir); + for (Map.Entry entry : this.functions.entrySet()) { + Path functionDir = functionsDir.resolve(entry.getKey()); + Files.createDirectory(functionDir); + Files.copy(this.jtos(entry.getValue()), functionDir.resolve(FUNCTION_JSON_NAME), StandardCopyOption.REPLACE_EXISTING); - for (Map.Entry entry : this.functions.entrySet()) { - Path functionDir = zipfs.getPath("/" + entry.getKey()); - Files.createDirectory(functionDir); - Files.copy(this.jtos(entry.getValue()), functionDir.resolve(FUNCTION_JSON_NAME), - StandardCopyOption.REPLACE_EXISTING); - } } - Util.unzipFolder(parent.resolve(outputFileName), parent.resolve(Constants.AF_LOCAL_DIR)); + } + + private void generateLocalSettings(Path azureFunctionsDir) throws IOException { + Files.copy(jtos(new LocalSettings()), azureFunctionsDir.resolve(Constants.SETTINGS_LOCAL_FILE_NAME), + StandardCopyOption.REPLACE_EXISTING); + } + + private void generateVsCodeConfigs(Path projectDir) throws IOException { + Path vsCodeDir = projectDir.resolve(VSCODE_DIRECTORY); + Files.createDirectories(vsCodeDir); + Files.copy(jtos(new Extensions()), vsCodeDir.resolve(Constants.EXTENSIONS_FILE_NAME), + StandardCopyOption.REPLACE_EXISTING); + Files.copy(jtos(new Settings()), vsCodeDir.resolve(Constants.SETTINGS_FILE_NAME), + StandardCopyOption.REPLACE_EXISTING); + Files.copy(jtos(new Tasks()), vsCodeDir.resolve(Constants.TASKS_FILE_NAME), + StandardCopyOption.REPLACE_EXISTING); + + addToGitIgnore(projectDir); } + private void addToGitIgnore(Path projectDir) throws IOException { + Path gitIgnore = projectDir.resolve(GITIGNORE); + if (!Files.exists(gitIgnore)) { + return; + } + String gitIgnoreContent = Files.readString(gitIgnore); + if (gitIgnoreContent.contains(VSCODE_DIRECTORY)) { + return; + } + + Files.write(gitIgnore, VSCODE_DIRECTORY.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND); + } } + diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Extensions.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Extensions.java new file mode 100644 index 00000000..4a1bd61e --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Extensions.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.tooling; + +/** + * Represents extensions.json in the .vscode directory. + * + * @since 2201.3.0 + */ +public class Extensions { + + private String[] recommendations; + + public Extensions() { + this.recommendations = new String[1]; + this.recommendations[0] = "ms-azuretools.vscode-azurefunctions"; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/LocalSettings.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/LocalSettings.java new file mode 100644 index 00000000..56a5ea74 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/LocalSettings.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.tooling; + +import com.google.gson.annotations.SerializedName; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents local.settings.json in the target directory. + * + * @since 2201.3.0 + */ +public class LocalSettings { + + @SerializedName("IsEncrypted") + private boolean isEncrypted; + + @SerializedName("Values") + private Map values; + + public LocalSettings() { + this.isEncrypted = false; + this.values = new HashMap<>(); + this.values.put("AzureWebJobsStorage", ""); + this.values.put("FUNCTIONS_WORKER_RUNTIME", "java"); + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java new file mode 100644 index 00000000..57365668 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.tooling; + +import com.google.gson.annotations.SerializedName; +import org.ballerinax.azurefunctions.Constants; + +/** + * Represents settings.json in the .vscode directory. + * + * @since 2201.3.0 + */ +public class Settings { + + @SerializedName("azureFunctions.deploySubpath") + private String deploySubpath; + + @SerializedName("azureFunctions.projectLanguage") + private String projectLanguage; + + @SerializedName("azureFunctions.projectRuntime") + private String projectRuntime; + + @SerializedName("debug.internalConsoleOptions") + private String internalConsoleOptions; + + public Settings() { + this.deploySubpath = Constants.ARTIFACT_PATH; + this.projectLanguage = "Custom"; + this.projectRuntime = "~3"; + this.internalConsoleOptions = "neverOpen"; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Tasks.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Tasks.java new file mode 100644 index 00000000..03c96a17 --- /dev/null +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Tasks.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.tooling; + +import org.ballerinax.azurefunctions.Constants; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents tasks.json in the .vscode directory. + * + * @since 2201.3.0 + */ +public class Tasks { + + private String version; + private Tasks.Task[] tasks; + + public Tasks() { + this.version = "2.0.0"; + this.tasks = new Tasks.Task[1]; + this.tasks[0] = new Tasks.Task(); + } + + static class Task { + + private String type; + private String command; + private String problemMatcher; + private boolean isBackground; + private Map options; + + public Task() { + this.type = "func"; + this.command = "host start"; + this.problemMatcher = "$func-watch"; + this.isBackground = true; + this.options = new HashMap<>(); + this.options.put("cwd", "${workspaceFolder}/" + Constants.ARTIFACT_PATH); + } + } +} From f51147f37e9123855346d93c05cf0293eb3df268 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 3 Oct 2022 11:44:53 +0530 Subject: [PATCH 34/59] Improve Code Coverage of service based implementation --- .gitignore | 2 + .../azurefunctions/test/DeploymentTest.java | 26 +-- ...lerTest.java => FunctionArtifactTest.java} | 6 +- .../test/resources/deployment/Ballerina.toml | 7 - .../test/resources/deployment/functions.bal | 195 ------------------ .../src/test/resources/testng.xml | 4 +- .../azurefunctions/FunctionsArtifact.java | 4 +- .../org/ballerinax/azurefunctions/Util.java | 51 ----- 8 files changed, 18 insertions(+), 277 deletions(-) rename compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/{HandlerTest.java => FunctionArtifactTest.java} (98%) delete mode 100644 compiler-plugin-tests/src/test/resources/deployment/Ballerina.toml delete mode 100644 compiler-plugin-tests/src/test/resources/deployment/functions.bal diff --git a/.gitignore b/.gitignore index e9a4896f..572028bf 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ target .ballerina .idea *.iml + +compiler-plugin-tests/src/test/resources/handlers/.vscode/ diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java index 4dda6f1d..752508a5 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/DeploymentTest.java @@ -22,13 +22,9 @@ import org.testng.Assert; import org.testng.annotations.Test; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; /** * Azure functions deployment test. @@ -39,26 +35,20 @@ public class DeploymentTest { @Test public void testAzureFunctionsDeploymentProject() throws Exception { - Path depedenciesToml = SOURCE_DIR.resolve("deployment").resolve("Dependencies.toml"); + Path handlers = SOURCE_DIR.resolve("handlers"); + Path depedenciesToml = handlers.resolve("Dependencies.toml"); Files.deleteIfExists(depedenciesToml); - ProcessOutput processOutput = TestUtils.compileBallerinaProject(SOURCE_DIR.resolve("deployment")); + ProcessOutput processOutput = TestUtils.compileBallerinaProject(handlers); Assert.assertEquals(processOutput.getExitCode(), 0); Assert.assertTrue(processOutput.getStdOutput().contains("@azure_functions")); // check if the executable jar and the host.json files are in the generated zip file - Path zipFilePath = SOURCE_DIR.resolve("deployment").resolve("target").resolve("bin").resolve("azure-functions" + - ".zip"); + Path zipFilePath = handlers.resolve("target").resolve("azure_functions"); Assert.assertTrue(Files.exists(zipFilePath)); - URI uri = URI.create("jar:file:" + zipFilePath.toUri().getPath()); - FileSystem zipfs = FileSystems.newFileSystem(uri, new HashMap<>()); - try { - Path jarFile = zipfs.getPath("/deployment.jar"); - Path hostJson = zipfs.getPath("/host.json"); - Assert.assertTrue(Files.exists(jarFile)); - Assert.assertTrue(Files.exists(hostJson)); - } finally { - zipfs.close(); - } + + Assert.assertTrue(Files.exists(zipFilePath.resolve("azure_functions_tests.jar"))); + Assert.assertTrue(Files.exists(zipFilePath.resolve("host.json"))); + Files.deleteIfExists(depedenciesToml); } } diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java similarity index 98% rename from compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java rename to compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java index 3c824cd1..99ec8ce2 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/HandlerTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java @@ -40,9 +40,11 @@ import java.util.Map; /** - * Test case for checking generated source of handler. + * Test cases to generated function.json in different cases. + * + * @since 2201.3.0 */ -public class HandlerTest { +public class FunctionArtifactTest { public static final Path RESOURCE_DIRECTORY = Paths.get("src/test/resources/handlers/"); diff --git a/compiler-plugin-tests/src/test/resources/deployment/Ballerina.toml b/compiler-plugin-tests/src/test/resources/deployment/Ballerina.toml deleted file mode 100644 index cd92fa2b..00000000 --- a/compiler-plugin-tests/src/test/resources/deployment/Ballerina.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -org = "anjana" -name = "deployment" -version = "0.1.0" - -[build-options] -observabilityIncluded = true diff --git a/compiler-plugin-tests/src/test/resources/deployment/functions.bal b/compiler-plugin-tests/src/test/resources/deployment/functions.bal deleted file mode 100644 index 383d15e6..00000000 --- a/compiler-plugin-tests/src/test/resources/deployment/functions.bal +++ /dev/null @@ -1,195 +0,0 @@ -import ballerina/uuid; -import ballerinax/azure_functions as af; - -// HTTP request/response with no authentication -@af:Function -public isolated function hello(@af:HTTPTrigger { authLevel: "anonymous" } string payload) - returns @af:HttpOutput string|error { - return "Hello, " + payload + "!"; -} - -// HTTP request to add data to a queue -@af:Function -public isolated function fromHttpToQueue(af:Context ctx, - @af:HTTPTrigger af:HTTPRequest req, - @af:QueueOutput { queueName: "queue1" } af:StringOutputBinding msg) - returns @af:HttpOutput af:HTTPBinding { - msg.value = req.body; - return { statusCode: 200, payload: "Request: " + req.toString() }; -} - -// A message put to a queue is copied to another queue -@af:Function -public isolated function fromQueueToQueue(af:Context ctx, - @af:QueueTrigger { queueName: "queue2" } string inMsg, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - ctx.log("In Message: " + inMsg); - ctx.log("Metadata: " + ctx.metadata.toString()); - outMsg.value = inMsg; -} - -// A blob added to a container is copied to a queue -@af:Function -public isolated function fromBlobToQueue(af:Context ctx, - @af:BlobTrigger { path: "bpath1/{name}" } byte[] blobIn, - @af:BindingName string name, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) - returns error? { - outMsg.value = "Name: " + name + " Content: " + blobIn.toString(); -} - -// HTTP request to read a blob value -@af:Function -public isolated function httpTriggerBlobInput(@af:HTTPTrigger af:HTTPRequest req, - @af:BlobInput { path: "bpath1/{Query.name}" } byte[]? blobIn) - returns @af:HttpOutput string { - int length = 0; - if blobIn is byte[] { - length = blobIn.length(); - } - return "Blob: " + req.query["name"].toString() + " Length: " + - length.toString() + " Content: " + blobIn.toString(); -} - -// HTTP request to read a blob value input string -@af:Function -public isolated function httpTriggerBlobInputStr(@af:HTTPTrigger af:HTTPRequest req, - @af:BlobInput { path: "bpath1/{Query.name}" } string? strIn) - returns @af:HttpOutput string { - string str = ""; - if strIn is string { - str = strIn; - } - return "Blob: " + req.query["name"].toString() + " Length: " + - strIn.toString() + " Content: " + str; -} - -// HTTP request to add a new blob -@af:Function -public isolated function httpTriggerBlobOutput(@af:HTTPTrigger af:HTTPRequest req, - @af:BlobOutput { path: "bpath1/{Query.name}" } af:StringOutputBinding bb) - returns @af:HttpOutput string|error { - bb.value = req.body; - return "Blob: " + req.query["name"].toString() + " Content: " + - bb?.value.toString(); -} - -// HTTP request to add a new blob -@af:Function -public isolated function httpTriggerBlobOutput2(@af:HTTPTrigger af:HTTPRequest req, - @af:BlobOutput { path: "bpath1/{Query.name}" } af:BytesOutputBinding bb) - returns @af:HttpOutput string|error { - bb.value = [65, 66, 67, 97, 98]; - return "Blob: " + req.query["name"].toString() + " Content: " + - bb?.value.toString(); -} - -// Sending an SMS -@af:Function -public isolated function sendSMS(@af:HTTPTrigger af:HTTPRequest req, - @af:TwilioSmsOutput { fromNumber: "+12069845840" } - af:TwilioSmsOutputBinding tb) - returns @af:HttpOutput string { - tb.to = req.query["to"].toString(); - tb.body = req.body.toString(); - return "Message - to: " + tb?.to.toString() + " body: " + tb?.body.toString(); -} - -public type Person record { - string id; - string name; - string country; -}; - -// CosmosDB record trigger -@af:Function -public isolated function cosmosDBToQueue1(@af:CosmosDBTrigger { - connectionStringSetting: "CosmosDBConnection", databaseName: "db1", - collectionName: "c1" } Person[] req, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - outMsg.value = req.toString(); -} - -@af:Function -public isolated function cosmosDBToQueue2(@af:CosmosDBTrigger { - connectionStringSetting: "CosmosDBConnection", databaseName: "db1", - collectionName: "c2" } json req, - @af:QueueOutput { queueName: "queue3" } af:StringOutputBinding outMsg) { - outMsg.value = req.toString(); -} - -// HTTP request to read CosmosDB records -@af:Function -public isolated function httpTriggerCosmosDBInput1( - @af:HTTPTrigger af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - id: "{Query.id}", partitionKey: "{Query.country}" } json dbReq) - returns @af:HttpOutput string|error { - return dbReq.toString(); -} - -@af:Function -public isolated function httpTriggerCosmosDBInput2( - @af:HTTPTrigger af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - id: "{Query.id}", partitionKey: "{Query.country}" } Person? dbReq) - returns @af:HttpOutput string|error { - return dbReq.toString(); -} - -@af:Function -public isolated function httpTriggerCosmosDBInput3( - @af:HTTPTrigger { route: "c1/{country}" } af:HTTPRequest httpReq, - @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1", - sqlQuery: "select * from c1 where c1.country = {country}" } - Person[] dbReq) - returns @af:HttpOutput string|error { - return dbReq.toString(); -} - -// HTTP request to write records to CosmosDB -@af:Function -public isolated function httpTriggerCosmosDBOutput1( - @af:HTTPTrigger af:HTTPRequest httpReq, @af:HttpOutput af:HTTPBinding hb) - returns @af:CosmosDBOutput { connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } json { - json entry = { id: uuid:createType1AsString(), name: "Saman", country: "Sri Lanka" }; - hb.payload = "Adding entry: " + entry.toString(); - return entry; -} - -@af:Function -public isolated function httpTriggerCosmosDBOutput2( - @af:HTTPTrigger af:HTTPRequest httpReq, - @af:HttpOutput af:HTTPBinding hb) - returns @af:CosmosDBOutput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } json { - json entry = [{ id: uuid:createType1AsString(), name: "John Doe A", country: "USA" }, - { id: uuid:createType1AsString(), name: "John Doe B", country: "USA" }]; - hb.payload = "Adding entries: " + entry.toString(); - return entry; -} - -@af:Function -public isolated function httpTriggerCosmosDBOutput3( - @af:HTTPTrigger af:HTTPRequest httpReq) - returns @af:CosmosDBOutput { - connectionStringSetting: "CosmosDBConnection", - databaseName: "db1", collectionName: "c1" } Person[] { - Person[] persons = []; - persons.push({id: uuid:createType1AsString(), name: "Jack", country: "UK"}); - persons.push({id: uuid:createType1AsString(), name: "Will", country: "UK"}); - return persons; -} - -// A timer function which is executed every 10 seconds. -@af:Function -public isolated function queuePopulationTimer( - @af:TimerTrigger { schedule: "*/10 * * * * *" } json triggerInfo, - @af:QueueOutput { queueName: "queue4" } af:StringOutputBinding msg) { - msg.value = triggerInfo.toString(); -} diff --git a/compiler-plugin-tests/src/test/resources/testng.xml b/compiler-plugin-tests/src/test/resources/testng.xml index fe3d9371..140520f9 100644 --- a/compiler-plugin-tests/src/test/resources/testng.xml +++ b/compiler-plugin-tests/src/test/resources/testng.xml @@ -20,8 +20,8 @@ - - + + diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index e1603156..284ba3a8 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -144,7 +144,7 @@ public void generate() throws IOException { generateVsCodeConfigs(projectDir); Path functionsDir = targetDir.resolve(Constants.FUNCTION_DIRECTORY); - Files.createDirectory(functionsDir); + Files.createDirectories(functionsDir); Files.copy(this.binaryPath, functionsDir.resolve(this.binaryPath.getFileName()), StandardCopyOption.REPLACE_EXISTING); Files.copy(this.jtos(this.hostJson), functionsDir.resolve(HOST_JSON_NAME), @@ -152,7 +152,7 @@ public void generate() throws IOException { generateLocalSettings(functionsDir); for (Map.Entry entry : this.functions.entrySet()) { Path functionDir = functionsDir.resolve(entry.getKey()); - Files.createDirectory(functionDir); + Files.createDirectories(functionDir); Files.copy(this.jtos(entry.getValue()), functionDir.resolve(FUNCTION_JSON_NAME), StandardCopyOption.REPLACE_EXISTING); } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index f7a09348..5d8e11ce 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -39,16 +39,8 @@ import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextRange; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.List; import java.util.Optional; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; /** * Contains the utilities required for the compiler extension. @@ -108,49 +100,6 @@ public static String resourcePathToString(NodeList nodes) { return finalPath; } - public static void unzipFolder(Path source, Path target) throws IOException { - - try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile()))) { - ZipEntry zipEntry = zis.getNextEntry(); - - while (zipEntry != null) { - boolean isDirectory = false; - if (zipEntry.getName().endsWith(File.separator)) { - isDirectory = true; - } - - Path newPath = zipSlipProtect(zipEntry, target); - - if (isDirectory) { - Files.createDirectories(newPath); - } else { - Path newPathParent = newPath.getParent(); - if (newPathParent != null) { - if (Files.notExists(newPathParent)) { - Files.createDirectories(newPathParent); - } - } - Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING); - } - zipEntry = zis.getNextEntry(); - } - zis.closeEntry(); - } - } - - // protect zip slip attack - public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir) - throws IOException { - Path targetDirResolved = targetDir.resolve(zipEntry.getName()); - - Path normalizePath = targetDirResolved.normalize(); - if (!normalizePath.startsWith(targetDir)) { - throw new IOException("Bad zip entry: " + zipEntry.getName()); - } - - return normalizePath; - } - public static void updateDiagnostic(SyntaxNodeAnalysisContext ctx, Location location, AzureDiagnosticCodes httpDiagnosticCodes) { DiagnosticInfo diagnosticInfo = getDiagnosticInfo(httpDiagnosticCodes); From 17261bce746624ad65120c129eaa24236767a1ef Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 4 Oct 2022 12:35:20 +0530 Subject: [PATCH 35/59] Fix optional payload annotation bug in compiler extensions --- .../src/test/resources/handlers/main.bal | 8 +++--- .../service/RemoteTriggerBinding.java | 2 +- .../service/TriggerBinding.java | 25 +++++++++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index 4cfac87e..896bd071 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -89,7 +89,7 @@ service /hello on ep { listener af:QueueListener queueListener = new af:QueueListener(); service "queue" on queueListener { - remote function onMessage (@http:Payload string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onMessage (string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo "+ inMsg; } } @@ -98,7 +98,7 @@ service "queue" on queueListener { listener af:CosmosDBListener cosmosEp = new (); service "cosmos" on cosmosEp { - remote function onUpdated (@http:Payload DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onUpdated (DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { string id = inMsg[0].id; return "helloo "+ id; } @@ -107,7 +107,7 @@ service "cosmos" on cosmosEp { @af:TimerTrigger { schedule: "*/10 * * * * *" } listener af:TimerListener timerListener = new af:TimerListener(); service "timer" on timerListener { - remote function onTrigger (@http:Payload af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + remote function onTrigger (af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { return "helloo "+ inMsg.IsPastDue.toString(); } } @@ -118,7 +118,7 @@ service "timer" on timerListener { listener af:BlobListener blobListener = new af:BlobListener(); service "blob" on blobListener { - remote function onUpdated (@http:Payload byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { + remote function onUpdated (byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { path: "bpath1/newBlob" } byte[]|error { return blobIn; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java index c734e386..fcad222c 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java @@ -69,7 +69,7 @@ public List getBindings() { continue; } String variableName = reqParam.paramName().get().text(); - if (isPayloadAnnotationExist(reqParam.annotations())) { + if (!isAzureFunctionsAnnotationExist(reqParam.annotations())) { this.setVarName(variableName); continue; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java index 4559decb..99da8b7e 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java @@ -1,6 +1,7 @@ package org.ballerinax.azurefunctions.service; import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ModuleSymbol; import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.SymbolKind; import io.ballerina.compiler.api.symbols.VariableSymbol; @@ -10,9 +11,7 @@ import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; -import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; -import io.ballerina.compiler.syntax.tree.SyntaxKind; import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.FunctionContext; import org.ballerinax.azurefunctions.Util; @@ -84,14 +83,24 @@ public Optional getListenerAnnotation(ServiceDeclarationNode svc return Optional.empty(); } - protected boolean isPayloadAnnotationExist(NodeList nodes) { + protected boolean isAzureFunctionsAnnotationExist(NodeList nodes) { for (AnnotationNode annotation : nodes) { Node annotRef = annotation.annotReference(); - if (annotRef.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { - QualifiedNameReferenceNode annotationRef = (QualifiedNameReferenceNode) annotRef; - if (annotationRef.identifier().text().equals("Payload")) { //Add other stuff - return true; - } + Optional annotationSymbol = semanticModel.symbol(annotRef); + if (annotationSymbol.isEmpty()) { + continue; + } + Optional module = annotationSymbol.get().getModule(); + if (module.isEmpty()) { + continue; + } + Optional name = module.get().getName(); + if (name.isEmpty()) { + continue; + } + + if (name.get().equals(Constants.AZURE_FUNCTIONS_MODULE_NAME)) { + return true; } } return false; From 3eb2c4198d1e59f9b831d77b1d69d707fed9d091 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Tue, 4 Oct 2022 15:40:01 +0530 Subject: [PATCH 36/59] Add inline listener support --- ballerina/annotation.bal | 2 +- .../test/FunctionArtifactTest.java | 59 ++++++++++++++++++- .../src/test/resources/handlers/main.bal | 47 +++++++++++++++ .../service/TriggerBinding.java | 29 +++++---- 4 files changed, 123 insertions(+), 14 deletions(-) diff --git a/ballerina/annotation.bal b/ballerina/annotation.bal index 1bb72981..385f2b9e 100644 --- a/ballerina/annotation.bal +++ b/ballerina/annotation.bal @@ -23,7 +23,7 @@ public type AZFunctionConfiguration record {| |}; public type AUTH_LEVEL "anonymous"|"function"|"admin"; -public const annotation HTTPTriggerConfiguration HttpTrigger on source listener; +public const annotation HTTPTriggerConfiguration HttpTrigger on source listener, service; # HTTPTrigger annotation configuration. # diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java index 99ec8ce2..11935380 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/FunctionArtifactTest.java @@ -75,7 +75,7 @@ public void compileSample() { DiagnosticResult diagnosticResult = compilation.diagnosticResult(); Assert.assertFalse(diagnosticResult.hasErrors()); - Assert.assertEquals(generatedFunctions.size(), 19); + Assert.assertEquals(generatedFunctions.size(), 24); } @Test @@ -89,6 +89,17 @@ public void testOptionalHttp() { Assert.assertEquals(httpHello, parse); } + @Test + public void testHttpTriggerInlineListener() { + JsonObject httpHello = generatedFunctions.get("post-helo-hello-query"); + String str = + "{\"bindings\":[{\"type\":\"httpTrigger\",\"authLevel\":\"anonymous\",\"methods\":[\"post\"]," + + "\"direction\":\"in\",\"name\":\"httpPayload\",\"route\":\"helo/hello-query\"}," + + "{\"type\":\"http\",\"direction\":\"out\",\"name\":\"resp\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(httpHello, parse); + } + @Test public void testHttpHello() { JsonObject httpHello = generatedFunctions.get("post-hello"); @@ -155,6 +166,18 @@ public void testQueueTrigger() { Assert.assertEquals(actual, parse); } + @Test + public void testQueueTriggerInlineListener() { + JsonObject actual = generatedFunctions.get("queue1"); + String str = + "{\"bindings\":[{\"type\":\"queueTrigger\",\"connection\":\"AzureWebJobsStorage\"," + + "\"queueName\":\"queue21\",\"direction\":\"in\",\"name\":\"inMsg\"},{\"type\":\"queue\"," + + "\"connection\":\"AzureWebJobsStorage\",\"queueName\":\"queue3\",\"direction\":\"out\"," + + "\"name\":\"outMsg\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + @Test public void testCosmosTrigger() { JsonObject actual = generatedFunctions.get("cosmos"); @@ -168,6 +191,19 @@ public void testCosmosTrigger() { Assert.assertEquals(actual, parse); } + @Test + public void testCosmosTriggerInlineListener() { + JsonObject actual = generatedFunctions.get("cosmos1"); + String str = + "{\"bindings\":[{\"type\":\"cosmosDBTrigger\",\"connectionStringSetting\":\"CosmosDBConnection\"," + + "\"databaseName\":\"db1\",\"collectionName\":\"c2\",\"name\":\"inMsg\",\"direction\":\"in\"," + + "\"createLeaseCollectionIfNotExists\":true,\"leasesCollectionThroughput\":400}," + + "{\"type\":\"queue\",\"connection\":\"AzureWebJobsStorage\",\"queueName\":\"queue3\"," + + "\"direction\":\"out\",\"name\":\"outMsg\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + @Test public void testTimerTrigger() { JsonObject actual = generatedFunctions.get("timer"); @@ -178,6 +214,16 @@ public void testTimerTrigger() { Assert.assertEquals(actual, parse); } + @Test + public void testTimerTriggerInlineListener() { + JsonObject actual = generatedFunctions.get("timer1"); + String str = "{\"bindings\":[{\"type\":\"timerTrigger\",\"schedule\":\"*/10 * * * * *\"," + + "\"runOnStartup\":true,\"direction\":\"in\",\"name\":\"inMsg\"},{\"type\":\"queue\",\"connection\":" + + "\"AzureWebJobsStorage\",\"queueName\":\"queue3\",\"direction\":\"out\",\"name\":\"outMsg\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + @Test public void testBlobTrigger() { JsonObject actual = generatedFunctions.get("blob"); @@ -189,6 +235,17 @@ public void testBlobTrigger() { Assert.assertEquals(actual, parse); } + @Test + public void testBlobTriggerInlineListener() { + JsonObject actual = generatedFunctions.get("blob1"); + String str = "{\"bindings\":[{\"type\":\"blobTrigger\",\"name\":\"blobIn\",\"direction\":\"in\"," + + "\"path\":\"bpath1/{name}\",\"connection\":\"AzureWebJobsStorage\"},{\"type\":\"blob\"," + + "\"direction\":\"out\",\"name\":\"outMsg\",\"path\":\"bpath1/newBlob\"," + + "\"connection\":\"AzureWebJobsStorage\",\"dataType\":\"string\"}]}"; + JsonElement parse = jsonParser.parse(str); + Assert.assertEquals(actual, parse); + } + @Test public void testEscapeSequence() { JsonObject httpHello = generatedFunctions.get("post-hello--hello-query"); diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index 896bd071..f168e0af 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -21,6 +21,13 @@ service /hello\- on ep1 { } } +service /helo on new af:HttpListener() { + + resource function post hello\-query() returns string|error { + return "Hello from the hello-query"; + } +} + // @af:HTTPTest service /hello on ep { resource function default all() returns @af:HttpOutput string { @@ -94,6 +101,17 @@ service "queue" on queueListener { } } + +@af:QueueTrigger { + queueName: "queue21" +} +service "queue1" on new af:QueueListener() { + remote function onMessage (string inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg; + } +} + + @af:CosmosDBTrigger {connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2"} listener af:CosmosDBListener cosmosEp = new (); @@ -104,6 +122,16 @@ service "cosmos" on cosmosEp { } } + +@af:CosmosDBTrigger {connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c2"} +service "cosmos1" on new af:CosmosDBListener() { + remote function onUpdated (DBEntry[] inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + string id = inMsg[0].id; + return "helloo "+ id; + } +} + + @af:TimerTrigger { schedule: "*/10 * * * * *" } listener af:TimerListener timerListener = new af:TimerListener(); service "timer" on timerListener { @@ -112,6 +140,14 @@ service "timer" on timerListener { } } + +@af:TimerTrigger { schedule: "*/10 * * * * *" } +service "timer1" on new af:TimerListener() { + remote function onTrigger (af:TimerMetadata inMsg) returns @af:QueueOutput {queueName: "queue3"} string|error { + return "helloo "+ inMsg.IsPastDue.toString(); + } +} + @af:BlobTrigger { path: "bpath1/{name}" } @@ -123,3 +159,14 @@ service "blob" on blobListener { return blobIn; } } + +@af:BlobTrigger { + path: "bpath1/{name}" +} + +service "blob1" on new af:BlobListener() { + remote function onUpdated (byte[] blobIn, @af:BindingName { } string name) returns @af:BlobOutput { + path: "bpath1/newBlob" } byte[]|error { + return blobIn; + } +} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java index 99da8b7e..cb79cc8f 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Optional; +import static io.ballerina.compiler.syntax.tree.SyntaxKind.EXPLICIT_NEW_EXPRESSION; /** * Represents an Trigger Binding in Azure Functions. * @@ -37,18 +38,23 @@ public TriggerBinding(String triggerType) { public Optional getListenerAnnotation(ServiceDeclarationNode svcDeclNode, String annotationName) { //TODO handle inline decl for (ExpressionNode expression : svcDeclNode.expressions()) { - Optional symbol = this.semanticModel.symbol(expression); - if (symbol.isEmpty()) { - continue; - } - Symbol listenerSymbol = symbol.get(); - if (listenerSymbol.kind() != SymbolKind.VARIABLE) { - continue; + Optional metadata; + if (expression.kind() == EXPLICIT_NEW_EXPRESSION) { + metadata = svcDeclNode.metadata(); + } else { + Optional symbol = this.semanticModel.symbol(expression); + if (symbol.isEmpty()) { + continue; + } + Symbol listenerSymbol = symbol.get(); + if (listenerSymbol.kind() != SymbolKind.VARIABLE) { + continue; + } + VariableSymbol variableSymbol = (VariableSymbol) listenerSymbol; + ListenerDeclarationNode listenerDeclarationNode = + (ListenerDeclarationNode) Util.findNode(svcDeclNode, variableSymbol); + metadata = listenerDeclarationNode.metadata(); } - VariableSymbol variableSymbol = (VariableSymbol) listenerSymbol; - ListenerDeclarationNode listenerDeclarationNode = - (ListenerDeclarationNode) Util.findNode(svcDeclNode, variableSymbol); - Optional metadata = listenerDeclarationNode.metadata(); if (metadata.isEmpty()) { continue; } @@ -79,7 +85,6 @@ public Optional getListenerAnnotation(ServiceDeclarationNode svc // } // } } - return Optional.empty(); } From 02a740e2b08ea24eda58c0e0b7a8081a749a2271 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Thu, 6 Oct 2022 15:12:31 +0530 Subject: [PATCH 37/59] Add code coverage for ballerina tests --- ballerina-tests/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-tests/build.gradle b/ballerina-tests/build.gradle index d8045708..26c95cbd 100644 --- a/ballerina-tests/build.gradle +++ b/ballerina-tests/build.gradle @@ -29,7 +29,7 @@ def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/res def ballerinaTomlFile = new File("$project.projectDir/Ballerina.toml") def ballerinaDist = "${project.rootDir}/target/ballerina-runtime" def distributionBinPath = "${ballerinaDist}/bin" -def testCoverageParam = "--code-coverage --includes=*" +def testCoverageParam = "--test-report --code-coverage --coverage-format=xml --includes=io.ballerina.stdlib.azure.functions.*:ballerinax.azure_functions" def stripBallerinaExtensionVersion(String extVersion) { if (extVersion.matches(project.ext.timestampedVersionRegex)) { From d76d16be87f8e577a62a98c5897f0148d8219418 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Wed, 5 Oct 2022 18:38:19 +0530 Subject: [PATCH 38/59] Change AzureFunction annotation to Function --- ballerina/annotation.bal | 6 ++--- .../azurefunctions/AzureFunctionModifier.java | 2 +- .../ballerinax/azurefunctions/Constants.java | 24 +++---------------- .../service/http/HTTPTriggerBinding.java | 3 +-- .../stdlib/azure/functions/Constants.java | 5 +++- .../functions/NativeHttpToAzureAdaptor.java | 8 +++---- .../stdlib/azure/functions/ParamHandler.java | 4 +--- 7 files changed, 16 insertions(+), 36 deletions(-) diff --git a/ballerina/annotation.bal b/ballerina/annotation.bal index 385f2b9e..a4b4ee5b 100644 --- a/ballerina/annotation.bal +++ b/ballerina/annotation.bal @@ -14,11 +14,9 @@ // specific language governing permissions and limitations // under the License. -# @azurefunctions:Function annotation. -public const annotation Function on function; //Todo remove -public const annotation AZFunctionConfiguration AzureFunction on function; +public const annotation FunctionConfiguration Function on function; -public type AZFunctionConfiguration record {| +public type FunctionConfiguration record {| string name; |}; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index 84465618..da192c02 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -111,7 +111,7 @@ public AnnotationNode getFunctionNameAnnotation(String functionName) { QualifiedNameReferenceNode azureFunctionAnnotRef = NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(modulePrefix), NodeFactory.createToken(SyntaxKind.COLON_TOKEN), - NodeFactory.createIdentifierToken("AzureFunction")); + NodeFactory.createIdentifierToken(Constants.FUNCTION_ANNOTATION)); LiteralValueToken literalValueToken = NodeFactory.createLiteralValueToken(SyntaxKind.STRING_LITERAL_TOKEN, "\"" + functionName + "\"", NodeFactory.createEmptyMinutiaeList(), AbstractNodeFactory diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index f89e962e..34561302 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -22,32 +22,12 @@ */ public class Constants { - public static final String MAIN_FUNC_NAME = "main"; public static final String AZURE_FUNCTIONS_MODULE_NAME = "azure_functions"; - public static final String BALLERINA_ORG = "ballerina"; - public static final String REQUEST_PARAMS_TYPE = "HandlerParams"; - public static final String REQUEST_PARAMS_NAME = "params"; - public static final String HTTP_CALLER_PARAMS_NAME = "caller"; - public static final String HTTP_REQUEST_PARAMS_NAME = "request"; - public static final String REMOTE_KEYWORD = "remote"; public static final String HTTP = "http"; public static final String AZURE_FUNCTIONS = "azure_functions"; public static final String HEADER_ANNOTATION_TYPE = "HttpHeader"; - public static final String AZURE_FUNCTIONS_PACKAGE_ORG = "ballerinax"; - public static final String AZURE_FUNCTIONS_CONTEXT_NAME = "Context"; - public static final String AZURE_FUNCS_OUTPUT_ZIP_FILENAME = "azure-functions.zip"; - public static final String FUNCTION_BINDINGS_NAME = "bindings"; + public static final String AZURE_FUNCTIONS_PACKAGE_ORG = "ballerinax"; public static final String CHARSET = "UTF-8"; - public static final String DEFAULT_STORAGE_CONNECTION_NAME = "AzureWebJobsStorage"; - public static final String DEFAULT_TWILIO_ACCOUNT_SID_SETTING = "AzureWebJobsTwilioAccountSid"; - public static final String DEFAULT_TWILIO_AUTH_TOKEN_SETTING = "AzureWebJobsTwilioAuthToken"; - public static final boolean DEFAULT_TIMER_TRIGGER_RUNONSTARTUP = true; - public static final boolean DEFAULT_COSMOS_DB_CREATELEASECOLLECTIONIFNOTEXISTS = true; - public static final String PARAMS = "params"; - public static final String AF_IMPORT_ALIAS = "af"; - public static final String HTTP_IMPORT = "http"; - public static final String AWS_FUNCTION_TYPE = "Function"; - public static final String AZ_FUNCTION_PREFIX = "az-func"; public static final String ANNOTATION_HTTP_TRIGGER = "HttpTrigger"; public static final String ANNOTATION_QUEUE_TRIGGER = "QueueTrigger"; @@ -66,4 +46,6 @@ public class Constants { public static final String EXTENSIONS_FILE_NAME = "extensions.json"; public static final String SETTINGS_FILE_NAME = "settings.json"; public static final String TASKS_FILE_NAME = "tasks.json"; + + public static final String FUNCTION_ANNOTATION = "Function"; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index f89c4f8f..a75692da 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -158,8 +158,7 @@ public Optional getFunctionNameFromAnnotation(FunctionDefinitionNode fun continue; } QualifiedNameReferenceNode qualifiedRef = (QualifiedNameReferenceNode) ref; - if (!qualifiedRef.identifier().text().equals("AzureFunction") || - !qualifiedRef.modulePrefix().text().equals("af")) { + if (!qualifiedRef.identifier().text().equals(Constants.FUNCTION_ANNOTATION)) { continue; } Optional val = diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index d96f6359..a2d2aff3 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -22,10 +22,14 @@ * {@code Constants} contains the public constants to be used. */ public interface Constants { + String SLASH = "/"; String PACKAGE_ORG = "ballerinax"; String PACKAGE_NAME = "azure_functions"; + + String PACKAGE_COMPLETE = PACKAGE_ORG + "/" + PACKAGE_NAME + ":3"; + String FUNCTION_ANNOTATION_COMPLETE = PACKAGE_COMPLETE + ":Function"; String HTTP_PACKAGE_ORG = "ballerina"; String HTTP_PACKAGE_NAME = "http"; @@ -71,5 +75,4 @@ public interface Constants { String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; String HEADER_NOT_FOUND_ERROR = "HeaderNotFoundError"; String HTTP_PACKAGE_VERSION = "2"; - } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index 9a8491f5..2bf2f2f2 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -54,7 +54,7 @@ public static BArray getAzureFunctionNames(Environment env, BObject adaptor) { List functionNameList = new ArrayList<>(); for (ResourceMethodType resourceMethod : svcType.getResourceMethods()) { BString functionName = ((BMap) resourceMethod - .getAnnotation(StringUtils.fromString("ballerinax/azure_functions:3:AzureFunction"))) + .getAnnotation(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_COMPLETE))) .getStringValue(StringUtils.fromString("name")); functionNameList.add(functionName); } @@ -108,9 +108,9 @@ private static Object invokeResourceFunction(Environment env, BObject bHubServic private static Optional getResourceMethodType(ResourceMethodType[] types, BString enteredFunctionName) { for (ResourceMethodType type : types) { - BString functionName = ((BMap) type - .getAnnotation(StringUtils.fromString("ballerinax/azure_functions:3:AzureFunction"))) - .getStringValue(StringUtils.fromString("name")); + BString functionName = + ((BMap) type.getAnnotation(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_COMPLETE))) + .getStringValue(StringUtils.fromString("name")); if (functionName.toString().equals(enteredFunctionName.toString())) { return Optional.of(type); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 0886e34d..4ddb0ed5 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -73,8 +73,6 @@ public static boolean isPayloadAnnotationParam(Object annotation) { } } return false; -// Object value = ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:Payload")); -// return value instanceof Boolean; } public static boolean isHeaderAnnotationParam(Object annotation) { @@ -146,7 +144,7 @@ public static boolean isBindingNameParam(Object annotation) { } Object value = - ((BMap) annotation).get(StringUtils.fromString("ballerinax/azure_functions:3:BindingName")); + ((BMap) annotation).get(StringUtils.fromString(Constants.PACKAGE_COMPLETE + ":BindingName")); return value != null; } From 71d19e30972a4cbd93ca0b72425ae1c1d0cf9683 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 09:14:55 +0530 Subject: [PATCH 39/59] Fix artifact generation when service path is empty --- .../azurefunctions/AzureFunctionNameGenerator.java | 6 +++++- .../azurefunctions/service/http/HTTPTriggerBinding.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index 5bde8d30..a9f1917d 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -49,7 +49,11 @@ private String getFunctionName(String servicePath, FunctionDefinitionNode functi resourcePath.append("/").append(token.text()); } } - return method + "-" + resourcePath.toString().replace("/", "-"); + String functionName = resourcePath.toString().replace("/", "-"); + if (servicePath.equals("")) { + return method + functionName; + } + return method + "-" + functionName; } public String getUniqueFunctionName(String servicePath, FunctionDefinitionNode functionDefinitionNode) { diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index a75692da..3304605d 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -89,7 +89,12 @@ public List getBindings() { } //TODO add wildcard } - httpTriggerBinding.setPath(resourcePath.toString()); + String resPath = resourcePath.toString(); + if (resPath.startsWith("/")) { + httpTriggerBinding.setPath(resPath.substring(1)); + } else { + httpTriggerBinding.setPath(resPath); + } bindings.add(httpTriggerBinding); String variableName; SeparatedNodeList parameters = functionDefinitionNode.functionSignature().parameters(); From e3125ef07fc962f9329cd30f10e915c36c043aa9 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 11:32:09 +0530 Subject: [PATCH 40/59] Remove http prefix and migrate to runtime v4 --- .../azurefunctions/FunctionsArtifact.java | 19 ++++++++++++------- .../azurefunctions/tooling/Settings.java | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index 284ba3a8..b271966b 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -95,6 +95,11 @@ private void generateHostJson() throws IOException { this.hostJson = new JsonObject(); } this.hostJson.add("version", new JsonPrimitive("2.0")); + JsonObject extensions = new JsonObject(); + JsonObject http = new JsonObject(); + http.addProperty("routePrefix", ""); + extensions.add("http", http); + this.hostJson.add("extensions", extensions); JsonObject httpWorker = new JsonObject(); this.hostJson.add("customHandler", httpWorker); JsonObject httpWorkerDesc = new JsonObject(); @@ -111,9 +116,9 @@ private void generateHostJson() throws IOException { JsonObject extensionBundle = new JsonObject(); this.hostJson.add("extensionBundle", extensionBundle); extensionBundle.add("id", new JsonPrimitive("Microsoft.Azure.Functions.ExtensionBundle")); - extensionBundle.add("version", new JsonPrimitive("[2.*, 3.0.0)")); + extensionBundle.add("version", new JsonPrimitive("[3.*, 4.0.0)")); } - + private InputStream jtos(Object element) { try { return new ByteArrayInputStream(this.gson.toJson(element).getBytes(Constants.CHARSET)); @@ -142,13 +147,13 @@ public void generate() throws IOException { return; } generateVsCodeConfigs(projectDir); - + Path functionsDir = targetDir.resolve(Constants.FUNCTION_DIRECTORY); Files.createDirectories(functionsDir); Files.copy(this.binaryPath, functionsDir.resolve(this.binaryPath.getFileName()), StandardCopyOption.REPLACE_EXISTING); Files.copy(this.jtos(this.hostJson), functionsDir.resolve(HOST_JSON_NAME), - StandardCopyOption.REPLACE_EXISTING); + StandardCopyOption.REPLACE_EXISTING); generateLocalSettings(functionsDir); for (Map.Entry entry : this.functions.entrySet()) { Path functionDir = functionsDir.resolve(entry.getKey()); @@ -157,12 +162,12 @@ public void generate() throws IOException { StandardCopyOption.REPLACE_EXISTING); } } - + private void generateLocalSettings(Path azureFunctionsDir) throws IOException { Files.copy(jtos(new LocalSettings()), azureFunctionsDir.resolve(Constants.SETTINGS_LOCAL_FILE_NAME), StandardCopyOption.REPLACE_EXISTING); } - + private void generateVsCodeConfigs(Path projectDir) throws IOException { Path vsCodeDir = projectDir.resolve(VSCODE_DIRECTORY); Files.createDirectories(vsCodeDir); @@ -172,7 +177,7 @@ private void generateVsCodeConfigs(Path projectDir) throws IOException { StandardCopyOption.REPLACE_EXISTING); Files.copy(jtos(new Tasks()), vsCodeDir.resolve(Constants.TASKS_FILE_NAME), StandardCopyOption.REPLACE_EXISTING); - + addToGitIgnore(projectDir); } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java index 57365668..a62a92a3 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/tooling/Settings.java @@ -42,7 +42,7 @@ public class Settings { public Settings() { this.deploySubpath = Constants.ARTIFACT_PATH; this.projectLanguage = "Custom"; - this.projectRuntime = "~3"; + this.projectRuntime = "~4"; this.internalConsoleOptions = "neverOpen"; } } From 7812ace8978a1815dd295eb7d13ae2fe869ac14c Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 17:04:15 +0530 Subject: [PATCH 41/59] Remove artifacts before generating while persisting existing local.settings.json --- .../azurefunctions/FunctionsArtifact.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index b271966b..5ef70766 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -41,7 +41,9 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; +import java.util.Comparator; import java.util.Map; +import java.util.Optional; /** * Represents the output artifact (.zip) generated for Azure Functions. @@ -149,12 +151,22 @@ public void generate() throws IOException { generateVsCodeConfigs(projectDir); Path functionsDir = targetDir.resolve(Constants.FUNCTION_DIRECTORY); + Optional cachedLocalSettings = cacheLocalSettings(functionsDir); + deleteDirectory(functionsDir); Files.createDirectories(functionsDir); Files.copy(this.binaryPath, functionsDir.resolve(this.binaryPath.getFileName()), StandardCopyOption.REPLACE_EXISTING); Files.copy(this.jtos(this.hostJson), functionsDir.resolve(HOST_JSON_NAME), StandardCopyOption.REPLACE_EXISTING); - generateLocalSettings(functionsDir); + if (cachedLocalSettings.isEmpty()) { + generateLocalSettings(functionsDir); + } else { + String localSettings = cachedLocalSettings.get(); + ByteArrayInputStream inStream = + new ByteArrayInputStream(localSettings.getBytes(StandardCharsets.UTF_8)); + Files.copy(inStream, functionsDir.resolve(Constants.SETTINGS_LOCAL_FILE_NAME), + StandardCopyOption.REPLACE_EXISTING); + } for (Map.Entry entry : this.functions.entrySet()) { Path functionDir = functionsDir.resolve(entry.getKey()); Files.createDirectories(functionDir); @@ -163,11 +175,26 @@ public void generate() throws IOException { } } + private void deleteDirectory(Path azureFunctionsDir) throws IOException { + Files.walk(azureFunctionsDir) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + private void generateLocalSettings(Path azureFunctionsDir) throws IOException { Files.copy(jtos(new LocalSettings()), azureFunctionsDir.resolve(Constants.SETTINGS_LOCAL_FILE_NAME), StandardCopyOption.REPLACE_EXISTING); } + private Optional cacheLocalSettings(Path azureFunctionsDir) throws IOException { + Path localSettingsPath = azureFunctionsDir.resolve(Constants.SETTINGS_LOCAL_FILE_NAME); + if (localSettingsPath.toFile().exists()) { + return Optional.of(Files.readString(localSettingsPath)); + } + return Optional.empty(); + } + private void generateVsCodeConfigs(Path projectDir) throws IOException { Path vsCodeDir = projectDir.resolve(VSCODE_DIRECTORY); Files.createDirectories(vsCodeDir); From eefec7a90e9dc0396c624606a9c4a6a9790be7e5 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 17:09:13 +0530 Subject: [PATCH 42/59] Remove api/ prefix from tests --- ballerina-tests/tests/resources/base-dot.json | 10 +++++----- ballerina-tests/tests/resources/cosmos-db-arr.json | 10 +++++----- ballerina-tests/tests/resources/cosmos-db.json | 10 +++++----- ballerina-tests/tests/resources/default.json | 10 +++++----- .../tests/resources/error-invalid-payload.json | 10 +++++----- .../tests/resources/error-missing-payload.json | 2 +- ballerina-tests/tests/resources/escape-seq.json | 10 +++++----- .../tests/resources/http-optional-out.json | 10 +++++----- .../tests/resources/http-optional-with-payload.json | 10 +++++----- .../resources/http-optional-without-payload.json | 10 +++++----- .../tests/resources/http-query-blob-input.json | 10 +++++----- .../resources/http-query-blob-optional-input.json | 10 +++++----- .../tests/resources/httpAccessorTest.json | 10 +++++----- ballerina-tests/tests/resources/httpHeaderTest.json | 12 ++++++------ ballerina-tests/tests/resources/httpResTest1.json | 10 +++++----- ballerina-tests/tests/resources/httpResTest2.json | 10 +++++----- ballerina-tests/tests/resources/httpResTest3.json | 10 +++++----- ballerina-tests/tests/resources/httpResTest4.json | 10 +++++----- ballerina-tests/tests/resources/local-res-path.json | 2 +- .../tests/resources/multi-path-param.json | 10 +++++----- ballerina-tests/tests/resources/multi-res-query.json | 10 +++++----- ballerina-tests/tests/resources/multi-res.json | 10 +++++----- ballerina-tests/tests/resources/nonHttpResTest.json | 10 +++++----- ballerina-tests/tests/resources/nonReturnTest1.json | 10 +++++----- .../tests/resources/payload-json-json.json | 10 +++++----- .../tests/resources/payload-json-record.json | 10 +++++----- .../tests/resources/payload-octa-byte.json | 10 +++++----- .../tests/resources/payload-text-byte.json | 10 +++++----- .../tests/resources/payload-text-string.json | 10 +++++----- ballerina-tests/tests/resources/payload-xml-xml.json | 10 +++++----- .../tests/resources/query-arr-no-payload.json | 10 +++++----- .../tests/resources/query-arr-optional-with.json | 2 +- .../tests/resources/query-arr-optional-without.json | 2 +- ballerina-tests/tests/resources/query-arr.json | 2 +- ballerina-tests/tests/resources/query-bool.json | 2 +- ballerina-tests/tests/resources/query-float.json | 2 +- .../tests/resources/query-no-payload.json | 10 +++++----- .../tests/resources/query-optional-with.json | 10 +++++----- .../tests/resources/query-optional-without.json | 10 +++++----- ballerina-tests/tests/resources/query-param.json | 10 +++++----- .../tests/resources/res-path-conflict-param.json | 10 +++++----- ballerina-tests/tests/resources/res-path-param.json | 10 +++++----- ballerina-tests/tests/resources/res-path.json | 10 +++++----- ballerina-tests/tests/resources/simple.json | 10 +++++----- ballerina-tests/tests/resources/temp.json | 10 +++++----- 45 files changed, 198 insertions(+), 198 deletions(-) diff --git a/ballerina-tests/tests/resources/base-dot.json b/ballerina-tests/tests/resources/base-dot.json index 14adac11..2d77addf 100644 --- a/ballerina-tests/tests/resources/base-dot.json +++ b/ballerina-tests/tests/resources/base-dot.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello", + "Url": "https://bal-dev.azurewebsites.net/hello", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello" + "/hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello" + "/hello" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello", - "X-WAWS-Unencoded-URL": "/api/hello" + "X-Original-URL": "/hello", + "X-WAWS-Unencoded-URL": "/hello" }, "sys": { "MethodName": "post-hello", diff --git a/ballerina-tests/tests/resources/cosmos-db-arr.json b/ballerina-tests/tests/resources/cosmos-db-arr.json index bb9ca613..815c452c 100644 --- a/ballerina-tests/tests/resources/cosmos-db-arr.json +++ b/ballerina-tests/tests/resources/cosmos-db-arr.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/db", + "Url": "https://bal-dev.azurewebsites.net/hello/db", "Method": "POST", "Query": {}, "Headers": { @@ -51,10 +51,10 @@ "112.134.130.212:17220" ], "X-Original-URL": [ - "/api/hello/db" + "/hello/db" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/db" + "/hello/db" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.130.212:17220", - "X-Original-URL": "/api/hello/db", - "X-WAWS-Unencoded-URL": "/api/hello/db", + "X-Original-URL": "/hello/db", + "X-WAWS-Unencoded-URL": "/hello/db", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/cosmos-db.json b/ballerina-tests/tests/resources/cosmos-db.json index d770df42..3920acd2 100644 --- a/ballerina-tests/tests/resources/cosmos-db.json +++ b/ballerina-tests/tests/resources/cosmos-db.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://sl-update-1-rc.azurewebsites.net/api/hello/hi?id=11", + "Url": "https://sl-update-1-rc.azurewebsites.net/hello/hi?id=11", "Method": "POST", "Query": { "id": "11" @@ -53,10 +53,10 @@ "112.134.131.153:31932" ], "X-Original-URL": [ - "/api/hello/hi?id=11" + "/hello/hi?id=11" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/hi?id=11" + "/hello/hi?id=11" ], "DISGUISED-HOST": [ "sl-update-1-rc.azurewebsites.net" @@ -104,8 +104,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.131.153:31932", - "X-Original-URL": "/api/hello/hi?id=11", - "X-WAWS-Unencoded-URL": "/api/hello/hi?id=11", + "X-Original-URL": "/hello/hi?id=11", + "X-WAWS-Unencoded-URL": "/hello/hi?id=11", "DISGUISED-HOST": "sl-update-1-rc.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/default.json b/ballerina-tests/tests/resources/default.json index db53e9f3..fb801b6a 100644 --- a/ballerina-tests/tests/resources/default.json +++ b/ballerina-tests/tests/resources/default.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/all", + "Url": "https://bal-dev.azurewebsites.net/hello/all", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/all" + "/hello/all" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/all" + "/hello/all" ] }, "Params": {}, @@ -95,8 +95,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/all", - "X-WAWS-Unencoded-URL": "/api/hello/all" + "X-Original-URL": "/hello/all", + "X-WAWS-Unencoded-URL": "/hello/all" }, "sys": { "MethodName": "default-hello-all", diff --git a/ballerina-tests/tests/resources/error-invalid-payload.json b/ballerina-tests/tests/resources/error-invalid-payload.json index 704a6e10..9fdc9626 100644 --- a/ballerina-tests/tests/resources/error-invalid-payload.json +++ b/ballerina-tests/tests/resources/error-invalid-payload.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/octaToByte", + "Url": "https://bal-dev.azurewebsites.net/hello/payload/octaToByte", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.128.183:8622" ], "X-Original-URL": [ - "/api/hello/payload/octaToByte" + "/hello/payload/octaToByte" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/payload/octaToByte" + "/hello/payload/octaToByte" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -103,8 +103,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.183:8622", - "X-Original-URL": "/api/hello/payload/octaToByte", - "X-WAWS-Unencoded-URL": "/api/hello/payload/octaToByte", + "X-Original-URL": "/hello/payload/octaToByte", + "X-WAWS-Unencoded-URL": "/hello/payload/octaToByte", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/error-missing-payload.json b/ballerina-tests/tests/resources/error-missing-payload.json index a957e533..f252a245 100644 --- a/ballerina-tests/tests/resources/error-missing-payload.json +++ b/ballerina-tests/tests/resources/error-missing-payload.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/err/empty/payload", + "Url": "http://localhost:7071/hello/err/empty/payload", "Method": "GET", "Query": {}, "Headers": { diff --git a/ballerina-tests/tests/resources/escape-seq.json b/ballerina-tests/tests/resources/escape-seq.json index 62b8c896..66ea5990 100644 --- a/ballerina-tests/tests/resources/escape-seq.json +++ b/ballerina-tests/tests/resources/escape-seq.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello-/hello-query", + "Url": "https://bal-dev.azurewebsites.net/hello-/hello-query", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello-/hello-query" + "/hello-/hello-query" ], "X-WAWS-Unencoded-URL": [ - "/api/hello-/hello-query" + "/hello-/hello-query" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello-/hello-query", - "X-WAWS-Unencoded-URL": "/api/hello-/hello-query" + "X-Original-URL": "/hello-/hello-query", + "X-WAWS-Unencoded-URL": "/hello-/hello-query" }, "sys": { "MethodName": "post-hello--hello-query", diff --git a/ballerina-tests/tests/resources/http-optional-out.json b/ballerina-tests/tests/resources/http-optional-out.json index da81f2c2..c16bd550 100644 --- a/ballerina-tests/tests/resources/http-optional-out.json +++ b/ballerina-tests/tests/resources/http-optional-out.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/optional/out", + "Url": "https://bal-dev.azurewebsites.net/hello/optional/out", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/optional/out" + "/hello/optional/out" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/optional/out" + "/hello/optional/out" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/optional/out", - "X-WAWS-Unencoded-URL": "/api/hello/optional/out" + "X-Original-URL": "/hello/optional/out", + "X-WAWS-Unencoded-URL": "/hello/optional/out" }, "sys": { "MethodName": "post-hello-optional-out", diff --git a/ballerina-tests/tests/resources/http-optional-with-payload.json b/ballerina-tests/tests/resources/http-optional-with-payload.json index d57cb4b3..e6051881 100644 --- a/ballerina-tests/tests/resources/http-optional-with-payload.json +++ b/ballerina-tests/tests/resources/http-optional-with-payload.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/optional/payload", + "Url": "https://bal-dev.azurewebsites.net/hello/optional/payload", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/optional/payload" + "/hello/optional/payload" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/optional/payload" + "/hello/optional/payload" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/optional/payload", - "X-WAWS-Unencoded-URL": "/api/hello/optional/payload" + "X-Original-URL": "/hello/optional/payload", + "X-WAWS-Unencoded-URL": "/hello/optional/payload" }, "sys": { "MethodName": "post-hello-optional-payload", diff --git a/ballerina-tests/tests/resources/http-optional-without-payload.json b/ballerina-tests/tests/resources/http-optional-without-payload.json index 40147d14..dc75d58f 100644 --- a/ballerina-tests/tests/resources/http-optional-without-payload.json +++ b/ballerina-tests/tests/resources/http-optional-without-payload.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/optional/payload", + "Url": "https://bal-dev.azurewebsites.net/hello/optional/payload", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/optional/payload" + "/hello/optional/payload" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/optional/payload" + "/hello/optional/payload" ] }, "Params": {}, @@ -95,8 +95,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/optional/payload", - "X-WAWS-Unencoded-URL": "/api/hello/optional/payload" + "X-Original-URL": "/hello/optional/payload", + "X-WAWS-Unencoded-URL": "/hello/optional/payload" }, "sys": { "MethodName": "post-hello-optional-payload", diff --git a/ballerina-tests/tests/resources/http-query-blob-input.json b/ballerina-tests/tests/resources/http-query-blob-input.json index 5bab970e..a2726a73 100644 --- a/ballerina-tests/tests/resources/http-query-blob-input.json +++ b/ballerina-tests/tests/resources/http-query-blob-input.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/blobInput?name=hello.txt", + "Url": "https://bal-dev.azurewebsites.net/hello/blobInput?name=hello.txt", "Method": "GET", "Query": { "name": "hello.txt" @@ -59,10 +59,10 @@ "112.134.128.76:48146" ], "X-Original-URL": [ - "/api/hello/blobInput?name=hello.txt" + "/hello/blobInput?name=hello.txt" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/blobInput?name=hello.txt" + "/hello/blobInput?name=hello.txt" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -109,8 +109,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.76:48146", - "X-Original-URL": "/api/hello/blobInput?name=hello.txt", - "X-WAWS-Unencoded-URL": "/api/hello/blobInput?name=hello.txt", + "X-Original-URL": "/hello/blobInput?name=hello.txt", + "X-WAWS-Unencoded-URL": "/hello/blobInput?name=hello.txt", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/http-query-blob-optional-input.json b/ballerina-tests/tests/resources/http-query-blob-optional-input.json index db64c352..9497c807 100644 --- a/ballerina-tests/tests/resources/http-query-blob-optional-input.json +++ b/ballerina-tests/tests/resources/http-query-blob-optional-input.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/blobInput?name=hello1.txt", + "Url": "https://bal-dev.azurewebsites.net/hello/blobInput?name=hello1.txt", "Method": "GET", "Query": { "name": "hello1.txt" @@ -59,10 +59,10 @@ "112.134.128.76:48828" ], "X-Original-URL": [ - "/api/hello/blobInput?name=hello1.txt" + "/hello/blobInput?name=hello1.txt" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/blobInput?name=hello1.txt" + "/hello/blobInput?name=hello1.txt" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -109,8 +109,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.76:48828", - "X-Original-URL": "/api/hello/blobInput?name=hello1.txt", - "X-WAWS-Unencoded-URL": "/api/hello/blobInput?name=hello1.txt", + "X-Original-URL": "/hello/blobInput?name=hello1.txt", + "X-WAWS-Unencoded-URL": "/hello/blobInput?name=hello1.txt", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/httpAccessorTest.json b/ballerina-tests/tests/resources/httpAccessorTest.json index 490023fd..a0ab5a6c 100644 --- a/ballerina-tests/tests/resources/httpAccessorTest.json +++ b/ballerina-tests/tests/resources/httpAccessorTest.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/httpAccessorTest", + "Url": "https://bal-dev.azurewebsites.net/hello/httpAccessorTest", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/httpAccessorTest" + "/hello/httpAccessorTest" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/httpAccessorTest" + "/hello/httpAccessorTest" ] }, "Params": {}, @@ -95,8 +95,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/httpAccessorTest", - "X-WAWS-Unencoded-URL": "/api/hello/httpAccessorTest" + "X-Original-URL": "/hello/httpAccessorTest", + "X-WAWS-Unencoded-URL": "/hello/httpAccessorTest" }, "sys": { "MethodName": "ACCESSOR_NAME-hello-httpAccessorTest", diff --git a/ballerina-tests/tests/resources/httpHeaderTest.json b/ballerina-tests/tests/resources/httpHeaderTest.json index 0bfa1766..2e6ae99b 100644 --- a/ballerina-tests/tests/resources/httpHeaderTest.json +++ b/ballerina-tests/tests/resources/httpHeaderTest.json @@ -1,7 +1,7 @@ { "Data":{ "httpPayload":{ - "Url":"https://az-func-http-test.azurewebsites.net/api/httpHeader/FUNC_NAME", + "Url":"https://az-func-http-test.azurewebsites.net/httpHeader/FUNC_NAME", "Method":"POST", "Query":{ @@ -59,10 +59,10 @@ "45.121.88.92:53880" ], "X-Original-URL":[ - "/api/httpHeader/FUNC_NAME" + "/httpHeader/FUNC_NAME" ], "X-WAWS-Unencoded-URL":[ - "/api/httpHeader/FUNC_NAME" + "/httpHeader/FUNC_NAME" ] }, "Params":{ @@ -108,8 +108,8 @@ "X-ARR-SSL":"2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion":"1.2", "X-Forwarded-For":"45.121.88.92:53880", - "X-Original-URL":"/api/httpHeader/FUNC_NAME", - "X-WAWS-Unencoded-URL":"/api/httpHeader/FUNC_NAME" + "X-Original-URL":"/httpHeader/FUNC_NAME", + "X-WAWS-Unencoded-URL":"/httpHeader/FUNC_NAME" }, "sys":{ "MethodName":"post-httpHeader-FUNC_NAME", @@ -117,4 +117,4 @@ "RandGuid":"f891f95b-cc04-4e8c-b440-24b8da192b28" } } - } \ No newline at end of file + } diff --git a/ballerina-tests/tests/resources/httpResTest1.json b/ballerina-tests/tests/resources/httpResTest1.json index 5134bc83..40f86b23 100644 --- a/ballerina-tests/tests/resources/httpResTest1.json +++ b/ballerina-tests/tests/resources/httpResTest1.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest1", + "Url": "https://bal-dev.azurewebsites.net/hello/httpResTest1", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/httpResTest1" + "/hello/httpResTest1" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/httpResTest1" + "/hello/httpResTest1" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/httpResTest1", - "X-WAWS-Unencoded-URL": "/api/hello/httpResTest1" + "X-Original-URL": "/hello/httpResTest1", + "X-WAWS-Unencoded-URL": "/hello/httpResTest1" }, "sys": { "MethodName": "post-hello-httpResTest1", diff --git a/ballerina-tests/tests/resources/httpResTest2.json b/ballerina-tests/tests/resources/httpResTest2.json index a39a835e..28c4373f 100644 --- a/ballerina-tests/tests/resources/httpResTest2.json +++ b/ballerina-tests/tests/resources/httpResTest2.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest2", + "Url": "https://bal-dev.azurewebsites.net/hello/httpResTest2", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/httpResTest2" + "/hello/httpResTest2" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/httpResTest2" + "/hello/httpResTest2" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/httpResTest2", - "X-WAWS-Unencoded-URL": "/api/hello/httpResTest2" + "X-Original-URL": "/hello/httpResTest2", + "X-WAWS-Unencoded-URL": "/hello/httpResTest2" }, "sys": { "MethodName": "post-hello-httpResTest2", diff --git a/ballerina-tests/tests/resources/httpResTest3.json b/ballerina-tests/tests/resources/httpResTest3.json index 5f998c76..31e7c14d 100644 --- a/ballerina-tests/tests/resources/httpResTest3.json +++ b/ballerina-tests/tests/resources/httpResTest3.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest3", + "Url": "https://bal-dev.azurewebsites.net/hello/httpResTest3", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/httpResTest3" + "/hello/httpResTest3" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/httpResTest3" + "/hello/httpResTest3" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/httpResTest3", - "X-WAWS-Unencoded-URL": "/api/hello/httpResTest3" + "X-Original-URL": "/hello/httpResTest3", + "X-WAWS-Unencoded-URL": "/hello/httpResTest3" }, "sys": { "MethodName": "post-hello-httpResTest3", diff --git a/ballerina-tests/tests/resources/httpResTest4.json b/ballerina-tests/tests/resources/httpResTest4.json index 5c59be97..d85447a3 100644 --- a/ballerina-tests/tests/resources/httpResTest4.json +++ b/ballerina-tests/tests/resources/httpResTest4.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/httpResTest4", + "Url": "https://bal-dev.azurewebsites.net/hello/httpResTest4", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/httpResTest4" + "/hello/httpResTest4" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/httpResTest4" + "/hello/httpResTest4" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/httpResTest4", - "X-WAWS-Unencoded-URL": "/api/hello/httpResTest4" + "X-Original-URL": "/hello/httpResTest4", + "X-WAWS-Unencoded-URL": "/hello/httpResTest4" }, "sys": { "MethodName": "post-hello-httpResTest4", diff --git a/ballerina-tests/tests/resources/local-res-path.json b/ballerina-tests/tests/resources/local-res-path.json index 9ed41218..918cc420 100644 --- a/ballerina-tests/tests/resources/local-res-path.json +++ b/ballerina-tests/tests/resources/local-res-path.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/foo", + "Url": "http://localhost:7071/hello/foo", "Method": "GET", "Query": {}, "Headers": { diff --git a/ballerina-tests/tests/resources/multi-path-param.json b/ballerina-tests/tests/resources/multi-path-param.json index d3967bc0..c64893f4 100644 --- a/ballerina-tests/tests/resources/multi-path-param.json +++ b/ballerina-tests/tests/resources/multi-path-param.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-svc-impl.azurewebsites.net/api/hello/foo/one/bar/two", + "Url": "https://bal-svc-impl.azurewebsites.net/hello/foo/one/bar/two", "Method": "POST", "Query": { }, @@ -52,10 +52,10 @@ "112.134.134.89:51660" ], "X-Original-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "DISGUISED-HOST": [ "bal-svc-impl.azurewebsites.net" @@ -99,8 +99,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.134.89:51660", - "X-Original-URL": "/api/hello/foo/bar?test=hello", - "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "X-Original-URL": "/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/hello/foo/bar?test=hello", "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/multi-res-query.json b/ballerina-tests/tests/resources/multi-res-query.json index 365d6cfa..4079638e 100644 --- a/ballerina-tests/tests/resources/multi-res-query.json +++ b/ballerina-tests/tests/resources/multi-res-query.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-svc-impl.azurewebsites.net/api/hello/foo?name=hello", + "Url": "https://bal-svc-impl.azurewebsites.net/hello/foo?name=hello", "Method": "POST", "Query": { "name": "hello" @@ -53,10 +53,10 @@ "112.134.134.89:51660" ], "X-Original-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "DISGUISED-HOST": [ "bal-svc-impl.azurewebsites.net" @@ -101,8 +101,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.134.89:51660", - "X-Original-URL": "/api/hello/foo/bar?test=hello", - "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "X-Original-URL": "/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/hello/foo/bar?test=hello", "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/multi-res.json b/ballerina-tests/tests/resources/multi-res.json index 4baa207f..ff5b64c6 100644 --- a/ballerina-tests/tests/resources/multi-res.json +++ b/ballerina-tests/tests/resources/multi-res.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-svc-impl.azurewebsites.net/api/hello/foo/bar?test=hello", + "Url": "https://bal-svc-impl.azurewebsites.net/hello/foo/bar?test=hello", "Method": "POST", "Query": { }, @@ -52,10 +52,10 @@ "112.134.134.89:51660" ], "X-Original-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "DISGUISED-HOST": [ "bal-svc-impl.azurewebsites.net" @@ -99,8 +99,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.134.89:51660", - "X-Original-URL": "/api/hello/foo/bar?test=hello", - "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "X-Original-URL": "/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/hello/foo/bar?test=hello", "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/nonHttpResTest.json b/ballerina-tests/tests/resources/nonHttpResTest.json index ce90028b..965b8e65 100644 --- a/ballerina-tests/tests/resources/nonHttpResTest.json +++ b/ballerina-tests/tests/resources/nonHttpResTest.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/FUNC_NAME", + "Url": "https://bal-dev.azurewebsites.net/hello/FUNC_NAME", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/FUNC_NAME" + "/hello/FUNC_NAME" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/FUNC_NAME" + "/hello/FUNC_NAME" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/FUNC_NAME", - "X-WAWS-Unencoded-URL": "/api/hello/FUNC_NAME" + "X-Original-URL": "/hello/FUNC_NAME", + "X-WAWS-Unencoded-URL": "/hello/FUNC_NAME" }, "sys": { "MethodName": "post-hello-FUNC_NAME", diff --git a/ballerina-tests/tests/resources/nonReturnTest1.json b/ballerina-tests/tests/resources/nonReturnTest1.json index ceb6786b..317a2f81 100644 --- a/ballerina-tests/tests/resources/nonReturnTest1.json +++ b/ballerina-tests/tests/resources/nonReturnTest1.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/nonReturnTest1", + "Url": "https://bal-dev.azurewebsites.net/hello/nonReturnTest1", "Method": "POST", "Query": {}, "Headers": { @@ -54,10 +54,10 @@ "112.134.128.105:41856" ], "X-Original-URL": [ - "/api/hello/nonReturnTest1" + "/hello/nonReturnTest1" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/nonReturnTest1" + "/hello/nonReturnTest1" ] }, "Params": {}, @@ -96,8 +96,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41856", - "X-Original-URL": "/api/hello/nonReturnTest1", - "X-WAWS-Unencoded-URL": "/api/hello/nonReturnTest1" + "X-Original-URL": "/hello/nonReturnTest1", + "X-WAWS-Unencoded-URL": "/hello/nonReturnTest1" }, "sys": { "MethodName": "post-hello-nonReturnTest1", diff --git a/ballerina-tests/tests/resources/payload-json-json.json b/ballerina-tests/tests/resources/payload-json-json.json index ffcd3908..6f51b603 100644 --- a/ballerina-tests/tests/resources/payload-json-json.json +++ b/ballerina-tests/tests/resources/payload-json-json.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello", + "Url": "https://bal-dev.azurewebsites.net/hello", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.131.214:19414" ], "X-Original-URL": [ - "/api/hello" + "/hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello" + "/hello" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -105,8 +105,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.131.214:19414", - "X-Original-URL": "/api/hello", - "X-WAWS-Unencoded-URL": "/api/hello", + "X-Original-URL": "/hello", + "X-WAWS-Unencoded-URL": "/hello", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/payload-json-record.json b/ballerina-tests/tests/resources/payload-json-record.json index d325737e..c7bc678d 100644 --- a/ballerina-tests/tests/resources/payload-json-record.json +++ b/ballerina-tests/tests/resources/payload-json-record.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello", + "Url": "https://bal-dev.azurewebsites.net/hello", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.131.214:19414" ], "X-Original-URL": [ - "/api/hello" + "/hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello" + "/hello" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -105,8 +105,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.131.214:19414", - "X-Original-URL": "/api/hello", - "X-WAWS-Unencoded-URL": "/api/hello", + "X-Original-URL": "/hello", + "X-WAWS-Unencoded-URL": "/hello", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/payload-octa-byte.json b/ballerina-tests/tests/resources/payload-octa-byte.json index d7af5b94..d4de06dc 100644 --- a/ballerina-tests/tests/resources/payload-octa-byte.json +++ b/ballerina-tests/tests/resources/payload-octa-byte.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/octaToByte", + "Url": "https://bal-dev.azurewebsites.net/hello/payload/octaToByte", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.128.183:8622" ], "X-Original-URL": [ - "/api/hello/payload/octaToByte" + "/hello/payload/octaToByte" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/payload/octaToByte" + "/hello/payload/octaToByte" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -103,8 +103,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.183:8622", - "X-Original-URL": "/api/hello/payload/octaToByte", - "X-WAWS-Unencoded-URL": "/api/hello/payload/octaToByte", + "X-Original-URL": "/hello/payload/octaToByte", + "X-WAWS-Unencoded-URL": "/hello/payload/octaToByte", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/payload-text-byte.json b/ballerina-tests/tests/resources/payload-text-byte.json index 84821773..0353da2a 100644 --- a/ballerina-tests/tests/resources/payload-text-byte.json +++ b/ballerina-tests/tests/resources/payload-text-byte.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/textToByte", + "Url": "https://bal-dev.azurewebsites.net/hello/payload/textToByte", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.128.183:7912" ], "X-Original-URL": [ - "/api/hello/payload/textToByte" + "/hello/payload/textToByte" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/payload/textToByte" + "/hello/payload/textToByte" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -103,8 +103,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.183:7912", - "X-Original-URL": "/api/hello/payload/textToByte", - "X-WAWS-Unencoded-URL": "/api/hello/payload/textToByte", + "X-Original-URL": "/hello/payload/textToByte", + "X-WAWS-Unencoded-URL": "/hello/payload/textToByte", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/payload-text-string.json b/ballerina-tests/tests/resources/payload-text-string.json index 760304e2..c807f601 100644 --- a/ballerina-tests/tests/resources/payload-text-string.json +++ b/ballerina-tests/tests/resources/payload-text-string.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/textToString", + "Url": "https://bal-dev.azurewebsites.net/hello/payload/textToString", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.128.183:7868" ], "X-Original-URL": [ - "/api/hello/payload/textToString" + "/hello/payload/textToString" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/payload/textToString" + "/hello/payload/textToString" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -103,8 +103,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.183:7868", - "X-Original-URL": "/api/hello/payload/textToString", - "X-WAWS-Unencoded-URL": "/api/hello/payload/textToString", + "X-Original-URL": "/hello/payload/textToString", + "X-WAWS-Unencoded-URL": "/hello/payload/textToString", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/payload-xml-xml.json b/ballerina-tests/tests/resources/payload-xml-xml.json index dfd73511..deb7c8d1 100644 --- a/ballerina-tests/tests/resources/payload-xml-xml.json +++ b/ballerina-tests/tests/resources/payload-xml-xml.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/payload/xmlToXml", + "Url": "https://bal-dev.azurewebsites.net/hello/payload/xmlToXml", "Method": "POST", "Query": {}, "Headers": { @@ -57,10 +57,10 @@ "112.134.131.47:2686" ], "X-Original-URL": [ - "/api/hello/payload/xmlToXml" + "/hello/payload/xmlToXml" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/payload/xmlToXml" + "/hello/payload/xmlToXml" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -103,8 +103,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.131.47:2686", - "X-Original-URL": "/api/hello/payload/xmlToXml", - "X-WAWS-Unencoded-URL": "/api/hello/payload/xmlToXml", + "X-Original-URL": "/hello/payload/xmlToXml", + "X-WAWS-Unencoded-URL": "/hello/payload/xmlToXml", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/query-arr-no-payload.json b/ballerina-tests/tests/resources/query-arr-no-payload.json index 0f64e492..48a73ea6 100644 --- a/ballerina-tests/tests/resources/query-arr-no-payload.json +++ b/ballerina-tests/tests/resources/query-arr-no-payload.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-svc-impl.azurewebsites.net/api/hello", + "Url": "https://bal-svc-impl.azurewebsites.net/hello", "Method": "GET", "Query": { "name" : ["Jack 1", "Jack 2"] @@ -53,10 +53,10 @@ "112.134.134.89:51660" ], "X-Original-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "DISGUISED-HOST": [ "bal-svc-impl.azurewebsites.net" @@ -100,8 +100,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.134.89:51660", - "X-Original-URL": "/api/hello/foo/bar?test=hello", - "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "X-Original-URL": "/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/hello/foo/bar?test=hello", "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/query-arr-optional-with.json b/ballerina-tests/tests/resources/query-arr-optional-with.json index 02c91495..b714ac93 100644 --- a/ballerina-tests/tests/resources/query-arr-optional-with.json +++ b/ballerina-tests/tests/resources/query-arr-optional-with.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/query/arrOrNil?name=red&name=green", + "Url": "http://localhost:7071/hello/query/arrOrNil?name=red&name=green", "Method": "GET", "Query": { "name": "red,green" diff --git a/ballerina-tests/tests/resources/query-arr-optional-without.json b/ballerina-tests/tests/resources/query-arr-optional-without.json index 00d50c13..ce25880e 100644 --- a/ballerina-tests/tests/resources/query-arr-optional-without.json +++ b/ballerina-tests/tests/resources/query-arr-optional-without.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/query/arrOrNil", + "Url": "http://localhost:7071/hello/query/arrOrNil", "Method": "GET", "Query": {}, "Headers": { diff --git a/ballerina-tests/tests/resources/query-arr.json b/ballerina-tests/tests/resources/query-arr.json index 3cb30930..cd2ad2f8 100644 --- a/ballerina-tests/tests/resources/query-arr.json +++ b/ballerina-tests/tests/resources/query-arr.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/query/arr?name=red&name=green", + "Url": "http://localhost:7071/hello/query/arr?name=red&name=green", "Method": "GET", "Query": { "name": "red,green" diff --git a/ballerina-tests/tests/resources/query-bool.json b/ballerina-tests/tests/resources/query-bool.json index 5b5f51e5..b06feaec 100644 --- a/ballerina-tests/tests/resources/query-bool.json +++ b/ballerina-tests/tests/resources/query-bool.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/query/bool?name=false", + "Url": "http://localhost:7071/hello/query/bool?name=false", "Method": "GET", "Query": { "name": "false" diff --git a/ballerina-tests/tests/resources/query-float.json b/ballerina-tests/tests/resources/query-float.json index e7e55d8b..1f48a9c5 100644 --- a/ballerina-tests/tests/resources/query-float.json +++ b/ballerina-tests/tests/resources/query-float.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "http://localhost:7071/api/hello/floatt/bool?name=10.5", + "Url": "http://localhost:7071/hello/floatt/bool?name=10.5", "Method": "GET", "Query": { "name": "10.5" diff --git a/ballerina-tests/tests/resources/query-no-payload.json b/ballerina-tests/tests/resources/query-no-payload.json index 647d96c0..49f2f518 100644 --- a/ballerina-tests/tests/resources/query-no-payload.json +++ b/ballerina-tests/tests/resources/query-no-payload.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-svc-impl.azurewebsites.net/api/hello", + "Url": "https://bal-svc-impl.azurewebsites.net/hello", "Method": "GET", "Query": { "name" : "Jack" @@ -53,10 +53,10 @@ "112.134.134.89:51660" ], "X-Original-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/bar?test=hello" + "/hello/foo/bar?test=hello" ], "DISGUISED-HOST": [ "bal-svc-impl.azurewebsites.net" @@ -100,8 +100,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.134.89:51660", - "X-Original-URL": "/api/hello/foo/bar?test=hello", - "X-WAWS-Unencoded-URL": "/api/hello/foo/bar?test=hello", + "X-Original-URL": "/hello/foo/bar?test=hello", + "X-WAWS-Unencoded-URL": "/hello/foo/bar?test=hello", "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/query-optional-with.json b/ballerina-tests/tests/resources/query-optional-with.json index 08b912d4..607d036b 100644 --- a/ballerina-tests/tests/resources/query-optional-with.json +++ b/ballerina-tests/tests/resources/query-optional-with.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/query/optional?name=test1", + "Url": "https://bal-dev.azurewebsites.net/hello/query/optional?name=test1", "Method": "GET", "Query": { "name": "test1" @@ -53,10 +53,10 @@ "112.134.130.212:18792" ], "X-Original-URL": [ - "/api/hello/query/optional?name=test1" + "/hello/query/optional?name=test1" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/query/optional?name=test1" + "/hello/query/optional?name=test1" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -100,8 +100,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.130.212:18792", - "X-Original-URL": "/api/hello/query/optional?name=test1", - "X-WAWS-Unencoded-URL": "/api/hello/query/optional?name=test1", + "X-Original-URL": "/hello/query/optional?name=test1", + "X-WAWS-Unencoded-URL": "/hello/query/optional?name=test1", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/query-optional-without.json b/ballerina-tests/tests/resources/query-optional-without.json index d57a18a8..a04a256b 100644 --- a/ballerina-tests/tests/resources/query-optional-without.json +++ b/ballerina-tests/tests/resources/query-optional-without.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/query/optional", + "Url": "https://bal-dev.azurewebsites.net/hello/query/optional", "Method": "GET", "Query": { }, @@ -52,10 +52,10 @@ "112.134.130.212:18792" ], "X-Original-URL": [ - "/api/hello/query/optional" + "/hello/query/optional" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/query/optional" + "/hello/query/optional" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -98,8 +98,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.130.212:18792", - "X-Original-URL": "/api/hello/query/optional", - "X-WAWS-Unencoded-URL": "/api/hello/query/optional", + "X-Original-URL": "/hello/query/optional", + "X-WAWS-Unencoded-URL": "/hello/query/optional", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/query-param.json b/ballerina-tests/tests/resources/query-param.json index 18ec1f5d..1cec095b 100644 --- a/ballerina-tests/tests/resources/query-param.json +++ b/ballerina-tests/tests/resources/query-param.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/query?name=test1", + "Url": "https://bal-dev.azurewebsites.net/hello/query?name=test1", "Method": "POST", "Query": { "name": "test1" @@ -53,10 +53,10 @@ "112.134.130.212:18792" ], "X-Original-URL": [ - "/api/hello/query?name=test1" + "/hello/query?name=test1" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/query?name=test1" + "/hello/query?name=test1" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -100,8 +100,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.130.212:18792", - "X-Original-URL": "/api/hello/query?name=test1", - "X-WAWS-Unencoded-URL": "/api/hello/query?name=test1", + "X-Original-URL": "/hello/query?name=test1", + "X-WAWS-Unencoded-URL": "/hello/query?name=test1", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/res-path-conflict-param.json b/ballerina-tests/tests/resources/res-path-conflict-param.json index 615a94b3..b9d26415 100644 --- a/ballerina-tests/tests/resources/res-path-conflict-param.json +++ b/ballerina-tests/tests/resources/res-path-conflict-param.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/foo/meow", + "Url": "https://bal-dev.azurewebsites.net/hello/foo/meow", "Method": "POST", "Query": {}, "Headers": { @@ -51,10 +51,10 @@ "112.134.128.105:42408" ], "X-Original-URL": [ - "/api/hello/foo/meow" + "/hello/foo/meow" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/meow" + "/hello/foo/meow" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -98,8 +98,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:42408", - "X-Original-URL": "/api/hello/foo/meow", - "X-WAWS-Unencoded-URL": "/api/hello/foo/meow", + "X-Original-URL": "/hello/foo/meow", + "X-WAWS-Unencoded-URL": "/hello/foo/meow", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/res-path-param.json b/ballerina-tests/tests/resources/res-path-param.json index 230ac700..d148318a 100644 --- a/ballerina-tests/tests/resources/res-path-param.json +++ b/ballerina-tests/tests/resources/res-path-param.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/foo/bar", + "Url": "https://bal-dev.azurewebsites.net/hello/foo/bar", "Method": "POST", "Query": {}, "Headers": { @@ -51,10 +51,10 @@ "112.134.128.105:42010" ], "X-Original-URL": [ - "/api/hello/foo/bar" + "/hello/foo/bar" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo/bar" + "/hello/foo/bar" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -95,8 +95,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:42010", - "X-Original-URL": "/api/hello/foo/bar", - "X-WAWS-Unencoded-URL": "/api/hello/foo/bar", + "X-Original-URL": "/hello/foo/bar", + "X-WAWS-Unencoded-URL": "/hello/foo/bar", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/res-path.json b/ballerina-tests/tests/resources/res-path.json index 6260426a..27c7db85 100644 --- a/ballerina-tests/tests/resources/res-path.json +++ b/ballerina-tests/tests/resources/res-path.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-dev.azurewebsites.net/api/hello/foo", + "Url": "https://bal-dev.azurewebsites.net/hello/foo", "Method": "POST", "Query": {}, "Headers": { @@ -51,10 +51,10 @@ "112.134.128.105:41478" ], "X-Original-URL": [ - "/api/hello/foo" + "/hello/foo" ], "X-WAWS-Unencoded-URL": [ - "/api/hello/foo" + "/hello/foo" ], "DISGUISED-HOST": [ "bal-dev.azurewebsites.net" @@ -95,8 +95,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.128.105:41478", - "X-Original-URL": "/api/hello/foo", - "X-WAWS-Unencoded-URL": "/api/hello/foo", + "X-Original-URL": "/hello/foo", + "X-WAWS-Unencoded-URL": "/hello/foo", "DISGUISED-HOST": "bal-dev.azurewebsites.net" }, "sys": { diff --git a/ballerina-tests/tests/resources/simple.json b/ballerina-tests/tests/resources/simple.json index 7785182f..91f655c2 100644 --- a/ballerina-tests/tests/resources/simple.json +++ b/ballerina-tests/tests/resources/simple.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://functions1778.azurewebsites.net/api/hello", + "Url": "https://functions1778.azurewebsites.net/hello", "Method": "POST", "Query": {}, "Headers": { @@ -12,12 +12,12 @@ "Host": ["functions1778.azurewebsites.net"], "Max-Forwards": ["9"], "User-Agent": ["curl/7.71.1"], - "X-WAWS-Unencoded-URL": ["/api/hello"], + "X-WAWS-Unencoded-URL": ["/hello"], "CLIENT-IP": ["10.0.128.35:48692"], "X-ARR-LOG-ID": ["b1d1b77a-689a-41a0-8e6e-aeb9563c7c41"], "X-SITE-DEPLOYMENT-ID": ["functions1778"], "WAS-DEFAULT-HOSTNAME": ["functions1778.azurewebsites.net"], - "X-Original-URL": ["/api/hello"], + "X-Original-URL": ["/hello"], "X-Forwarded-For": ["45.30.94.9:41710"], "X-ARR-SSL": ["2048|256|C=US, O=Microsoft Corporation, CN=Microsoft RSA TLS CA 01|CN=*.azurewebsites.net"], "X-Forwarded-Proto": ["https"], @@ -52,12 +52,12 @@ "Host": "functions1778.azurewebsites.net", "Max-Forwards": "9", "User-Agent": "curl/7.71.1", - "X-WAWS-Unencoded-URL": "/api/hello", + "X-WAWS-Unencoded-URL": "/hello", "CLIENT-IP": "10.0.128.35:48692", "X-ARR-LOG-ID": "b1d1b77a-689a-41a0-8e6e-aeb9563c7c41", "X-SITE-DEPLOYMENT-ID": "functions1778", "WAS-DEFAULT-HOSTNAME": "functions1778.azurewebsites.net", - "X-Original-URL": "/api/hello", + "X-Original-URL": "/hello", "X-Forwarded-For": "45.30.94.9:41710", "X-ARR-SSL": "2048|256|C=US, O=Microsoft Corporation, CN=Microsoft RSA TLS CA 01|CN=*.azurewebsites.net", "X-Forwarded-Proto": "https", diff --git a/ballerina-tests/tests/resources/temp.json b/ballerina-tests/tests/resources/temp.json index 4d62b253..70e920b0 100644 --- a/ballerina-tests/tests/resources/temp.json +++ b/ballerina-tests/tests/resources/temp.json @@ -1,7 +1,7 @@ { "Data": { "httpPayload": { - "Url": "https://bal-svc-impl.azurewebsites.net/api/hello", + "Url": "https://bal-svc-impl.azurewebsites.net/hello", "Method": "POST", "Query": {}, "Headers": { @@ -51,10 +51,10 @@ "112.134.134.3:36070" ], "X-Original-URL": [ - "/api/hello" + "/hello" ], "X-WAWS-Unencoded-URL": [ - "/api/hello" + "/hello" ], "DISGUISED-HOST": [ "bal-svc-impl.azurewebsites.net" @@ -97,8 +97,8 @@ "X-ARR-SSL": "2048|256|CN=Microsoft Azure TLS Issuing CA 01, O=Microsoft Corporation, C=US|CN=*.azurewebsites.net, O=Microsoft Corporation, L=Redmond, S=WA, C=US", "X-Forwarded-TlsVersion": "1.2", "X-Forwarded-For": "112.134.134.3:36070", - "X-Original-URL": "/api/hello", - "X-WAWS-Unencoded-URL": "/api/hello", + "X-Original-URL": "/hello", + "X-WAWS-Unencoded-URL": "/hello", "DISGUISED-HOST": "bal-svc-impl.azurewebsites.net" }, "sys": { From 4580f47a335af710ed3dd32910e31249d7634d50 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 17:26:04 +0530 Subject: [PATCH 43/59] Fix tests --- .../ballerinax/azurefunctions/FunctionsArtifact.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index 5ef70766..08b73a1b 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -176,10 +176,12 @@ public void generate() throws IOException { } private void deleteDirectory(Path azureFunctionsDir) throws IOException { - Files.walk(azureFunctionsDir) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); + if (azureFunctionsDir.toFile().exists()) { + Files.walk(azureFunctionsDir) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } } private void generateLocalSettings(Path azureFunctionsDir) throws IOException { From 2905d95b15ab9762277056be95451ba5b576ac44 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 19:32:58 +0530 Subject: [PATCH 44/59] [Automated] Update the native jar versions --- ballerina-tests/Ballerina.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina-tests/Ballerina.toml b/ballerina-tests/Ballerina.toml index 71ce6b06..30e22734 100644 --- a/ballerina-tests/Ballerina.toml +++ b/ballerina-tests/Ballerina.toml @@ -1,4 +1,4 @@ [package] org = "ballerinax" name = "azure_functions_tests" -version = "3.0.0" +version = "3.0.0-alpha.1" From 8c2c86e1076367b8530193ca4be5ae00356e5c6b Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 19:32:58 +0530 Subject: [PATCH 45/59] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 4 ++-- ballerina/CompilerPlugin.toml | 2 +- ballerina/Dependencies.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 471b66c0..84915d24 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,10 +1,10 @@ [package] org = "ballerinax" name = "azure_functions" -version = "3.0.0" +version = "3.0.0-alpha.1" [[platform.java11.dependency]] -path = "../native/build/libs/azure_functions-native-3.0.0-SNAPSHOT.jar" +path = "../native/build/libs/azure_functions-native-3.0.0-alpha.1.jar" #[[platform.java11.dependency]] #path = "./lib/http-native-@stdlib.httpnative.version@.jar" diff --git a/ballerina/CompilerPlugin.toml b/ballerina/CompilerPlugin.toml index fcbcadaf..f2c21aba 100644 --- a/ballerina/CompilerPlugin.toml +++ b/ballerina/CompilerPlugin.toml @@ -3,4 +3,4 @@ id = "azure-functions" class = "org.ballerinax.azurefunctions.AzureCompilerPlugin" [[dependency]] -path = "../compiler-plugin/build/libs/azure_functions-compiler-plugin-3.0.0-SNAPSHOT.jar" +path = "../compiler-plugin/build/libs/azure_functions-compiler-plugin-3.0.0-alpha.1.jar" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 3f0fc2f0..60d95fed 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -288,7 +288,7 @@ dependencies = [ [[package]] org = "ballerinax" name = "azure_functions" -version = "3.0.0" +version = "3.0.0-alpha.1" dependencies = [ {org = "ballerina", name = "http"}, {org = "ballerina", name = "io"}, From ce446a71e7a43ff2d4390c57eaa79eefea6816ce Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Mon, 10 Oct 2022 23:35:42 +0530 Subject: [PATCH 46/59] Add pre release --- .github/workflows/publish-pre-release.yml | 58 +++++++++++++++++++ .../test/resources/handlers/Ballerina.toml | 5 ++ .../http-header-annotation/Ballerina.toml | 5 ++ gradle.properties | 2 +- 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/publish-pre-release.yml diff --git a/.github/workflows/publish-pre-release.yml b/.github/workflows/publish-pre-release.yml new file mode 100644 index 00000000..c7eb5e41 --- /dev/null +++ b/.github/workflows/publish-pre-release.yml @@ -0,0 +1,58 @@ +name: Publish pre-release + +on: + workflow_dispatch: + +jobs: + publish-release: + runs-on: ubuntu-latest + if: github.repository_owner == 'ballerina-platform' + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: 11 + - name: Build with Gradle + env: + packageUser: ${{ github.actor }} + packagePAT: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.name ${{ secrets.BALLERINA_BOT_USERNAME }} + git config --global user.email ${{ secrets.BALLERINA_BOT_EMAIL }} + ./gradlew build -x check -x test + - name: Set version env variable + run: echo "VERSION=$((grep -w 'version' | cut -d= -f2) < gradle.properties)" >> $GITHUB_ENV + - name: Pre release dependency version update + env: + GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }} + run: | + echo "Version: ${VERSION}" + git checkout -b release-${VERSION} + sed -i 's/ballerinaLangVersion=\(.*\)-SNAPSHOT/ballerinaLangVersion=\1/g' gradle.properties + sed -i 's/ballerinaLangVersion=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/ballerinaLangVersion=\1/g' gradle.properties + sed -i 's/stdlib\(.*\)=\(.*\)-SNAPSHOT/stdlib\1=\2/g' gradle.properties + sed -i 's/stdlib\(.*\)=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/stdlib\1=\2/g' gradle.properties + sed -i 's/observe\(.*\)=\(.*\)-SNAPSHOT/observe\1=\2/g' gradle.properties + sed -i 's/observe\(.*\)=\(.*\)-[0-9]\{8\}-[0-9]\{6\}-.*$/observe\1=\2/g' gradle.properties + git add gradle.properties + git commit -m "Move dependencies to stable version" || echo "No changes to commit" + - name: Publish artifact + env: + GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }} + BALLERINA_CENTRAL_ACCESS_TOKEN: ${{ secrets.BALLERINA_CENTRAL_ACCESS_TOKEN }} + packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }} + packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }} + publishUser: ${{ secrets.BALLERINA_BOT_USERNAME }} + publishPAT: ${{ secrets.BALLERINA_BOT_TOKEN }} + run: | + ./gradlew clean release -Prelease.useAutomaticVersion=true + ./gradlew -Pversion=${VERSION} publish -x test -PpublishToCentral=true + - name: GitHub Release and Release Sync PR + env: + GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }} + run: | + gh release create v$VERSION --title "module-ballerinax-azure.functions-v$VERSION" + gh pr create --title "[Automated] Sync master after $VERSION release" --body "Sync master after $VERSION release" diff --git a/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml b/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml index 71ce6b06..2758f261 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml +++ b/compiler-plugin-tests/src/test/resources/handlers/Ballerina.toml @@ -2,3 +2,8 @@ org = "ballerinax" name = "azure_functions_tests" version = "3.0.0" + +[[dependency]] +org = "ballerinax" +name = "azure_functions" +version = "3.0.0-alpha.1" diff --git a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml index a2ead863..e9f5cff9 100644 --- a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml +++ b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/Ballerina.toml @@ -6,3 +6,8 @@ distribution = "2201.1.0" [build-options] observabilityIncluded = true + +[[dependency]] +org = "ballerinax" +name = "azure_functions" +version = "3.0.0-alpha.1" diff --git a/gradle.properties b/gradle.properties index 203022ad..956c626b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.caching=true org.gradle.jvmargs='-Dfile.encoding=UTF-8' group=org.ballerinax.azurefunctions -version=3.0.0-SNAPSHOT +version=3.0.0-alpha.1 systemProp.org.gradle.internal.publish.checksums.insecure=true ballerinaLangVersion=2201.1.0 ballerinaGradlePluginVersion=0.14.3 From d9fc249e0952d5c5421385147ce91babcbfde72b Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 09:52:16 +0530 Subject: [PATCH 47/59] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 84915d24..44c7ab8e 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -10,4 +10,4 @@ path = "../native/build/libs/azure_functions-native-3.0.0-alpha.1.jar" #path = "./lib/http-native-@stdlib.httpnative.version@.jar" [[platform.java11.dependency]] -path = "./lib/mime-native-2.3.0.jar" +path = "./lib/mime-native-2.4.0.jar" From 2adc6613ab3bda39c34097b83b3e02b246f8b119 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 09:57:22 +0530 Subject: [PATCH 48/59] [Automated] Update the native jar versions --- ballerina-tests/Ballerina.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ballerina-tests/Ballerina.toml b/ballerina-tests/Ballerina.toml index 30e22734..1645dbc2 100644 --- a/ballerina-tests/Ballerina.toml +++ b/ballerina-tests/Ballerina.toml @@ -2,3 +2,8 @@ org = "ballerinax" name = "azure_functions_tests" version = "3.0.0-alpha.1" + +[[dependency]] +org = "ballerinax" +name = "azure_functions" +version = "3.0.0-alpha.1" From 008a5d78a027c7226ed4ef92d1a0c5e58063e2ff Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 11:18:09 +0530 Subject: [PATCH 49/59] Bump dependencies to latest and related fixes --- ballerina-tests/main.bal | 2 +- build-config/resources/BallerinaTest.toml | 5 ++++ .../src/test/resources/handlers/main.bal | 2 +- .../http-header-annotation/main.bal | 2 +- .../AzureFunctionNameGenerator.java | 3 ++- .../service/http/HTTPTriggerBinding.java | 3 ++- gradle.properties | 27 ++++++++++--------- .../stdlib/azure/functions/HttpResource.java | 12 +++++---- 8 files changed, 33 insertions(+), 23 deletions(-) diff --git a/ballerina-tests/main.bal b/ballerina-tests/main.bal index 5c16e318..33ee6b15 100644 --- a/ballerina-tests/main.bal +++ b/ballerina-tests/main.bal @@ -5,7 +5,7 @@ public type DBEntry record { string id; }; -type Person record { +public type Person record { string name; int age; }; diff --git a/build-config/resources/BallerinaTest.toml b/build-config/resources/BallerinaTest.toml index 72d040e2..72a1eb02 100644 --- a/build-config/resources/BallerinaTest.toml +++ b/build-config/resources/BallerinaTest.toml @@ -2,3 +2,8 @@ org = "ballerinax" name = "azure_functions_tests" version = "@toml.version@" + +[[dependency]] +org = "ballerinax" +name = "azure_functions" +version = "3.0.0-alpha.1" diff --git a/compiler-plugin-tests/src/test/resources/handlers/main.bal b/compiler-plugin-tests/src/test/resources/handlers/main.bal index f168e0af..804920e0 100644 --- a/compiler-plugin-tests/src/test/resources/handlers/main.bal +++ b/compiler-plugin-tests/src/test/resources/handlers/main.bal @@ -7,7 +7,7 @@ public type DBEntry record { string id; }; -type Person record { +public type Person record { string name; int age; }; diff --git a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal index c9bf2556..7f7d782f 100644 --- a/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal +++ b/compiler-plugin-tests/src/test/resources/validations/http-header-annotation/main.bal @@ -3,7 +3,7 @@ import ballerina/http; listener af:HttpListener ep = new (); -type Person record {| +public type Person record {| readonly int id; |}; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index a9f1917d..96264da3 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -45,7 +45,8 @@ private String getFunctionName(String servicePath, FunctionDefinitionNode functi String specialCharReplacedPathBlock = (((IdentifierToken) pathBlock).text()).replace("\\", ""); resourcePath.append("/").append(specialCharReplacedPathBlock); } else if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { - Token token = ((ResourcePathParameterNode) pathBlock).paramName(); + //TODO changed + Token token = ((ResourcePathParameterNode) pathBlock).paramName().get(); resourcePath.append("/").append(token.text()); } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index 3304605d..6b47f5c5 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -84,7 +84,8 @@ public List getBindings() { if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { ResourcePathParameterNode pathParamNode = (ResourcePathParameterNode) pathBlock; //TODO Handle optional - resourcePath.append("/" + "{").append(pathParamNode.paramName().text()).append("}"); + //TODO changed + resourcePath.append("/" + "{").append(pathParamNode.paramName().get().text()).append("}"); continue; } //TODO add wildcard diff --git a/gradle.properties b/gradle.properties index 956c626b..52f6feed 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,23 +3,24 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8' group=org.ballerinax.azurefunctions version=3.0.0-alpha.1 systemProp.org.gradle.internal.publish.checksums.insecure=true -ballerinaLangVersion=2201.1.0 -ballerinaGradlePluginVersion=0.14.3 -stdlibIoVersion=1.2.2 -stdlibLogVersion=2.3.0 -stdlibHttpVersion=2.3.0 -stdlibAuthVersion=2.3.0 -stdlibFileVersion=1.3.0 +ballerinaLangVersion=2201.2.1 +stdlibConstraintVersion=1.0.0 +stdlibIoVersion=1.3.0 +stdlibLogVersion=2.4.0 +stdlibHttpVersion=2.4.0 +stdlibAuthVersion=2.4.0 +stdlibFileVersion=1.4.0 stdlibRegexVersion=1.3.0 stdlibCacheVersion=3.2.2 stdlibCryptoVersion=2.2.2 stdlibTimeVersion=2.2.2 -stdlibMimeVersion=2.3.0 -stdlibOsVersion=1.3.0 +stdlibMimeVersion=2.4.0 +stdlibOsVersion=1.4.0 stdlibTaskVersion=2.2.2 -stdlibJwtVersion=2.3.0 -stdlibOAuth2Version=2.3.0 +stdlibJwtVersion=2.4.0 +stdlibOAuth2Version=2.4.0 stdlibUuidVersion=1.3.0 stdlibUrlVersion=2.2.2 -observeVersion=1.0.4 -observeInternalVersion=1.0.3 +observeVersion=1.0.5 +observeInternalVersion=1.0.4 +ballerinaGradlePluginVersion=0.14.3 diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index 616c8a99..d1e59131 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.RecordType; +import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.UnionType; @@ -291,8 +292,9 @@ private Optional processHeaderParam(ResourceMethodType resource BMap headerAnnotationField = (BMap) ((BMap) annotation).get(StringUtils.fromString(headerAnnotation)); if (headerAnnotationField.size() == 0) { //No annotation field defined {name: ....} - if ((parameter.type).getTag() == TypeTags.RECORD_TYPE_TAG) { - headerParam = processHeaderRecordParam(headers, parameter, treatNilableAsOptional); + if ((parameter.type).getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { + ReferenceType type = (ReferenceType) parameter.type; + headerParam = processHeaderRecordParam(headers, type, treatNilableAsOptional); return Optional.of(new HeaderParameter(i, parameter, headerParam)); } headerParam = getHeaderValue(headers, parameter.type, name, treatNilableAsOptional); @@ -324,7 +326,7 @@ private Object getHeaderValue(BMap headers, Type type, String fieldN headerValue = (BString) ((BArray) (headers.get(headerKey))).get(0); } } - if (isHeaderExist == false) { + if (!isHeaderExist) { //Header name not exist case if (isNilType(type) && treatNilableAsOptional) { return null; @@ -341,9 +343,9 @@ private Object getHeaderValue(BMap headers, Type type, String fieldN return createValue(type, headerValue); } - private Object processHeaderRecordParam(BMap headers, Parameter parameter, + private Object processHeaderRecordParam(BMap headers, ReferenceType parameter, Boolean treatNilableAsOptional) { - RecordType recordType = (RecordType) parameter.type; + RecordType recordType = (RecordType) parameter.getReferredType(); Map fields = recordType.getFields(); BMap recordValue = ValueCreator.createRecordValue(recordType); for (Map.Entry field : fields.entrySet()) { From 44856cda050e7d90d6a5ab754aadb4bb368c79e1 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 11:29:51 +0530 Subject: [PATCH 50/59] Address review suggestions --- .../ballerinax/azurefunctions/AzureFunctionNameGenerator.java | 1 - .../azurefunctions/service/http/HTTPTriggerBinding.java | 1 - 2 files changed, 2 deletions(-) diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index 96264da3..25e7bd96 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -45,7 +45,6 @@ private String getFunctionName(String servicePath, FunctionDefinitionNode functi String specialCharReplacedPathBlock = (((IdentifierToken) pathBlock).text()).replace("\\", ""); resourcePath.append("/").append(specialCharReplacedPathBlock); } else if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { - //TODO changed Token token = ((ResourcePathParameterNode) pathBlock).paramName().get(); resourcePath.append("/").append(token.text()); } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index 6b47f5c5..0a852d28 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -84,7 +84,6 @@ public List getBindings() { if (pathBlock.kind() == SyntaxKind.RESOURCE_PATH_SEGMENT_PARAM) { ResourcePathParameterNode pathParamNode = (ResourcePathParameterNode) pathBlock; //TODO Handle optional - //TODO changed resourcePath.append("/" + "{").append(pathParamNode.paramName().get().text()).append("}"); continue; } From 0c9376303972f869f978c65ef0a2104eb637fb34 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 11:37:12 +0530 Subject: [PATCH 51/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 60d95fed..f636767a 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -9,7 +9,7 @@ dependencies-toml-version = "2" [[package]] org = "ballerina" name = "auth" -version = "2.3.1" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "jballerina.java"}, @@ -29,6 +29,14 @@ dependencies = [ {org = "ballerina", name = "time"} ] +[[package]] +org = "ballerina" +name = "constraint" +version = "1.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + [[package]] org = "ballerina" name = "crypto" @@ -41,7 +49,7 @@ dependencies = [ [[package]] org = "ballerina" name = "file" -version = "1.3.0" +version = "1.4.0" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -54,10 +62,11 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.3.0" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "file"}, {org = "ballerina", name = "io"}, @@ -84,7 +93,7 @@ modules = [ [[package]] org = "ballerina" name = "io" -version = "1.2.2" +version = "1.3.0" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.value"} @@ -104,7 +113,7 @@ modules = [ [[package]] org = "ballerina" name = "jwt" -version = "2.3.1" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -202,7 +211,7 @@ dependencies = [ [[package]] org = "ballerina" name = "log" -version = "2.3.0" +version = "2.4.1" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -213,7 +222,7 @@ dependencies = [ [[package]] org = "ballerina" name = "mime" -version = "2.3.0" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -223,7 +232,7 @@ dependencies = [ [[package]] org = "ballerina" name = "oauth2" -version = "2.3.1" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "cache"}, {org = "ballerina", name = "crypto"}, @@ -243,8 +252,9 @@ dependencies = [ [[package]] org = "ballerina" name = "os" -version = "1.3.0" +version = "1.4.0" dependencies = [ + {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"} ] modules = [ From dc02644c5c7fe01b4535eb1099bc281139689532 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 11:42:29 +0530 Subject: [PATCH 52/59] Add constraint lib to the pack --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index aae6d775..655a09a6 100644 --- a/build.gradle +++ b/build.gradle @@ -84,6 +84,7 @@ subprojects { } /* Standard libraries */ + ballerinaStdLibs "io.ballerina.stdlib:constraint-ballerina:${stdlibConstraintVersion}" ballerinaStdLibs "io.ballerina.stdlib:io-ballerina:${stdlibIoVersion}" ballerinaStdLibs "io.ballerina.stdlib:log-ballerina:${stdlibLogVersion}" ballerinaStdLibs "io.ballerina.stdlib:regex-ballerina:${stdlibRegexVersion}" From 57ad32b197661bc4d99d70d2765a8ad6487c871d Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 13:58:48 +0530 Subject: [PATCH 53/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index f636767a..b208f89a 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -211,7 +211,7 @@ dependencies = [ [[package]] org = "ballerina" name = "log" -version = "2.4.1" +version = "2.4.0" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, From f6ddca764c056da6bd65c4bb75d7184e88e2949e Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 14:36:02 +0530 Subject: [PATCH 54/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index b208f89a..75c93166 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -98,9 +98,6 @@ dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.value"} ] -modules = [ - {org = "ballerina", packageName = "io", moduleName = "io"} -] [[package]] org = "ballerina" @@ -211,7 +208,7 @@ dependencies = [ [[package]] org = "ballerina" name = "log" -version = "2.4.0" +version = "2.4.1" dependencies = [ {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, @@ -301,7 +298,6 @@ name = "azure_functions" version = "3.0.0-alpha.1" dependencies = [ {org = "ballerina", name = "http"}, - {org = "ballerina", name = "io"}, {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.array"}, {org = "ballerina", name = "lang.boolean"}, From 07ffbe2762cb99963a0e6bdebfc00ed45e39d8c2 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Tue, 11 Oct 2022 22:36:55 +0530 Subject: [PATCH 55/59] Refactor Code --- ballerina-tests/build.gradle | 2 +- ballerina/dispatcher_service.bal | 17 +- ballerina/http_listener.bal | 9 +- ballerina/http_service.bal | 3 - .../AzureFunctionDiagnostics.java | 61 ----- .../azurefunctions/AzureFunctionModifier.java | 27 ++- .../AzureFunctionNameGenerator.java | 27 ++- .../AzureFunctionServiceVisitor.java | 25 +-- .../AzureFunctionsException.java | 42 ---- .../ballerinax/azurefunctions/Constants.java | 18 +- .../azurefunctions/FunctionContext.java | 17 ++ .../azurefunctions/FunctionUpdaterTask.java | 17 ++ .../azurefunctions/FunctionsArtifact.java | 4 - .../org/ballerinax/azurefunctions/Util.java | 3 + .../azurefunctions/service/Binding.java | 17 ++ .../azurefunctions/service/InputBinding.java | 17 ++ .../service/InputBindingBuilder.java | 26 ++- .../azurefunctions/service/OutputBinding.java | 17 ++ .../service/OutputBindingBuilder.java | 30 ++- .../service/RemoteTriggerBinding.java | 38 ++-- .../service/ServiceHandler.java | 149 ++----------- .../service/TriggerBinding.java | 37 +-- .../service/blob/BlobInputBinding.java | 17 ++ .../service/blob/BlobOutputBinding.java | 20 +- .../service/blob/BlobTriggerBinding.java | 17 ++ .../cosmosdb/CosmosDBInputBinding.java | 17 ++ .../cosmosdb/CosmosDBOutputBinding.java | 20 +- .../cosmosdb/CosmosDBTriggerBinding.java | 17 ++ .../service/http/HTTPOutputBinding.java | 17 ++ .../service/http/HTTPTriggerBinding.java | 47 ++-- .../service/queue/QueueOutputBinding.java | 20 +- .../service/queue/QueueTriggerBinding.java | 17 ++ .../service/timer/TimerTriggerBinding.java | 17 ++ .../twilio/TwilioSmsOutputBinding.java | 20 +- .../AzureFunctionsCodeAnalyzerTask.java | 10 +- .../validators/HttpListenerValidator.java | 12 +- native/spotbugs-exclude.xml | 4 + .../stdlib/azure/functions/Constants.java | 29 ++- .../azure/functions/FunctionCallback.java | 45 ++-- .../stdlib/azure/functions/HttpResource.java | 210 ++++-------------- .../functions/NativeHttpToAzureAdaptor.java | 15 +- .../azure/functions/NativeRemoteAdapter.java | 2 +- .../stdlib/azure/functions/ParamHandler.java | 2 +- .../stdlib/azure/functions/Utils.java | 146 +++++++++++- .../functions/builder/JsonPayloadBuilder.java | 5 +- .../functions/builder/XmlPayloadBuilder.java | 2 - .../converter/JsonToRecordConverter.java | 15 -- 47 files changed, 749 insertions(+), 597 deletions(-) delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java delete mode 100644 compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java diff --git a/ballerina-tests/build.gradle b/ballerina-tests/build.gradle index 26c95cbd..a1e3030b 100644 --- a/ballerina-tests/build.gradle +++ b/ballerina-tests/build.gradle @@ -22,7 +22,7 @@ import org.apache.tools.ant.taskdefs.condition.Os description = 'Ballerinax - Azure Functions Tests' def packageName = "azure_functions" -def packageOrg = "ballerinax" //TODO change +def packageOrg = "ballerinax" def moduleName = "tests" def tomlVersion = stripBallerinaExtensionVersion("${project.version}") def ballerinaTomlFilePlaceHolder = new File("${project.rootDir}/build-config/resources/BallerinaTest.toml") diff --git a/ballerina/dispatcher_service.bal b/ballerina/dispatcher_service.bal index aa078974..26957514 100644 --- a/ballerina/dispatcher_service.bal +++ b/ballerina/dispatcher_service.bal @@ -15,7 +15,6 @@ // under the License. import ballerina/http; -import ballerina/io; isolated service class DispatcherService { *http:Service; @@ -30,21 +29,11 @@ isolated service class DispatcherService { isolated resource function post .(http:Caller caller, http:Request request) returns error? { http:Response response = new; - json message = check request.getJsonPayload(); - io:println(message.toJsonString()); - //map body = >check message.Data; + json platformPayload = check request.getJsonPayload(); - map|error callRegisterMethod = self.adaptor.callRemoteFunction(>message, self - .remoteMethodName); - if (callRegisterMethod is error) { - io:println (callRegisterMethod); - return; - } - json result = {Outputs: callRegisterMethod.toJson(), Logs: []}; - result = check result.mergeJson({ReturnValue: null}); - io:println(result); - response.setJsonPayload(result); + map callRegisterMethod = check self.adaptor.callRemoteFunction(>platformPayload, self.remoteMethodName); + response.setJsonPayload({Outputs: callRegisterMethod.toJson(), Logs: [], ReturnValue: null}); check caller->respond(response); } } diff --git a/ballerina/http_listener.bal b/ballerina/http_listener.bal index 14ce876b..827e2162 100644 --- a/ballerina/http_listener.bal +++ b/ballerina/http_listener.bal @@ -14,8 +14,6 @@ // specific language governing permissions and limitations // under the License. -import ballerina/io; - public class HttpListener { ResourceService[] httpServices; @@ -25,11 +23,10 @@ public class HttpListener { public function attach(HttpService svc, string[]|string? name = ()) returns error? { HttpToAzureAdaptor adaptor = new(svc); - string[] resList = adaptor.getAzureFunctionNames(); - foreach string resPath in resList{ - io:println(resPath); + string[] resourcePaths = adaptor.getAzureFunctionNames(); + foreach string resourcePath in resourcePaths{ ResourceService httpService = new (adaptor); - check httpListener.attach(httpService, resPath); + check httpListener.attach(httpService, resourcePath); } } diff --git a/ballerina/http_service.bal b/ballerina/http_service.bal index 4be9bb78..7f1c03b3 100644 --- a/ballerina/http_service.bal +++ b/ballerina/http_service.bal @@ -15,7 +15,6 @@ // under the License. import ballerina/http; -import ballerina/io; isolated service class ResourceService { *http:Service; @@ -29,12 +28,10 @@ isolated service class ResourceService { isolated resource function post .(http:Caller caller, http:Request request) returns error? { http:Response response = new; json message = check request.getJsonPayload(); - io:println(message.toJsonString()); Payload payload = check message.cloneWithType(Payload); string functionName = payload.Metadata.sys.MethodName; map|error callRegisterMethod = self.adaptor.callNativeMethod(payload.Data, functionName); response.setJsonPayload(getResponsePayload(callRegisterMethod)); - //io:println(getResponsePayload(callRegisterMethod)); check caller->respond(response); } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java deleted file mode 100644 index 589f2b29..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionDiagnostics.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions; - -import io.ballerina.tools.diagnostics.Diagnostic; -import io.ballerina.tools.diagnostics.DiagnosticInfo; -import io.ballerina.tools.diagnostics.DiagnosticProperty; -import io.ballerina.tools.diagnostics.Location; - -import java.util.List; - -/** - * This util class use for creating Azure Function diagnostic. - */ -public class AzureFunctionDiagnostics extends Diagnostic { - - private Location location; - private DiagnosticInfo diagnosticInfo; - private String message; - - public AzureFunctionDiagnostics(Location location, DiagnosticInfo diagnosticInfo, String message) { - this.location = location; - this.diagnosticInfo = diagnosticInfo; - this.message = message; - } - - @Override - public Location location() { - return this.location; - } - - @Override - public DiagnosticInfo diagnosticInfo() { - return this.diagnosticInfo; - } - - @Override - public String message() { - return this.message; - } - - @Override - public List> properties() { - return null; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index da192c02..448572e7 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; import io.ballerina.compiler.api.SemanticModel; @@ -51,7 +68,7 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio return super.transform(serviceDeclarationNode); } TypeReferenceTypeSymbol typeSymbol1; - if (typeSymbol.get().typeKind() == TypeDescKind.UNION) { + if (TypeDescKind.UNION == typeSymbol.get().typeKind()) { UnionTypeSymbol union = (UnionTypeSymbol) typeSymbol.get(); typeSymbol1 = (TypeReferenceTypeSymbol) union.memberTypeDescriptors().get(0); @@ -63,7 +80,7 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio return super.transform(serviceDeclarationNode); } NodeList members = serviceDeclarationNode.members(); - if (!name.get().equals("HttpListener")) { + if (!Constants.AZURE_HTTP_LISTENER.equals(name.get())) { return super.transform(serviceDeclarationNode); } AzureFunctionNameGenerator nameGen = new AzureFunctionNameGenerator(serviceDeclarationNode); @@ -81,7 +98,7 @@ public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclaratio } public Optional getModifiedMember(Node node, String servicePath, AzureFunctionNameGenerator nameGen) { - if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION != node.kind()) { return Optional.empty(); } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; @@ -98,7 +115,7 @@ public Optional getModifiedMember(Node node, String servicePath, AzureFunc //Create and add annotation NodeList modifiedAnnotations = - existingAnnotations.add(getFunctionNameAnnotation(uniqueFunctionName)); + existingAnnotations.add(createFunctionAnnotation(uniqueFunctionName)); MetadataNode modifiedMetadata = new MetadataNode.MetadataNodeModifier(metadataNode).withAnnotations(modifiedAnnotations).apply(); FunctionDefinitionNode updatedFunctionNode = @@ -107,7 +124,7 @@ public Optional getModifiedMember(Node node, String servicePath, AzureFunc return Optional.of(updatedFunctionNode); } - public AnnotationNode getFunctionNameAnnotation(String functionName) { + public AnnotationNode createFunctionAnnotation(String functionName) { QualifiedNameReferenceNode azureFunctionAnnotRef = NodeFactory.createQualifiedNameReferenceNode(NodeFactory.createIdentifierToken(modulePrefix), NodeFactory.createToken(SyntaxKind.COLON_TOKEN), diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index 25e7bd96..c8639895 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; @@ -14,7 +31,7 @@ /** * Responsible for generating Azure function name for each resource function. - * + * * @since 2.0.0 */ public class AzureFunctionNameGenerator { @@ -26,7 +43,7 @@ public AzureFunctionNameGenerator(ServiceDeclarationNode serviceDeclarationNode) NodeList members = serviceDeclarationNode.members(); String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); for (Node node : members) { - if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION != node.kind()) { continue; } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; @@ -49,7 +66,11 @@ private String getFunctionName(String servicePath, FunctionDefinitionNode functi resourcePath.append("/").append(token.text()); } } - String functionName = resourcePath.toString().replace("/", "-"); + return getEncodedAzureFunctionName(resourcePath.toString(), servicePath, method); + } + + private String getEncodedAzureFunctionName(String resourcePath, String servicePath, String method) { + String functionName = resourcePath.replace("/", "-"); if (servicePath.equals("")) { return method + functionName; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java index bc514a09..3a420e5f 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java @@ -32,8 +32,7 @@ * @since 2.0.0 */ public class AzureFunctionServiceVisitor extends NodeVisitor { - - // private String moduleName; + private List functionContexts; private SemanticModel semanticModel; @@ -47,28 +46,6 @@ public void visit(ModulePartNode modulePartNode) { super.visit(modulePartNode); } -// @Override -// public void visit(ImportDeclarationNode importDeclarationNode) { -// if (importDeclarationNode.orgName().isEmpty()) { -// return; -// } -// String orgName = importDeclarationNode.orgName().get().orgName().text(); -// if (!Constants.AZURE_FUNCTIONS_PACKAGE_ORG.equals(orgName)) { -// return; -// } -// if (importDeclarationNode.moduleName().size() != 1) { -// return; -// } -// String moduleName = importDeclarationNode.moduleName().get(0).text(); -// if (Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(moduleName)) { -// this.moduleName = moduleName; -// } -// if (importDeclarationNode.prefix().isEmpty()) { -// return; -// } -// this.moduleName = importDeclarationNode.prefix().get().prefix().text(); -// } - @Override public void visit(ServiceDeclarationNode serviceDeclarationNode) { TriggerBinding builder = ServiceHandler.getBuilder(serviceDeclarationNode, semanticModel); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java deleted file mode 100644 index e48305ad..00000000 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionsException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 org.ballerinax.azurefunctions; - -import io.ballerina.tools.diagnostics.Diagnostic; - -/** - * Represents Azure functions compiler extension errors. - */ -public class AzureFunctionsException extends Exception { - - private static final long serialVersionUID = -6540373040546296073L; - private transient Diagnostic diagnostic; - - public AzureFunctionsException(Diagnostic diagnostic) { - super(diagnostic.message()); - this.diagnostic = diagnostic; - } - - public AzureFunctionsException(Diagnostic diagnostic, Throwable cause) { - super(diagnostic.message(), cause); - } - - public Diagnostic getDiagnostic() { - return diagnostic; - } -} diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index 34561302..abb771d4 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -24,7 +24,6 @@ public class Constants { public static final String AZURE_FUNCTIONS_MODULE_NAME = "azure_functions"; public static final String HTTP = "http"; - public static final String AZURE_FUNCTIONS = "azure_functions"; public static final String HEADER_ANNOTATION_TYPE = "HttpHeader"; public static final String AZURE_FUNCTIONS_PACKAGE_ORG = "ballerinax"; public static final String CHARSET = "UTF-8"; @@ -34,9 +33,26 @@ public class Constants { public static final String ANNOTATION_COSMOS_TRIGGER = "CosmosDBTrigger"; public static final String ANNOTATION_TIMER_TRIGGER = "TimerTrigger"; public static final String ANNOTATION_BLOB_TRIGGER = "BlobTrigger"; + + public static final String AZURE_HTTP_LISTENER = "HttpListener"; + public static final String AZURE_QUEUE_LISTENER = "QueueListener"; + public static final String AZURE_COSMOS_LISTENER = "CosmosDBListener"; + public static final String AZURE_TIMER_LISTENER = "TimerListener"; + public static final String AZURE_BLOB_LISTENER = "BlobListener"; + public static final String COSMOS_INPUT_BINDING = "CosmosDBInput"; + public static final String BLOB_INPUT_BINDING = "BlobInput"; + + public static final String QUEUE_OUTPUT_BINDING = "QueueOutput"; + public static final String HTTP_OUTPUT_BINDING = "HttpOutput"; + public static final String COSMOS_OUTPUT_BINDING = "CosmosDBOutput"; + public static final String TWILIO_OUTPUT_BINDING = "TwilioSmsOutput"; + public static final String BLOB_OUTPUT_BINDING = "BlobOutput"; + public static final String DIRECTION_IN = "in"; public static final String DIRECTION_OUT = "out"; + + public static final String RETURN_VAR_NAME = "outMsg"; public static final String FUNCTION_DIRECTORY = "azure_functions"; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java index 66a49679..0c1b31fc 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionContext.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; import org.ballerinax.azurefunctions.service.Binding; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java index 799d46d8..966e2238 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionUpdaterTask.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions; import io.ballerina.compiler.api.SemanticModel; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java index 08b73a1b..d77a6771 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/FunctionsArtifact.java @@ -74,10 +74,6 @@ public Map getFunctions() { return functions; } - public Path getBinaryPath() { - return binaryPath; - } - private JsonObject readExistingHostJson() throws IOException { File file = new File(HOST_JSON_NAME); if (file.exists()) { diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index 5d8e11ce..0128d3df 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -51,6 +51,9 @@ public class Util { public static Optional extractValueFromAnnotationField(SpecificFieldNode fieldNode) { Optional expressionNode = fieldNode.valueExpr(); + if (expressionNode.isEmpty()) { + return Optional.empty(); + } ExpressionNode expressionNode1 = expressionNode.get(); if (expressionNode1.kind() == SyntaxKind.STRING_LITERAL) { String text1 = ((BasicLiteralNode) expressionNode1).literalToken().text(); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java index 4e3f8431..773f148f 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/Binding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java index 95deea23..c5d97b15 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import org.ballerinax.azurefunctions.Constants; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java index d943844c..3db5ea90 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/InputBindingBuilder.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import io.ballerina.compiler.syntax.tree.AnnotationNode; @@ -5,6 +22,7 @@ import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.service.blob.BlobInputBinding; import org.ballerinax.azurefunctions.service.cosmosdb.CosmosDBInputBinding; @@ -15,21 +33,21 @@ * * @since 2.0.0 */ + public class InputBindingBuilder { public Optional getInputBinding(NodeList annotations, String varName) { for (AnnotationNode annotation : annotations) { Node annotRef = annotation.annotReference(); - if (annotRef.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { + if (SyntaxKind.QUALIFIED_NAME_REFERENCE == annotRef.kind()) { QualifiedNameReferenceNode annotationRef = (QualifiedNameReferenceNode) annotRef; String annotationText = annotationRef.identifier().text(); - if (annotationText.equals("CosmosDBInput")) { + if (Constants.COSMOS_INPUT_BINDING.equals(annotationText)) { return Optional.of(new CosmosDBInputBinding(annotation, varName)); } - if (annotationText.equals("BlobInput")) { + if (Constants.BLOB_INPUT_BINDING.equals(annotationText)) { return Optional.of(new BlobInputBinding(annotation, varName)); } - //TODO Add other stuff } } return Optional.empty(); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java index 3ed23770..6748eb3d 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import org.ballerinax.azurefunctions.Constants; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java index e5392ad0..38c69de7 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/OutputBindingBuilder.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import io.ballerina.compiler.syntax.tree.AnnotationNode; @@ -5,6 +22,7 @@ import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.QualifiedNameReferenceNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.service.blob.BlobOutputBinding; import org.ballerinax.azurefunctions.service.cosmosdb.CosmosDBOutputBinding; import org.ballerinax.azurefunctions.service.http.HTTPOutputBinding; @@ -23,21 +41,21 @@ public class OutputBindingBuilder { public Optional getOutputBinding(NodeList nodes) { for (AnnotationNode annotationNode : nodes) { Node node = annotationNode.annotReference(); - if (node.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { + if (SyntaxKind.QUALIFIED_NAME_REFERENCE != node.kind()) { continue; } QualifiedNameReferenceNode qualifiedNameReferenceNode = (QualifiedNameReferenceNode) node; String annotationName = qualifiedNameReferenceNode.identifier().text(); switch (annotationName) { - case "QueueOutput": + case Constants.QUEUE_OUTPUT_BINDING: return Optional.of(new QueueOutputBinding(annotationNode)); - case "HttpOutput": + case Constants.HTTP_OUTPUT_BINDING: return Optional.of(new HTTPOutputBinding(annotationNode)); - case "CosmosDBOutput": + case Constants.COSMOS_OUTPUT_BINDING: return Optional.of(new CosmosDBOutputBinding(annotationNode)); - case "TwilioSmsOutput": + case Constants.TWILIO_OUTPUT_BINDING: return Optional.of(new TwilioSmsOutputBinding(annotationNode)); - case "BlobOutput": + case Constants.BLOB_OUTPUT_BINDING: return Optional.of(new BlobOutputBinding(annotationNode)); default: throw new RuntimeException("Unexpected property in the annotation"); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java index fcad222c..24e0dd05 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/RemoteTriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import io.ballerina.compiler.api.SemanticModel; @@ -51,7 +68,7 @@ public List getBindings() { NodeList members = this.serviceDeclarationNode.members(); for (Node node : members) { List bindings = new ArrayList<>(); - if (node.kind() != SyntaxKind.OBJECT_METHOD_DEFINITION) { + if (SyntaxKind.OBJECT_METHOD_DEFINITION != node.kind()) { continue; } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; @@ -61,7 +78,7 @@ public List getBindings() { } for (ParameterNode parameterNode : functionDefinitionNode.functionSignature().parameters()) { - if (parameterNode.kind() != SyntaxKind.REQUIRED_PARAM) { + if (SyntaxKind.REQUIRED_PARAM != parameterNode.kind()) { continue; } RequiredParameterNode reqParam = (RequiredParameterNode) parameterNode; @@ -76,28 +93,15 @@ public List getBindings() { InputBindingBuilder inputBuilder = new InputBindingBuilder(); Optional inputBinding = inputBuilder.getInputBinding(reqParam.annotations(), variableName); - if (inputBinding.isPresent()) { - bindings.add(inputBinding.get()); - continue; - } - + inputBinding.ifPresent(bindings::add); } bindings.add(this); -// ParameterNode parameterNode = functionDefinitionNode.functionSignature().parameters().get(0); -// //TODO valid -// if (parameterNode.kind() != SyntaxKind.REQUIRED_PARAM) { -// continue; -// } -// RequiredParameterNode reqParam = (RequiredParameterNode) parameterNode; -// String paramName = reqParam.paramName().orElseThrow().text(); -// bindings.add(new QueueTriggerBinding(paramName, queueName)); - ReturnTypeDescriptorNode returnTypeDescriptorNode = functionDefinitionNode.functionSignature().returnTypeDesc().get(); //TODO recheck if return is must OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); Optional returnBinding = outputBuilder.getOutputBinding(returnTypeDescriptorNode.annotations()); bindings.add(returnBinding.orElseThrow()); //TODO handle in code analyzer - functionContexts.add(new FunctionContext(servicePath.replace("/", ""), bindings)); //TODO remove / + functionContexts.add(new FunctionContext(servicePath.replace("/", ""), bindings)); } return functionContexts; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java index 0dd19867..608c5dc4 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/ServiceHandler.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import io.ballerina.compiler.api.SemanticModel; @@ -8,6 +25,7 @@ import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.service.blob.BlobTriggerBinding; import org.ballerinax.azurefunctions.service.cosmosdb.CosmosDBTriggerBinding; import org.ballerinax.azurefunctions.service.http.HTTPTriggerBinding; @@ -31,7 +49,7 @@ public static TriggerBinding getBuilder(ServiceDeclarationNode svcDeclarationNod continue; } TypeReferenceTypeSymbol typeSymbol1; - if (typeSymbol.get().typeKind() == TypeDescKind.UNION) { + if (TypeDescKind.UNION == typeSymbol.get().typeKind()) { UnionTypeSymbol union = (UnionTypeSymbol) typeSymbol.get(); typeSymbol1 = (TypeReferenceTypeSymbol) union.memberTypeDescriptors().get(0); @@ -45,139 +63,20 @@ public static TriggerBinding getBuilder(ServiceDeclarationNode svcDeclarationNod String serviceTypeName = name.get(); switch (serviceTypeName) { - case "HttpListener": + case Constants.AZURE_HTTP_LISTENER: return new HTTPTriggerBinding(svcDeclarationNode, semanticModel); - case "QueueListener": + case Constants.AZURE_QUEUE_LISTENER: return new QueueTriggerBinding(svcDeclarationNode, semanticModel); - case "CosmosDBListener": + case Constants.AZURE_COSMOS_LISTENER: return new CosmosDBTriggerBinding(svcDeclarationNode, semanticModel); - case "TimerListener": + case Constants.AZURE_TIMER_LISTENER: return new TimerTriggerBinding(svcDeclarationNode, semanticModel); - case "BlobListener": + case Constants.AZURE_BLOB_LISTENER: return new BlobTriggerBinding(svcDeclarationNode, semanticModel); default: - //TODO Change throw new RuntimeException("Unsupported Listener type"); } } - //TODO Change throw new RuntimeException("Unsupported Listener type"); } - -// protected boolean isPayloadAnnotationExist(NodeList nodes) { -// for (AnnotationNode annotation : nodes) { -// Node annotRef = annotation.annotReference(); -// if (annotRef.kind() == SyntaxKind.QUALIFIED_NAME_REFERENCE) { -// QualifiedNameReferenceNode annotationRef = (QualifiedNameReferenceNode) annotRef; -// if (annotationRef.identifier().text().equals("Payload")) { //Add other stuff -// return true; -// } -// } -// } -// return false; -// } - -// public Optional getReturnBinding(ReturnTypeDescriptorNode returnTypeDescriptorNode) { -// NodeList annotations = returnTypeDescriptorNode.annotations(); -// for (AnnotationNode annotationNode : annotations) { -// Node node = annotationNode.annotReference(); -// if (node.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { -// continue; -// } -// QualifiedNameReferenceNode qualifiedNameReferenceNode = (QualifiedNameReferenceNode) node; -// String annotationName = qualifiedNameReferenceNode.identifier().text(); -// switch (annotationName) { -// case "QueueOutput": -// MappingFieldNode mappingFieldNode = annotationNode.annotValue().orElseThrow().fields().get(0); -// if (mappingFieldNode.kind() == SyntaxKind.SPECIFIC_FIELD) { -// SpecificFieldNode specificFieldNode = (SpecificFieldNode) mappingFieldNode; -// Optional value = Util.extractValueFromAnnotationField(specificFieldNode); -// if (value.isPresent()) { -// String text = ((IdentifierToken) specificFieldNode.fieldName()).text(); -// if (text.equals("queueName")) { -// return Optional.of(new QueueOutputBinding(value.get())); -// } -// } -// } -// break; -// case "HttpOutput": -// return Optional.of(new HTTPOutputBinding()); -// case "CosmosDBOutput": -// CosmosDBOutputBinding cosmosDBOutputBinding = new CosmosDBOutputBinding(); -// SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); -// for (MappingFieldNode mappingField : fields) { -// if (mappingField.kind() == SyntaxKind.SPECIFIC_FIELD) { -// SpecificFieldNode specificFieldNode = (SpecificFieldNode) mappingField; -// Optional value = Util.extractValueFromAnnotationField(specificFieldNode); -// if (value.isPresent()) { -// String text = ((IdentifierToken) specificFieldNode.fieldName()).text(); -// switch (text) { -// case "connectionStringSetting": -// cosmosDBOutputBinding.setConnectionStringSetting(value.get()); -// break; -// case "databaseName": -// cosmosDBOutputBinding.setDatabaseName(value.get()); -// break; -// case "collectionName": -// cosmosDBOutputBinding.setCollectionName(value.get()); -// break; -// default: -// throw new RuntimeException("Unexpected property in the annotation"); -// } -// } -// } -// } -// return Optional.of(cosmosDBOutputBinding); -// } -// } -// return Optional.empty(); -// } - -// public Optional getListenerAnnotation(ServiceDeclarationNode svcDeclNode, String annotationName) { -// //TODO handle inline decl -// for (ExpressionNode expression : svcDeclNode.expressions()) { -// Optional symbol = this.semanticModel.symbol(expression); -// if (symbol.isEmpty()) { -// continue; -// } -// Symbol listenerSymbol = symbol.get(); -// if (listenerSymbol.kind() != SymbolKind.VARIABLE) { -// continue; -// } -// VariableSymbol variableSymbol = (VariableSymbol) listenerSymbol; -// ListenerDeclarationNode listenerDeclarationNode = (ListenerDeclarationNode) findNode(variableSymbol); -// Optional metadata = listenerDeclarationNode.metadata(); -// if (metadata.isEmpty()) { -// continue; -// } -// NodeList annotations = metadata.get().annotations(); -// for (AnnotationNode annotationNode : annotations) { -// Optional typeSymbol = this.semanticModel.symbol(annotationNode); -// if (typeSymbol.isEmpty()) { -// continue; -// } -// Symbol annotationType = typeSymbol.get(); -// Optional name = annotationType.getName(); -// if (name.isEmpty()) { -// continue; -// } -// if (name.get().equals(annotationName)) { -// return Optional.of(annotationNode); -// } -// } -// -//// List annotations = variableSymbol.annotations(); -//// for (AnnotationSymbol annotationSymbol : annotations) { -//// Optional name = annotationSymbol.getName(); -//// if (name.isEmpty()) { -//// continue; -//// } -//// if (name.get().equals(annotationName)) { -//// return Optional.of(annotationSymbol); -//// } -//// } -// } -// -// return Optional.empty(); -// } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java index cb79cc8f..0294458b 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/TriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service; import io.ballerina.compiler.api.SemanticModel; @@ -36,10 +53,9 @@ public TriggerBinding(String triggerType) { public abstract List getBindings(); public Optional getListenerAnnotation(ServiceDeclarationNode svcDeclNode, String annotationName) { - //TODO handle inline decl for (ExpressionNode expression : svcDeclNode.expressions()) { Optional metadata; - if (expression.kind() == EXPLICIT_NEW_EXPRESSION) { + if (EXPLICIT_NEW_EXPRESSION == expression.kind()) { metadata = svcDeclNode.metadata(); } else { Optional symbol = this.semanticModel.symbol(expression); @@ -47,13 +63,13 @@ public Optional getListenerAnnotation(ServiceDeclarationNode svc continue; } Symbol listenerSymbol = symbol.get(); - if (listenerSymbol.kind() != SymbolKind.VARIABLE) { + if (SymbolKind.VARIABLE != listenerSymbol.kind()) { continue; } VariableSymbol variableSymbol = (VariableSymbol) listenerSymbol; ListenerDeclarationNode listenerDeclarationNode = (ListenerDeclarationNode) Util.findNode(svcDeclNode, variableSymbol); - metadata = listenerDeclarationNode.metadata(); + metadata = listenerDeclarationNode != null ? listenerDeclarationNode.metadata() : Optional.empty(); } if (metadata.isEmpty()) { continue; @@ -73,17 +89,6 @@ public Optional getListenerAnnotation(ServiceDeclarationNode svc return Optional.of(annotationNode); } } - -// List annotations = variableSymbol.annotations(); -// for (AnnotationSymbol annotationSymbol : annotations) { -// Optional name = annotationSymbol.getName(); -// if (name.isEmpty()) { -// continue; -// } -// if (name.get().equals(annotationName)) { -// return Optional.of(annotationSymbol); -// } -// } } return Optional.empty(); } @@ -104,7 +109,7 @@ protected boolean isAzureFunctionsAnnotationExist(NodeList nodes continue; } - if (name.get().equals(Constants.AZURE_FUNCTIONS_MODULE_NAME)) { + if (Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(name.get())) { return true; } } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java index 4781b616..9c85abaf 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobInputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.blob; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java index 223b726e..689830f2 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobOutputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.blob; import com.google.gson.JsonObject; @@ -6,6 +23,7 @@ import io.ballerina.compiler.syntax.tree.MappingFieldNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.Util; import org.ballerinax.azurefunctions.service.OutputBinding; @@ -24,7 +42,7 @@ public class BlobOutputBinding extends OutputBinding { public BlobOutputBinding(AnnotationNode annotationNode) { super("blob"); - this.setVarName("outMsg"); + this.setVarName(Constants.RETURN_VAR_NAME); SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); for (MappingFieldNode fieldNode : fields) { extractValueFromAnnotation((SpecificFieldNode) fieldNode); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java index d7db3e2a..3fb32459 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/blob/BlobTriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.blob; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java index e192eec8..d6b20b60 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBInputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.cosmosdb; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java index 4f3c9631..3db1f098 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBOutputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.cosmosdb; import com.google.gson.JsonObject; @@ -6,6 +23,7 @@ import io.ballerina.compiler.syntax.tree.MappingFieldNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.Util; import org.ballerinax.azurefunctions.service.OutputBinding; @@ -24,7 +42,7 @@ public class CosmosDBOutputBinding extends OutputBinding { public CosmosDBOutputBinding(AnnotationNode annotationNode) { super("cosmosDB"); - this.setVarName("outMsg"); + this.setVarName(Constants.RETURN_VAR_NAME); SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); for (MappingFieldNode fieldNode : fields) { extractValueFromAnnotation((SpecificFieldNode) fieldNode); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java index 2e59273a..15bb983f 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/cosmosdb/CosmosDBTriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.cosmosdb; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java index 113debbe..74084ed0 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPOutputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.http; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index 0a852d28..9b326bc1 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.http; import com.google.gson.JsonArray; @@ -61,13 +78,11 @@ public List getBindings() { List functionContexts = new ArrayList<>(); NodeList members = this.serviceDeclarationNode.members(); for (Node node : members) { -// HTTPTriggerBinding httpTriggerBinding = -// httpTriggerAnnot.map(HTTPTriggerBinding::new).orElseGet(HTTPTriggerBinding::new); HTTPTriggerBinding httpTriggerBinding = new HTTPTriggerBinding(this.serviceDeclarationNode, this.semanticModel); httpTriggerAnnot.ifPresent(queueTrigger -> getAnnotation(httpTriggerBinding, queueTrigger)); List bindings = new ArrayList<>(); - if (node.kind() != SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { + if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION != node.kind()) { continue; } FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) node; @@ -85,16 +100,10 @@ public List getBindings() { ResourcePathParameterNode pathParamNode = (ResourcePathParameterNode) pathBlock; //TODO Handle optional resourcePath.append("/" + "{").append(pathParamNode.paramName().get().text()).append("}"); - continue; } //TODO add wildcard } - String resPath = resourcePath.toString(); - if (resPath.startsWith("/")) { - httpTriggerBinding.setPath(resPath.substring(1)); - } else { - httpTriggerBinding.setPath(resPath); - } + httpTriggerBinding.setPath(getFunctionPath(resourcePath.toString())); bindings.add(httpTriggerBinding); String variableName; SeparatedNodeList parameters = functionDefinitionNode.functionSignature().parameters(); @@ -109,18 +118,14 @@ public List getBindings() { variableName = reqParam.paramName().get().text(); InputBindingBuilder inputBuilder = new InputBindingBuilder(); Optional inputBinding = inputBuilder.getInputBinding(reqParam.annotations(), variableName); - if (inputBinding.isPresent()) { - bindings.add(inputBinding.get()); - continue; - } + inputBinding.ifPresent(bindings::add); } Optional returnTypeDescriptor = functionDefinitionNode.functionSignature().returnTypeDesc(); if (returnTypeDescriptor.isEmpty()) { bindings.add(new HTTPOutputBinding(null)); } else { - ReturnTypeDescriptorNode returnTypeNode = - returnTypeDescriptor.get(); + ReturnTypeDescriptorNode returnTypeNode = returnTypeDescriptor.get(); OutputBindingBuilder outputBuilder = new OutputBindingBuilder(); Optional returnBinding = outputBuilder.getOutputBinding(returnTypeNode.annotations()); if (returnBinding.isEmpty()) { @@ -135,6 +140,14 @@ public List getBindings() { return functionContexts; } + private String getFunctionPath(String resourcePath) { + if (resourcePath.startsWith("/")) { + return resourcePath.substring(1); + } else { + return resourcePath; + } + } + private void getAnnotation(HTTPTriggerBinding triggerBinding, AnnotationNode queueTrigger) { SeparatedNodeList fields = queueTrigger.annotValue().orElseThrow().fields(); for (MappingFieldNode fieldNode : fields) { @@ -247,7 +260,7 @@ private JsonArray generateMethods() { methods.add("POST"); methods.add("PUT"); } else { - methods.add(this.methods); //TODO add default + methods.add(this.methods); } return methods; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java index 017dcb0a..3c2a7107 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueOutputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.queue; import com.google.gson.JsonObject; @@ -6,6 +23,7 @@ import io.ballerina.compiler.syntax.tree.MappingFieldNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.Util; import org.ballerinax.azurefunctions.service.OutputBinding; @@ -23,7 +41,7 @@ public class QueueOutputBinding extends OutputBinding { public QueueOutputBinding(AnnotationNode annotationNode) { super("queue"); - this.setVarName("outMsg"); + this.setVarName(Constants.RETURN_VAR_NAME); SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); for (MappingFieldNode fieldNode : fields) { extractValueFromAnnotation((SpecificFieldNode) fieldNode); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java index df6f736c..68340178 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/queue/QueueTriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.queue; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java index 36f64970..bd8955f6 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/timer/TimerTriggerBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.timer; import com.google.gson.JsonObject; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java index be309fa5..5432d73d 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/twilio/TwilioSmsOutputBinding.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 org.ballerinax.azurefunctions.service.twilio; import com.google.gson.JsonObject; @@ -6,6 +23,7 @@ import io.ballerina.compiler.syntax.tree.MappingFieldNode; import io.ballerina.compiler.syntax.tree.SeparatedNodeList; import io.ballerina.compiler.syntax.tree.SpecificFieldNode; +import org.ballerinax.azurefunctions.Constants; import org.ballerinax.azurefunctions.Util; import org.ballerinax.azurefunctions.service.OutputBinding; @@ -25,7 +43,7 @@ public class TwilioSmsOutputBinding extends OutputBinding { public TwilioSmsOutputBinding(AnnotationNode annotationNode) { super("twilioSms"); - this.setVarName("outMsg"); + this.setVarName(Constants.RETURN_VAR_NAME); SeparatedNodeList fields = annotationNode.annotValue().orElseThrow().fields(); for (MappingFieldNode fieldNode : fields) { extractValueFromAnnotation((SpecificFieldNode) fieldNode); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java index fd8b3bfe..3add33dc 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/AzureFunctionsCodeAnalyzerTask.java @@ -29,6 +29,7 @@ import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; import io.ballerina.tools.diagnostics.Diagnostic; import io.ballerina.tools.diagnostics.DiagnosticSeverity; +import org.ballerinax.azurefunctions.Constants; import java.util.List; import java.util.Optional; @@ -70,21 +71,18 @@ public void perform(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext) { private boolean isHTTPListener(TypeSymbol listenerType) { - if (listenerType.nameEquals("HttpListener")) { - return true; - } - return false; + return listenerType.nameEquals(Constants.AZURE_HTTP_LISTENER); } private boolean isListenerBelongsToAzureFuncModule(TypeSymbol listenerType) { - if (listenerType.typeKind() == TypeDescKind.UNION) { + if (TypeDescKind.UNION == listenerType.typeKind()) { return ((UnionTypeSymbol) listenerType).memberTypeDescriptors().stream() .filter(typeDescriptor -> typeDescriptor instanceof TypeReferenceTypeSymbol) .map(typeReferenceTypeSymbol -> (TypeReferenceTypeSymbol) typeReferenceTypeSymbol) .anyMatch(typeReferenceTypeSymbol -> isAzureFuncModule(typeReferenceTypeSymbol.getModule().get())); } - if (listenerType.typeKind() == TypeDescKind.TYPE_REFERENCE) { + if (TypeDescKind.TYPE_REFERENCE == listenerType.typeKind()) { return isAzureFuncModule(((TypeReferenceTypeSymbol) listenerType).typeDescriptor().getModule().get()); } return false; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java index 55f763a2..f86ae0b4 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java @@ -47,12 +47,11 @@ import java.util.Optional; import java.util.stream.Collectors; -import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS; +import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS_MODULE_NAME; import static org.ballerinax.azurefunctions.Constants.HEADER_ANNOTATION_TYPE; import static org.ballerinax.azurefunctions.Constants.HTTP; import static org.ballerinax.azurefunctions.Util.updateDiagnostic; - /** * Validates azure-function service on a HTTPListener . */ @@ -69,6 +68,7 @@ static void validate(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, } } + private static void validateResourceFunction(SyntaxNodeAnalysisContext ctx, FunctionDefinitionNode member) { validateInputParameters(ctx, member); //TODO : Other necessary validation for a resource function @@ -134,7 +134,8 @@ private static void validateAnnotatedInputParam(SyntaxNodeAnalysisContext ctx, L reportInvalidParameter(ctx, paramLocation, paramName); continue; } - if (!HTTP.equals(nameSymbolOptional.get()) && !AZURE_FUNCTIONS.equals(nameSymbolOptional.get())) { + if (!HTTP.equals(nameSymbolOptional.get()) && + !AZURE_FUNCTIONS_MODULE_NAME.equals(nameSymbolOptional.get())) { reportInvalidParameterAnnotation(ctx, paramLocation, paramName); continue; } @@ -229,9 +230,8 @@ private static void checkAllowedHeaderParamTypes(SyntaxNodeAnalysisContext ctx, } } - private static void checkAllowedHeaderParamUnionType(SyntaxNodeAnalysisContext ctx, Location paramLocation, - Symbol param, String paramName, TypeDescKind elementKind) { + Symbol param, String paramName, TypeDescKind elementKind) { if (!isAllowedHeaderParamPureType(elementKind)) { reportInvalidUnionHeaderType(ctx, paramLocation, paramName); } @@ -291,8 +291,6 @@ private static void reportInvalidParameter(SyntaxNodeAnalysisContext ctx, Locati updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_002, paramName); } - - private static void reportInvalidHeaderParameterType(SyntaxNodeAnalysisContext ctx, Location location, String paramName, Symbol parameterSymbol) { updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_003, List.of(new BSymbolicProperty(parameterSymbol)) diff --git a/native/spotbugs-exclude.xml b/native/spotbugs-exclude.xml index 5f29de4b..960de62e 100644 --- a/native/spotbugs-exclude.xml +++ b/native/spotbugs-exclude.xml @@ -24,4 +24,8 @@ + + + + diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java index a2d2aff3..ff2ede11 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Constants.java @@ -28,18 +28,31 @@ public interface Constants { String PACKAGE_ORG = "ballerinax"; String PACKAGE_NAME = "azure_functions"; + //TODO restrict "httpPayload" from the param names. + String HTTP_TRIGGER_IDENTIFIER = "httpPayload"; + + String PARAMETER_ANNOTATION = "$param$."; + String RETURN_ANNOTATION = "$returns$"; + String PACKAGE_COMPLETE = PACKAGE_ORG + "/" + PACKAGE_NAME + ":3"; String FUNCTION_ANNOTATION_COMPLETE = PACKAGE_COMPLETE + ":Function"; + String FUNCTION_ANNOTATION_NAME_FIELD = "name"; + String HTTP_PACKAGE_ORG = "ballerina"; String HTTP_PACKAGE_NAME = "http"; + String HTTP_ANNOTATION_PREFIX = Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME + ":" + + Constants.HTTP_PACKAGE_VERSION + ":"; + String SERVICE_OBJECT = "AZURE_FUNCTION_SERVICE_OBJECT"; String QUEUE_OUTPUT = "QueueOutput"; String COSMOS_DBOUTPUT = "CosmosDBOutput"; - String OUT_MSG = "outMsg"; String HTTP_OUTPUT = "HttpOutput"; String BLOB_OUTPUT = "BlobOutput"; + + String OUT_MSG = "outMsg"; + String PAYLOAD_ANNOTATAION = "Payload"; String HEADER_ANNOTATION = "Header"; String SERVICE_CONF_ANNOTATION = "ServiceConfig"; @@ -50,7 +63,8 @@ public interface Constants { String HEADERS = "headers"; String CONTENT_TYPE = "Content-Type"; String MEDIA_TYPE = "mediaType"; - String RESP = "resp"; + String RESPONSE_FIELD = "resp"; + String POST = "post"; String CREATED_201 = "201"; String GET = "get"; @@ -60,11 +74,15 @@ public interface Constants { String HEAD = "head"; String OPTIONS = "options"; String DEFAULT = "default"; + String OK_200 = "200"; + String ACCEPTED = "202"; + String TEXT_PLAIN = "text/plain"; String APPLICATION_XML = "application/xml"; String APPLICATION_OCTET_STREAM = "application/octet-stream"; String APPLICATION_JSON = "application/json"; + String BYTE_TYPE = "byte"; String MAP_TYPE = "map"; String JSON_TYPE = "json"; @@ -75,4 +93,11 @@ public interface Constants { String INVALID_PAYLOAD_ERROR = "InvalidPayloadError"; String HEADER_NOT_FOUND_ERROR = "HeaderNotFoundError"; String HTTP_PACKAGE_VERSION = "2"; + + String PATH_PARAM = "*"; + + String AZURE_PAYLOAD_PARAMS = "Params"; + String AZURE_PAYLOAD_HEADERS = "Headers"; + String AZURE_QUERY_HEADERS = "Query"; + String AZURE_BODY_HEADERS = "Body"; } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 50794a62..37e22a72 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -52,7 +52,6 @@ */ public class FunctionCallback implements Callback { - private final Future future; private final Module module; private final List annotations; @@ -63,7 +62,8 @@ public FunctionCallback(Future future, Module module, MethodType methodType) { this.module = module; this.methodType = methodType; this.annotations = new ArrayList<>(); - BMap annotations = (BMap) methodType.getAnnotation(StringUtils.fromString("$returns$")); + BMap annotations = + (BMap) methodType.getAnnotation(StringUtils.fromString(Constants.RETURN_ANNOTATION)); if (annotations != null) { for (BString annotation : annotations.getKeys()) { String[] split = annotation.getValue().split(":"); @@ -82,7 +82,6 @@ private String getOutputAnnotation() { return this.annotations.get(0); } - @Override public void notifySuccess(Object result) { if (result instanceof BError) { @@ -97,11 +96,11 @@ public void notifySuccess(Object result) { BMap mapValue = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); if (result == null) { - BString statusCode = StringUtils.fromString("202"); + BString statusCode = StringUtils.fromString(Constants.ACCEPTED); BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); - mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + mapValue.put(StringUtils.fromString(Constants.RESPONSE_FIELD), respMap); future.complete(mapValue); return; } @@ -115,12 +114,12 @@ public void notifySuccess(Object result) { if (result instanceof BArray) { BArray arrayValue = (BArray) result; BString encodedString = ToBase64.toBase64(arrayValue); - mapValue.put(StringUtils.fromString("outMsg"), encodedString); + mapValue.put(StringUtils.fromString(Constants.OUT_MSG), encodedString); } } else if (outputBinding == null || Constants.HTTP_OUTPUT.equals(outputBinding)) { if (isHTTPStatusCodeResponse(result)) { - handleStatusCodeResponse((BMap) result, mapValue); + handleStatusCodeResponse((BMap) result, mapValue); } else { handleNonStatusCodeResponse(result, mapValue); } @@ -128,7 +127,6 @@ public void notifySuccess(Object result) { future.complete(mapValue); } - @Override public void notifyFailure(BError bError) { bError.printStackTrace(); @@ -148,28 +146,28 @@ private boolean isModuleDefinedError(BError error) { private boolean isHTTPStatusCodeResponse(Object result) { // Module resultPkg = TypeUtils.getType(result).getPackage(); - return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))); + return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))); //TODO : Check inheritance //(https://github.com/ballerina-platform/module-ballerinax-azure.functions/issues/490) } - private boolean isContentTypeExist(BMap headersMap) { + private boolean isContentTypeExist(BMap headersMap) { for (BString headerKey : headersMap.getKeys()) { if (headerKey.getValue().toLowerCase(Locale.ROOT).equals(Constants.CONTENT_TYPE.toLowerCase(Locale.ROOT))) { - return true; + return true; } } return false; } - private void addStatusCodeImplicitly(BMap respMap) { + private void addStatusCodeImplicitly(BMap respMap) { String accessor = ((ResourceMethodType) this.methodType).getAccessor(); Object statusCode = ""; if (Constants.POST.equals(accessor)) { statusCode = Constants.CREATED_201; - } else if (Constants.GET.equals(accessor) || Constants.PUT.equals(accessor) || - Constants.PATCH.equals(accessor) || Constants.DELETE.equals(accessor) || - Constants.HEAD.equals(accessor) || Constants.OPTIONS.equals(accessor) || + } else if (Constants.GET.equals(accessor) || Constants.PUT.equals(accessor) || + Constants.PATCH.equals(accessor) || Constants.DELETE.equals(accessor) || + Constants.HEAD.equals(accessor) || Constants.OPTIONS.equals(accessor) || Constants.DEFAULT.equals(accessor)) { statusCode = Constants.OK_200; } @@ -177,7 +175,7 @@ private void addStatusCodeImplicitly(BMap respMap) { respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); } - private void addContentTypeImplicitly(Object result, BMap headers) { + private void addContentTypeImplicitly(Object result, BMap headers) { if (result instanceof BString) { headers.put(StringUtils.fromString(Constants.CONTENT_TYPE), StringUtils.fromString(Constants.TEXT_PLAIN)); @@ -231,34 +229,31 @@ private void addContentTypeImplicitly(Object result, BMap headers) { } } - private void handleNonStatusCodeResponse(Object result, BMap mapValue) { BMap respMap = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - Object headers = + BMap headers = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); addStatusCodeImplicitly(respMap); - addContentTypeImplicitly(result, (BMap) headers); + addContentTypeImplicitly(result, headers); respMap.put(StringUtils.fromString(Constants.HEADERS), headers); if (result instanceof BArray) { BArray arrayResult = (BArray) result; if (Constants.BYTE_TYPE.equals(arrayResult.getElementType().getName())) { respMap.put(StringUtils.fromString(Constants.BODY), ToBase64.toBase64(arrayResult)); -// respMap.put(StringUtils.fromString(Constants.BODY), arrayResult); -// respMap.put(StringUtils.fromString("isRaw"), true); } else { respMap.put(StringUtils.fromString(Constants.BODY), result); } } else { respMap.put(StringUtils.fromString(Constants.BODY), result); } - mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + mapValue.put(StringUtils.fromString(Constants.RESPONSE_FIELD), respMap); } - private void handleStatusCodeResponse(BMap result, BMap mapValue) { - BMap resultMap = result; + private void handleStatusCodeResponse(BMap result, BMap mapValue) { + BMap resultMap = result; // Extract status code BObject status = (BObject) (resultMap.get(StringUtils.fromString(Constants.STATUS))); @@ -304,7 +299,7 @@ private void handleStatusCodeResponse(BMap result, BMap mapValu ((BMap) headers).put(StringUtils.fromString(Constants.CONTENT_TYPE), mediaType); } } - mapValue.put(StringUtils.fromString(Constants.RESP), respMap); + mapValue.put(StringUtils.fromString(Constants.RESPONSE_FIELD), respMap); } } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index d1e59131..a92fc88f 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -17,18 +17,14 @@ */ package io.ballerina.stdlib.azure.functions; -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.Field; import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; -import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -39,8 +35,6 @@ import io.ballerina.stdlib.azure.functions.exceptions.HeaderNotFoundException; import io.ballerina.stdlib.azure.functions.exceptions.InvalidPayloadException; import io.ballerina.stdlib.azure.functions.exceptions.PayloadNotFoundException; -import org.ballerinalang.langlib.bool.FromString; - import java.util.ArrayList; import java.util.Arrays; @@ -50,11 +44,6 @@ import java.util.Map; import java.util.Optional; -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.FLOAT_TAG; -import static io.ballerina.runtime.api.TypeTags.INT_TAG; - /** * Represents an Azure Resource function property. * @@ -62,23 +51,18 @@ */ public class HttpResource { - private static final ArrayType INT_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_INT); - private static final ArrayType FLOAT_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_FLOAT); - private static final ArrayType BOOLEAN_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_BOOLEAN); - private static final ArrayType DECIMAL_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_DECIMAL); - private PathParameter[] pathParams; private QueryParameter[] queryParameter; private PayloadParameter payloadParameter; private InputBindingParameter[] inputBindingParameters; private HeaderParameter headerParameter; - public HttpResource(ResourceMethodType resourceMethodType, BMap body, BMap serviceAnnotations) { + public HttpResource(ResourceMethodType resourceMethodType, BMap body, BMap serviceAnnotations) { this.pathParams = getPathParams(resourceMethodType, body); this.payloadParameter = processPayloadParam(resourceMethodType, body).orElse(null); this.queryParameter = getQueryParams(resourceMethodType, body); this.inputBindingParameters = getInputBindingParams(resourceMethodType, body); - this.headerParameter = processHeaderParam(resourceMethodType, body, serviceAnnotations).orElse(null);; + this.headerParameter = processHeaderParam(resourceMethodType, body, serviceAnnotations).orElse(null); } private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourceMethod, BMap body) @@ -88,7 +72,8 @@ private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourc for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { Parameter parameter = parameters[i]; String name = parameter.name; - Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + Object annotation = + resourceMethod.getAnnotation(StringUtils.fromString(Constants.PARAMETER_ANNOTATION + name)); Optional inputBindingHandler = ParamHandler.getInputBindingHandler(annotation); if (inputBindingHandler.isEmpty()) { continue; @@ -109,21 +94,21 @@ private InputBindingParameter[] getInputBindingParams(ResourceMethodType resourc } private QueryParameter[] getQueryParams(ResourceMethodType resourceMethod, BMap body) { - BMap queryParams = body.getMapValue(StringUtils.fromString("httpPayload")) - .getMapValue(StringUtils.fromString("Query")); + BMap queryParams = body.getMapValue(StringUtils.fromString(Constants.HTTP_TRIGGER_IDENTIFIER)) + .getMapValue(StringUtils.fromString(Constants.AZURE_QUERY_HEADERS)); Parameter[] parameters = resourceMethod.getParameters(); List queryParameters = new ArrayList<>(); for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { Parameter parameter = parameters[i]; String name = parameter.name; - Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); - //TODO Add other annotations as well - if (isAzAnnotationExist(annotation)) { + Object annotation = + resourceMethod.getAnnotation(StringUtils.fromString(Constants.PARAMETER_ANNOTATION + name)); + if (Utils.isAzAnnotationExist(annotation)) { continue; } BString queryValue = queryParams.getStringValue(StringUtils.fromString(name)); try { - Object bValue = createValue(parameter.type, queryValue); + Object bValue = Utils.createValue(parameter.type, queryValue); queryParameters.add(new QueryParameter(i, parameter, bValue)); } catch (BError bError) { throw new InvalidPayloadException(bError.getMessage()); @@ -132,97 +117,16 @@ private QueryParameter[] getQueryParams(ResourceMethodType resourceMethod, BMap< return queryParameters.toArray(QueryParameter[]::new); } - private Object createValue(Type type, BString strValue) { - switch (type.getTag()) { - case TypeTags.STRING_TAG: - return strValue; - case TypeTags.BOOLEAN_TAG: - return FromString.fromString(strValue); - case TypeTags.INT_TAG: - return org.ballerinalang.langlib.integer.FromString.fromString(strValue); - case TypeTags.FLOAT_TAG: - return org.ballerinalang.langlib.floatingpoint.FromString.fromString(strValue); - case TypeTags.DECIMAL_TAG: - return org.ballerinalang.langlib.decimal.FromString.fromString(strValue); - case TypeTags.UNION_TAG: - List memberTypes = ((UnionType) type).getMemberTypes(); - for (Type memberType : memberTypes) { - try { - return createValue(memberType, strValue); - } catch (BError ignored) { - // thrown errors are ignored until all the types are iterated - } - } - return null; - case TypeTags.ARRAY_TAG: - ArrayType arrayType = (ArrayType) type; - Type elementType = arrayType.getElementType(); - if (strValue == null) { - return null; - } - String[] values = strValue.getValue().split(","); - return castParamArray(elementType.getTag(), values); - default: - throw new InvalidPayloadException("unsupported parameter type " + type.getName()); - } - } - - public static BArray castParamArray(int targetElementTypeTag, String[] argValueArr) { - switch (targetElementTypeTag) { - case INT_TAG: - return getBArray(argValueArr, INT_ARR, targetElementTypeTag); - case FLOAT_TAG: - return getBArray(argValueArr, FLOAT_ARR, targetElementTypeTag); - case BOOLEAN_TAG: - return getBArray(argValueArr, BOOLEAN_ARR, targetElementTypeTag); - case DECIMAL_TAG: - return getBArray(argValueArr, DECIMAL_ARR, targetElementTypeTag); - default: - return StringUtils.fromStringArray(argValueArr); - } - } - - private static BArray getBArray(String[] valueArray, ArrayType arrayType, int elementTypeTag) { - BArray arrayValue = ValueCreator.createArrayValue(arrayType); - int index = 0; - for (String element : valueArray) { - switch (elementTypeTag) { - case INT_TAG: - arrayValue.add(index++, Long.parseLong(element)); - break; - case FLOAT_TAG: - arrayValue.add(index++, Double.parseDouble(element)); - break; - case BOOLEAN_TAG: - arrayValue.add(index++, Boolean.parseBoolean(element)); - break; - case DECIMAL_TAG: - arrayValue.add(index++, ValueCreator.createDecimalValue(element)); - break; - default: - throw new InvalidPayloadException("Illegal state error: unexpected param type"); - } - } - return arrayValue; - } - - private boolean isAzAnnotationExist(Object annotation) { - if (annotation == null) { - return false; - } - return true; - } - private PathParameter[] getPathParams(ResourceMethodType resourceMethod, BMap body) { String[] resourcePath = resourceMethod.getResourcePath(); Parameter[] parameters = resourceMethod.getParameters(); List pathParams = new ArrayList<>(); int count = 0; for (String path : resourcePath) { - if (path.equals("*")) { + if (path.equals(Constants.PATH_PARAM)) { Parameter parameter = parameters[count]; - BMap payload = body.getMapValue(StringUtils.fromString("httpPayload")); - BMap params = payload.getMapValue(StringUtils.fromString("Params")); + BMap payload = body.getMapValue(StringUtils.fromString(Constants.HTTP_TRIGGER_IDENTIFIER)); + BMap params = payload.getMapValue(StringUtils.fromString(Constants.AZURE_PAYLOAD_PARAMS)); BString param = params.getStringValue(StringUtils.fromString(parameter.name)); pathParams.add(new PathParameter(count, parameter, param.getValue())); count++; @@ -238,16 +142,17 @@ private Optional processPayloadParam(ResourceMethodType resour for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { Parameter parameter = parameters[i]; String name = parameter.name; - Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + Object annotation = + resourceMethod.getAnnotation(StringUtils.fromString(Constants.PARAMETER_ANNOTATION + name)); if (!ParamHandler.isPayloadAnnotationParam(annotation)) { continue; } - BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); - BMap headers = httpPayload.getMapValue(StringUtils.fromString("Headers")); + BMap httpPayload = body.getMapValue(StringUtils.fromString(Constants.HTTP_TRIGGER_IDENTIFIER)); + BMap headers = httpPayload.getMapValue(StringUtils.fromString(Constants.AZURE_PAYLOAD_HEADERS)); Type type = parameter.type; - String contentType = getContentTypeHeader(headers); - BString bodyValue = getRequestBody(httpPayload, name, type); - if (isNilType(type) && bodyValue == null) { + String contentType = Utils.getContentTypeHeader(headers); + BString bodyValue = Utils.getRequestBody(httpPayload, name, type); + if (Utils.isNilType(type) && bodyValue == null) { return Optional.of(new PayloadParameter(i, parameter, null)); } try { @@ -262,13 +167,12 @@ private Optional processPayloadParam(ResourceMethodType resour } private Optional processHeaderParam(ResourceMethodType resourceMethod, BMap body, - BMap serviceAnnotations) { + BMap serviceAnnotations) { Parameter[] parameters = resourceMethod.getParameters(); - Object headerParam = null; + Object headerParam; Boolean treatNilableAsOptional = true; - String serviceConfig = Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME + ":" + - Constants.HTTP_PACKAGE_VERSION + ":" + Constants.SERVICE_CONF_ANNOTATION; - Boolean isServiceConfExist = ParamHandler.isHttpServiceConfExist(serviceAnnotations); + String serviceConfig = Constants.HTTP_ANNOTATION_PREFIX + Constants.SERVICE_CONF_ANNOTATION; + boolean isServiceConfExist = ParamHandler.isHttpServiceConfExist(serviceAnnotations); if (isServiceConfExist) { treatNilableAsOptional = serviceAnnotations.getMapValue(StringUtils.fromString(serviceConfig)). getBooleanValue(StringUtils.fromString("treatNilableAsOptional")); @@ -276,20 +180,22 @@ private Optional processHeaderParam(ResourceMethodType resource for (int i = this.pathParams.length, parametersLength = parameters.length; i < parametersLength; i++) { Parameter parameter = parameters[i]; String name = parameter.name; - Object annotation = resourceMethod.getAnnotation(StringUtils.fromString("$param$." + name)); + Object annotation = + resourceMethod.getAnnotation(StringUtils.fromString(Constants.PARAMETER_ANNOTATION + name)); if (annotation == null) { continue; } - Boolean isHeaderAnnotation = ParamHandler.isHeaderAnnotationParam(annotation); + boolean isHeaderAnnotation = ParamHandler.isHeaderAnnotationParam(annotation); if (!isHeaderAnnotation) { continue; } - BMap httpPayload = body.getMapValue(StringUtils.fromString("httpPayload")); - BMap headers = httpPayload.getMapValue(StringUtils.fromString("Headers")); + BMap httpPayload = body.getMapValue(StringUtils.fromString(Constants.HTTP_TRIGGER_IDENTIFIER)); + BMap headers = + (BMap) httpPayload.getMapValue(StringUtils.fromString(Constants.AZURE_PAYLOAD_HEADERS)); - String headerAnnotation = Constants.HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME + - ":" + Constants.HTTP_PACKAGE_VERSION + ":" + Constants.HEADER_ANNOTATION; - BMap headerAnnotationField = (BMap) ((BMap) annotation).get(StringUtils.fromString(headerAnnotation)); + String headerAnnotation = Constants.HTTP_ANNOTATION_PREFIX + Constants.HEADER_ANNOTATION; + BMap headerAnnotationField = + (BMap) ((BMap) annotation).get(StringUtils.fromString(headerAnnotation)); if (headerAnnotationField.size() == 0) { //No annotation field defined {name: ....} if ((parameter.type).getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { @@ -301,22 +207,21 @@ private Optional processHeaderParam(ResourceMethodType resource return Optional.of(new HeaderParameter(i, parameter, headerParam)); } else if (headerAnnotationField.size() == 1) { // Annotation field is defined - BString headerName = ((BMap) headerAnnotationField).getStringValue(StringUtils.fromString("name")); + BString headerName = headerAnnotationField.getStringValue(StringUtils.fromString("name")); headerParam = getHeaderValue(headers, parameter.type, headerName.getValue(), treatNilableAsOptional); return Optional.of(new HeaderParameter(i, parameter, headerParam)); } else { throw new RuntimeException("Header annotation can have only one name field."); //TODO :- add proper exception } - } return Optional.empty(); } private Object getHeaderValue(BMap headers, Type type, String fieldName, - Boolean treatNilableAsOptional) { + boolean treatNilableAsOptional) { BString headerValue = null; - Boolean isHeaderExist = false; + boolean isHeaderExist = false; for (BString headerKey : headers.getKeys()) { if (headerKey.getValue().toLowerCase(Locale.ROOT).equals(fieldName.toLowerCase(Locale.ROOT))) { isHeaderExist = true; @@ -328,19 +233,19 @@ private Object getHeaderValue(BMap headers, Type type, String fieldN } if (!isHeaderExist) { //Header name not exist case - if (isNilType(type) && treatNilableAsOptional) { + if (Utils.isNilType(type) && treatNilableAsOptional) { return null; } throw new HeaderNotFoundException("no header value found for '" + fieldName + "'"); } else if (headerValue.getValue().equals("")) { //Handle header value not exist case - if (isNilType(type)) { + if (Utils.isNilType(type)) { return null; } throw new HeaderNotFoundException("no header value found for '" + fieldName + "'"); } - return createValue(type, headerValue); + return Utils.createValue(type, headerValue); } private Object processHeaderRecordParam(BMap headers, ReferenceType parameter, @@ -357,43 +262,6 @@ private Object processHeaderRecordParam(BMap headers, ReferenceType return recordValue; } - - private boolean isNilType(Type type) { - if (type.getTag() == TypeTags.UNION_TAG) { - List memberTypes = ((UnionType) type).getMemberTypes(); - for (Type memberType : memberTypes) { - if (isNilType(memberType)) { - return true; - } - } - } else if (type.getTag() == TypeTags.NULL_TAG) { - return true; - } - return false; - } - - - private BString getRequestBody(BMap httpPayload, String name, Type type) throws PayloadNotFoundException { - BString bBody = StringUtils.fromString("Body"); - if (httpPayload.containsKey(bBody)) { - return httpPayload.getStringValue(bBody); - } - if (!isNilType(type)) { - throw new PayloadNotFoundException("payload not found for the variable '" + name + "'"); - } - return null; - } - - private String getContentTypeHeader(BMap headers) { - //TODO fix lower case - if (headers.containsKey(StringUtils.fromString(Constants.CONTENT_TYPE))) { - BArray headersArrayValue = headers.getArrayValue(StringUtils.fromString(Constants.CONTENT_TYPE)); - return headersArrayValue.getBString(0).getValue(); - } else { - return null; - } - } - public Object[] getArgList() { List parameters = new ArrayList<>(Arrays.asList(pathParams)); parameters.addAll(Arrays.asList(queryParameter)); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java index 2bf2f2f2..f624b394 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeHttpToAzureAdaptor.java @@ -53,22 +53,22 @@ public static BArray getAzureFunctionNames(Environment env, BObject adaptor) { ServiceType svcType = (ServiceType) bHubService.getType(); List functionNameList = new ArrayList<>(); for (ResourceMethodType resourceMethod : svcType.getResourceMethods()) { - BString functionName = ((BMap) resourceMethod + BString functionName = ((BMap) resourceMethod .getAnnotation(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_COMPLETE))) - .getStringValue(StringUtils.fromString("name")); + .getStringValue(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_NAME_FIELD)); functionNameList.add(functionName); } return ValueCreator.createArrayValue(functionNameList.toArray(BString[]::new)); } - public static Object callNativeMethod(Environment env, BObject adaptor, BMap body, BString functionName) { + public static Object callNativeMethod(Environment env, BObject adaptor, BMap body, BString functionName) { BObject bHubService = (BObject) adaptor.getNativeData(SERVICE_OBJECT); return invokeResourceFunction(env, bHubService, "callNativeMethod", body, functionName); } private static Object invokeResourceFunction(Environment env, BObject bHubService, String parentFunctionName, - BMap body, BString functionName) { + BMap body, BString functionName) { Future balFuture = env.markAsync(); Module module = ModuleUtils.getModule(); StrandMetadata metadata = new StrandMetadata(module.getOrg(), module.getName(), module.getVersion(), @@ -76,7 +76,6 @@ private static Object invokeResourceFunction(Environment env, BObject bHubServic ServiceType serviceType = (ServiceType) bHubService.getType(); ResourceMethodType[] resourceMethods = serviceType.getResourceMethods(); - //TODO restrict "httpPayload" from the param names. Optional resourceMethodType = getResourceMethodType(resourceMethods, functionName); if (resourceMethodType.isEmpty()) { balFuture.complete(Utils.createError(module, "function " + functionName.getValue() + " not found in the " + @@ -85,7 +84,7 @@ private static Object invokeResourceFunction(Environment env, BObject bHubServic } ResourceMethodType resourceMethod = resourceMethodType.get(); try { - BMap serviceAnnotations = serviceType.getAnnotations(); + BMap serviceAnnotations = serviceType.getAnnotations(); HttpResource httpResource = new HttpResource(resourceMethod, body, serviceAnnotations); Object[] args = httpResource.getArgList(); if (serviceType.isIsolated() && resourceMethod.isIsolated()) { @@ -109,8 +108,8 @@ private static Optional getResourceMethodType(ResourceMethod BString enteredFunctionName) { for (ResourceMethodType type : types) { BString functionName = - ((BMap) type.getAnnotation(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_COMPLETE))) - .getStringValue(StringUtils.fromString("name")); + ((BMap) type.getAnnotation(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_COMPLETE))) + .getStringValue(StringUtils.fromString(Constants.FUNCTION_ANNOTATION_NAME_FIELD)); if (functionName.toString().equals(enteredFunctionName.toString())) { return Optional.of(type); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index 6e85dd61..df76714f 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -70,7 +70,7 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, Parameter[] parameters = methodType.getParameters(); for (Parameter parameter : parameters) { String name = parameter.name; - Object annotation = methodType.getAnnotation(StringUtils.fromString("$param$." + name)); + Object annotation = methodType.getAnnotation(StringUtils.fromString(Constants.PARAMETER_ANNOTATION + name)); if (!ParamHandler.isAzureAnnotationExist(annotation)) { Object bValue = getDataboundValue(data, parameter, serviceType); argList.add(bValue); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java index 4ddb0ed5..efe37906 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/ParamHandler.java @@ -64,7 +64,7 @@ public static boolean isPayloadAnnotationParam(Object annotation) { if (!(annotation instanceof BMap)) { return false; } - + for (BString bKey : ((BMap) annotation).getKeys()) { String key = bKey.getValue(); if (key.startsWith(HTTP_PACKAGE_ORG + Constants.SLASH + Constants.HTTP_PACKAGE_NAME) && diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java index 40eba301..697e6e24 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/Utils.java @@ -19,20 +19,162 @@ package io.ballerina.stdlib.azure.functions; import io.ballerina.runtime.api.Module; +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.Type; +import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; +import io.ballerina.stdlib.azure.functions.exceptions.InvalidPayloadException; +import io.ballerina.stdlib.azure.functions.exceptions.PayloadNotFoundException; +import org.ballerinalang.langlib.bool.FromString; +import java.util.List; + +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.FLOAT_TAG; +import static io.ballerina.runtime.api.TypeTags.INT_TAG; import static io.ballerina.runtime.api.utils.StringUtils.fromString; /** - * Contains Utilities related to natives. - * + * Contains Utilities related to natives. + * * @since 2.0.0 */ public class Utils { + + private static final ArrayType INT_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_INT); + private static final ArrayType FLOAT_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_FLOAT); + private static final ArrayType BOOLEAN_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_BOOLEAN); + private static final ArrayType DECIMAL_ARR = TypeCreator.createArrayType(PredefinedTypes.TYPE_DECIMAL); + public static BError createError(Module module, String message, String type) { BString errorMessage = fromString(message); return ErrorCreator.createError(module, type, errorMessage, ErrorCreator.createError(errorMessage), null); } + + public static Object createValue(Type type, BString strValue) { + switch (type.getTag()) { + case TypeTags.STRING_TAG: + return strValue; + case TypeTags.BOOLEAN_TAG: + return FromString.fromString(strValue); + case TypeTags.INT_TAG: + return org.ballerinalang.langlib.integer.FromString.fromString(strValue); + case TypeTags.FLOAT_TAG: + return org.ballerinalang.langlib.floatingpoint.FromString.fromString(strValue); + case TypeTags.DECIMAL_TAG: + return org.ballerinalang.langlib.decimal.FromString.fromString(strValue); + case TypeTags.UNION_TAG: + List memberTypes = ((UnionType) type).getMemberTypes(); + for (Type memberType : memberTypes) { + try { + return createValue(memberType, strValue); + } catch (BError ignored) { + // thrown errors are ignored until all the types are iterated + } + } + return null; + case TypeTags.ARRAY_TAG: + ArrayType arrayType = (ArrayType) type; + Type elementType = arrayType.getElementType(); + if (strValue == null) { + return null; + } + String[] values = strValue.getValue().split(","); + return castParamArray(elementType.getTag(), values); + default: + throw new InvalidPayloadException("unsupported parameter type " + type.getName()); + } + } + + public static BArray castParamArray(int targetElementTypeTag, String[] argValueArr) { + switch (targetElementTypeTag) { + case INT_TAG: + return getBArray(argValueArr, INT_ARR, targetElementTypeTag); + case FLOAT_TAG: + return getBArray(argValueArr, FLOAT_ARR, targetElementTypeTag); + case BOOLEAN_TAG: + return getBArray(argValueArr, BOOLEAN_ARR, targetElementTypeTag); + case DECIMAL_TAG: + return getBArray(argValueArr, DECIMAL_ARR, targetElementTypeTag); + default: + return StringUtils.fromStringArray(argValueArr); + } + } + + private static BArray getBArray(String[] valueArray, ArrayType arrayType, int elementTypeTag) { + BArray arrayValue = ValueCreator.createArrayValue(arrayType); + int index = 0; + for (String element : valueArray) { + switch (elementTypeTag) { + case INT_TAG: + arrayValue.add(index++, Long.parseLong(element)); + break; + case FLOAT_TAG: + arrayValue.add(index++, Double.parseDouble(element)); + break; + case BOOLEAN_TAG: + arrayValue.add(index++, Boolean.parseBoolean(element)); + break; + case DECIMAL_TAG: + arrayValue.add(index++, ValueCreator.createDecimalValue(element)); + break; + default: + throw new InvalidPayloadException("Illegal state error: unexpected param type"); + } + } + return arrayValue; + } + + public static boolean isNilType(Type type) { + if (type.getTag() == TypeTags.UNION_TAG) { + List memberTypes = ((UnionType) type).getMemberTypes(); + for (Type memberType : memberTypes) { + if (isNilType(memberType)) { + return true; + } + } + } else if (type.getTag() == TypeTags.NULL_TAG) { + return true; + } + return false; + } + + public static BString getRequestBody(BMap httpPayload, String name, Type type) + throws PayloadNotFoundException { + BString bBody = StringUtils.fromString(Constants.AZURE_BODY_HEADERS); + if (httpPayload.containsKey(bBody)) { + return httpPayload.getStringValue(bBody); + } + if (!isNilType(type)) { + throw new PayloadNotFoundException("payload not found for the variable '" + name + "'"); + } + return null; + } + + public static String getContentTypeHeader(BMap headers) { + //TODO fix lower case + if (headers.containsKey(StringUtils.fromString(Constants.CONTENT_TYPE))) { + BArray headersArrayValue = headers.getArrayValue(StringUtils.fromString(Constants.CONTENT_TYPE)); + return headersArrayValue.getBString(0).getValue(); + } else { + return null; + } + } + + public static boolean isAzAnnotationExist(Object annotation) { + if (annotation == null) { + return false; + } + return true; + } } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java index c9f59b6d..b4977bfd 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/JsonPayloadBuilder.java @@ -57,10 +57,7 @@ public Object getValue(BString dataSource, boolean readonly) { } public Object createValue(Type payloadType, boolean readonly, Object dataSource) { - -// Object bjson = EntityBodyHandler.constructJsonDataSource(null); -// EntityBodyHandler.addJsonMessageDataSource(null, bjson); -// var result = FromJsonWithType.fromJsonWithType(bjson, ValueCreator.createTypedescValue(payloadType)); + if (dataSource instanceof BString) { BString datasource = (BString) dataSource; if (payloadType.getTag() == TypeTags.UNION_TAG) { diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java index a18c3fd7..70c6c9d2 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/builder/XmlPayloadBuilder.java @@ -40,8 +40,6 @@ public XmlPayloadBuilder(Type payloadType) { @Override public Object getValue(BString entity, boolean readonly) { if (isSubtypeOfAllowedType(payloadType, TypeTags.XML_TAG)) { -// BXml bxml = EntityBodyHandler.constructXmlDataSource(entity); -// EntityBodyHandler.addMessageDataSource(entity, bxml); BXml bxml = XmlUtils.parse(entity); if (readonly) { bxml.freezeDirect(); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java index 9ce0f84b..0ac5cad5 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/converter/JsonToRecordConverter.java @@ -39,8 +39,6 @@ public static Object convert(Type type, Object entity, boolean readonly) { } private static Object getRecordEntity(Object entity, Type entityBodyType) { -// Object bjson = EntityBodyHandler.getMessageDataSource(entity) == null ? getBJsonValue(entity) -// : EntityBodyHandler.getMessageDataSource(entity); Object result = getRecord(entityBodyType, entity); if (result instanceof BError) { throw (BError) result; @@ -63,19 +61,6 @@ private static Object getRecord(Type entityBodyType, Object bjson) { entityBodyType.getName()); } } - -// /** -// * Given an inbound request entity construct the ballerina json. -// * -// * @param entity Represents inbound request entity -// * @return a ballerina json value -// */ -// private static Object getBJsonValue(BObject entity) { -// Object bjson = EntityBodyHandler.constructJsonDataSource(entity); -// EntityBodyHandler.addJsonMessageDataSource(entity, bjson); -// return bjson; -// } - private JsonToRecordConverter() { } From c3e311069af8f00e92743c8d86a25ebb940ddf5c Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Wed, 12 Oct 2022 13:59:22 +0530 Subject: [PATCH 56/59] Fix code modifer prefix issue --- ballerina-tests/{ => tests}/main.bal | 0 .../azurefunctions/AzureFunctionModifier.java | 48 +++++++++++++++---- .../AzureFunctionNameGenerator.java | 2 +- .../AzureFunctionServiceVisitor.java | 5 -- .../org/ballerinax/azurefunctions/Util.java | 2 +- .../service/http/HTTPTriggerBinding.java | 2 +- 6 files changed, 42 insertions(+), 17 deletions(-) rename ballerina-tests/{ => tests}/main.bal (100%) diff --git a/ballerina-tests/main.bal b/ballerina-tests/tests/main.bal similarity index 100% rename from ballerina-tests/main.bal rename to ballerina-tests/tests/main.bal diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java index 448572e7..04f56a8f 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionModifier.java @@ -27,6 +27,10 @@ import io.ballerina.compiler.syntax.tree.BasicLiteralNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.ImportDeclarationNode; +import io.ballerina.compiler.syntax.tree.ImportOrgNameNode; +import io.ballerina.compiler.syntax.tree.ImportPrefixNode; import io.ballerina.compiler.syntax.tree.LiteralValueToken; import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; import io.ballerina.compiler.syntax.tree.MappingFieldNode; @@ -56,26 +60,52 @@ public class AzureFunctionModifier extends TreeModifier { public AzureFunctionModifier(SemanticModel semanticModel) { super(); this.semanticModel = semanticModel; - this.modulePrefix = "af"; //TODO fixme + this.modulePrefix = Constants.AZURE_FUNCTIONS_MODULE_NAME; + } + + @Override + public ImportDeclarationNode transform(ImportDeclarationNode importDeclarationNode) { + Optional importOrgNameNode = importDeclarationNode.orgName(); + if (importOrgNameNode.isEmpty()) { + return importDeclarationNode; + } + if (!Constants.AZURE_FUNCTIONS_PACKAGE_ORG.equals(importOrgNameNode.get().orgName().text())) { + return importDeclarationNode; + } + SeparatedNodeList identifierTokens = importDeclarationNode.moduleName(); + if (identifierTokens.size() != 1) { + return importDeclarationNode; + } + if (!Constants.AZURE_FUNCTIONS_MODULE_NAME.equals(identifierTokens.get(0).text())) { + return importDeclarationNode; + } + + Optional prefix = importDeclarationNode.prefix(); + if (prefix.isEmpty()) { + this.modulePrefix = Constants.AZURE_FUNCTIONS_MODULE_NAME; + return importDeclarationNode; + } + this.modulePrefix = prefix.get().prefix().text(); + return importDeclarationNode; } @Override public ServiceDeclarationNode transform(ServiceDeclarationNode serviceDeclarationNode) { String servicePath = Util.resourcePathToString(serviceDeclarationNode.absoluteResourcePath()); ExpressionNode listenerExpressionNode = serviceDeclarationNode.expressions().get(0); - Optional typeSymbol = semanticModel.typeOf(listenerExpressionNode); - if (typeSymbol.isEmpty()) { + Optional listenerSymbol = semanticModel.typeOf(listenerExpressionNode); + if (listenerSymbol.isEmpty()) { return super.transform(serviceDeclarationNode); } - TypeReferenceTypeSymbol typeSymbol1; - if (TypeDescKind.UNION == typeSymbol.get().typeKind()) { - UnionTypeSymbol union = (UnionTypeSymbol) typeSymbol.get(); - typeSymbol1 = (TypeReferenceTypeSymbol) union.memberTypeDescriptors().get(0); + TypeReferenceTypeSymbol typeRefSymbol; + if (TypeDescKind.UNION == listenerSymbol.get().typeKind()) { + UnionTypeSymbol union = (UnionTypeSymbol) listenerSymbol.get(); + typeRefSymbol = (TypeReferenceTypeSymbol) union.memberTypeDescriptors().get(0); } else { - typeSymbol1 = (TypeReferenceTypeSymbol) typeSymbol.get(); + typeRefSymbol = (TypeReferenceTypeSymbol) listenerSymbol.get(); } - Optional name = typeSymbol1.definition().getName(); + Optional name = typeRefSymbol.definition().getName(); if (name.isEmpty()) { return super.transform(serviceDeclarationNode); } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java index c8639895..799474ef 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionNameGenerator.java @@ -68,7 +68,7 @@ private String getFunctionName(String servicePath, FunctionDefinitionNode functi } return getEncodedAzureFunctionName(resourcePath.toString(), servicePath, method); } - + //TODO move slashs, dashes to consts private String getEncodedAzureFunctionName(String resourcePath, String servicePath, String method) { String functionName = resourcePath.replace("/", "-"); if (servicePath.equals("")) { diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java index 3a420e5f..99f87fed 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java @@ -41,11 +41,6 @@ public AzureFunctionServiceVisitor(SemanticModel semanticModel) { this.semanticModel = semanticModel; } - @Override - public void visit(ModulePartNode modulePartNode) { - super.visit(modulePartNode); - } - @Override public void visit(ServiceDeclarationNode serviceDeclarationNode) { TriggerBinding builder = ServiceHandler.getBuilder(serviceDeclarationNode, semanticModel); diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java index 0128d3df..ff9091b5 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Util.java @@ -99,7 +99,7 @@ public static String resourcePathToString(NodeList nodes) { String finalPath = out.toString(); if (finalPath.startsWith("/")) { return finalPath.substring(1); - } + } return finalPath; } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java index 9b326bc1..72fbca56 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/service/http/HTTPTriggerBinding.java @@ -80,7 +80,7 @@ public List getBindings() { for (Node node : members) { HTTPTriggerBinding httpTriggerBinding = new HTTPTriggerBinding(this.serviceDeclarationNode, this.semanticModel); - httpTriggerAnnot.ifPresent(queueTrigger -> getAnnotation(httpTriggerBinding, queueTrigger)); + httpTriggerAnnot.ifPresent(trigger -> getAnnotation(httpTriggerBinding, trigger)); List bindings = new ArrayList<>(); if (SyntaxKind.RESOURCE_ACCESSOR_DEFINITION != node.kind()) { continue; From 51fdd9dc5927ef72f4f43efa12aa917f1856dbf5 Mon Sep 17 00:00:00 2001 From: Thevakumar-Luheerathan Date: Thu, 13 Oct 2022 15:08:21 +0530 Subject: [PATCH 57/59] Modify based on review --- ballerina-tests/tests/main.bal | 32 ++++++------ .../tests/resources/httpHeaderTest.json | 4 +- ballerina-tests/tests/test.bal | 10 ++-- .../test/ProjectValidationTests.java | 13 +++++ .../http-service-config/Ballerina.toml | 13 +++++ .../validations/http-service-config/main.bal | 27 ++++++++++ .../azurefunctions/AzureDiagnosticCodes.java | 5 +- .../AzureFunctionServiceVisitor.java | 1 - .../ballerinax/azurefunctions/Constants.java | 5 ++ .../validators/HttpListenerValidator.java | 50 +++++++++++++++++-- .../stdlib/azure/functions/HttpResource.java | 1 - 11 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 compiler-plugin-tests/src/test/resources/validations/http-service-config/Ballerina.toml create mode 100644 compiler-plugin-tests/src/test/resources/validations/http-service-config/main.bal diff --git a/ballerina-tests/tests/main.bal b/ballerina-tests/tests/main.bal index 33ee6b15..4a6809ca 100644 --- a/ballerina-tests/tests/main.bal +++ b/ballerina-tests/tests/main.bal @@ -36,22 +36,22 @@ listener af:HttpListener ep2 = new (); treatNilableAsOptional: false } service /httpHeader on ep2 { - resource function post nonTreatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? Hoste) returns string? { - return Hoste; + resource function post nonTreatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? hoste) returns string? { + return hoste; } - resource function post nonTreatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string Hoste) returns string { - return Hoste; + resource function post nonTreatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string hoste) returns string { + return hoste; } - resource function post nonTreatNilAsOpt\-nonNil\-HeaderTest(@http:Header string Hos) returns string { - return Hos; + resource function post nonTreatNilAsOpt\-nonNil\-HeaderTest(@http:Header string hos) returns string { + return hos; } - resource function post nonTreatNilAsOpt\-Nil\-HeaderTest(@http:Header string? Hos) returns string? { - return Hos; + resource function post nonTreatNilAsOpt\-Nil\-HeaderTest(@http:Header string? hos) returns string? { + return hos; } } @@ -97,13 +97,13 @@ service /httpHeader on ep3 { } - resource function post treatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string Hoste) returns string { - return Hoste; + resource function post treatNilAsOpt\-nonNil\-noHeaderTest(@http:Header string hoste) returns string { + return hoste; } - resource function post treatNilAsOpt\-nonNil\-HeaderTest(@http:Header string Hos) returns string { - return Hos; + resource function post treatNilAsOpt\-nonNil\-HeaderTest(@http:Header string hos) returns string { + return hos; } @@ -112,13 +112,13 @@ service /httpHeader on ep3 { } - resource function post treatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? Hoste) returns string? { - return Hoste; + resource function post treatNilAsOpt\-Nil\-noHeaderTest(@http:Header string? hoste) returns string? { + return hoste; } - resource function post treatNilAsOpt\-Nil\-HeaderTest(@http:Header string? Hos) returns string? { - return Hos; + resource function post treatNilAsOpt\-Nil\-HeaderTest(@http:Header string? hos) returns string? { + return hos; } } diff --git a/ballerina-tests/tests/resources/httpHeaderTest.json b/ballerina-tests/tests/resources/httpHeaderTest.json index 2e6ae99b..7992b0b8 100644 --- a/ballerina-tests/tests/resources/httpHeaderTest.json +++ b/ballerina-tests/tests/resources/httpHeaderTest.json @@ -25,7 +25,7 @@ "test":[ "12, 13, 14" ], - "Hos":[ + "hos":[ "" ], "X-ARR-LOG-ID":[ @@ -97,7 +97,7 @@ "Max-Forwards":"10", "User-Agent":"ballerina", "test":"12, 13, 14", - "Hos": "", + "hos": "", "X-ARR-LOG-ID":"77ef8e37-520e-417b-a972-30746b719b60", "CLIENT-IP":"45.121.88.92:53880", "DISGUISED-HOST":"az-func-http-test.azurewebsites.net", diff --git a/ballerina-tests/tests/test.bal b/ballerina-tests/tests/test.bal index 50f4982d..108918a8 100644 --- a/ballerina-tests/tests/test.bal +++ b/ballerina-tests/tests/test.bal @@ -96,7 +96,7 @@ function nnonTreatNilAsOpt\-Nil\-noHeaderTest() returns error? { string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-Nil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-Nil-noHeaderTest", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -108,7 +108,7 @@ function treatNilAsOpt\-nonNil\-noHeaderTest() returns error? { string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-nonNil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-nonNil-noHeaderTest", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -120,7 +120,7 @@ function treatNilAsOpt\-nonNil\-HeaderTest() returns error? { string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "treatNilAsOpt-nonNil-HeaderTest"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-httpHeader-treatNilAsOpt-nonNil-HeaderTest", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -168,7 +168,7 @@ function nonTreatNilAsOpt\-nonNil\-noHeaderTest() returns error? { string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-nonNil-noHeaderTest"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-nonNil-noHeaderTest", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hoste'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } @@ -180,7 +180,7 @@ function nonTreatNilAsOpt\-nonNil\-HeaderTest() returns error? { string replacedString = regex:replaceAll(readString, "(FUNC_NAME)", "nonTreatNilAsOpt-nonNil-HeaderTest"); json readJson = check value:fromJsonString(replacedString); json resp = check clientEndpoint->post("/post-httpHeader-nonTreatNilAsOpt-nonNil-HeaderTest", readJson); - json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'Hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; + json expectedResp = {"Outputs":{"resp":{"statusCode":400, "body":"no header value found for 'hos'", "headers":{"Content-Type":"text/plain"}}}, "Logs":[], "ReturnValue":null}; test:assertEquals(resp, expectedResp); } diff --git a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java index 1a764cdd..ba855b4d 100644 --- a/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java +++ b/compiler-plugin-tests/src/test/java/org/ballerinax/azurefunctions/test/ProjectValidationTests.java @@ -91,4 +91,17 @@ public void headerAnnotationTest() { Assert.assertEquals(((Diagnostic) diagnostics[11]).diagnosticInfo().messageFormat(), diagnosticMessage11); Assert.assertEquals(((Diagnostic) diagnostics[12]).diagnosticInfo().messageFormat(), diagnosticMessage12); } + + @Test + public void httpServiceConfigTest() { + BuildProject project = BuildProject.load(RESOURCE_DIRECTORY.resolve("http-service-config")); + PackageCompilation compilation = project.currentPackage().getCompilation(); + DiagnosticResult diagnosticResult = compilation.diagnosticResult(); + Object[] diagnostics = diagnosticResult.warnings().toArray(); + Assert.assertEquals(diagnosticResult.warningCount(), 2); + String diagnosticMessage = "'treatNilableAsOptional' is the only @http:serviceConfig " + + "field supported by Azure Function at the moment"; + Assert.assertEquals(((Diagnostic) diagnostics[0]).diagnosticInfo().messageFormat(), diagnosticMessage); + Assert.assertEquals(((Diagnostic) diagnostics[1]).diagnosticInfo().messageFormat(), diagnosticMessage); + } } diff --git a/compiler-plugin-tests/src/test/resources/validations/http-service-config/Ballerina.toml b/compiler-plugin-tests/src/test/resources/validations/http-service-config/Ballerina.toml new file mode 100644 index 00000000..e9f5cff9 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/validations/http-service-config/Ballerina.toml @@ -0,0 +1,13 @@ +[package] +org = "luheerathan" +name = "http_header_annotation" +version = "0.1.0" +distribution = "2201.1.0" + +[build-options] +observabilityIncluded = true + +[[dependency]] +org = "ballerinax" +name = "azure_functions" +version = "3.0.0-alpha.1" diff --git a/compiler-plugin-tests/src/test/resources/validations/http-service-config/main.bal b/compiler-plugin-tests/src/test/resources/validations/http-service-config/main.bal new file mode 100644 index 00000000..bd96d751 --- /dev/null +++ b/compiler-plugin-tests/src/test/resources/validations/http-service-config/main.bal @@ -0,0 +1,27 @@ +import ballerinax/azure_functions as af; +import ballerina/http; + +listener af:HttpListener ep1 = new (); +@http:ServiceConfig{treatNilableAsOptional: true, host : "b7a.default"} +service "hello" on ep1 { + resource function get test1(@http:Header string? hoste) returns string? { + return hoste; + } +} + +listener af:HttpListener ep2 = new (); +@http:ServiceConfig{host : "b7a.default"} +service "hello" on ep2 { + resource function get test1(@http:Header string? hoste) returns string? { + return hoste; + } +} + +listener af:HttpListener ep3 = new (); +@http:ServiceConfig{treatNilableAsOptional: true} +service "hello" on ep3 { + resource function get test1(@http:Header string? hoste) returns string? { + return hoste; + } +} + diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java index 270b18f2..290a5540 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureDiagnosticCodes.java @@ -21,6 +21,7 @@ import io.ballerina.tools.diagnostics.DiagnosticSeverity; import static io.ballerina.tools.diagnostics.DiagnosticSeverity.ERROR; +import static io.ballerina.tools.diagnostics.DiagnosticSeverity.WARNING; /** * {@code DiagnosticCodes} is used to hold diagnostic codes. @@ -37,7 +38,9 @@ public enum AzureDiagnosticCodes { AF_005("AF_005", "invalid intersection type : '%s'. Only readonly type is allowed", ERROR), AF_006("AF_006", "rest fields are not allowed for header binding records. Use 'http:Headers' type to access " + "all headers", ERROR), - AF_007("AF_007", "invalid multiple resource parameter annotations for '%s'", ERROR); + AF_007("AF_007", "invalid multiple resource parameter annotations for '%s'", ERROR), + AF_008("AF_008", "'treatNilableAsOptional' is the only @http:serviceConfig field supported by Azure " + + "Function at the moment", WARNING); private final String code; private final String message; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java index 99f87fed..691b5a4a 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/AzureFunctionServiceVisitor.java @@ -18,7 +18,6 @@ package org.ballerinax.azurefunctions; import io.ballerina.compiler.api.SemanticModel; -import io.ballerina.compiler.syntax.tree.ModulePartNode; import io.ballerina.compiler.syntax.tree.NodeVisitor; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import org.ballerinax.azurefunctions.service.ServiceHandler; diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java index abb771d4..6093b57e 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/Constants.java @@ -23,6 +23,8 @@ public class Constants { public static final String AZURE_FUNCTIONS_MODULE_NAME = "azure_functions"; + public static final String COLON = ":"; + public static final String HTTP = "http"; public static final String HEADER_ANNOTATION_TYPE = "HttpHeader"; public static final String AZURE_FUNCTIONS_PACKAGE_ORG = "ballerinax"; @@ -64,4 +66,7 @@ public class Constants { public static final String TASKS_FILE_NAME = "tasks.json"; public static final String FUNCTION_ANNOTATION = "Function"; + public static final String SERVICE_CONFIG_ANNOTATION = "ServiceConfig"; + public static final String TREAT_NILABLE_AS_OPTIONAL = "treatNilableAsOptional"; + } diff --git a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java index f86ae0b4..183998b5 100644 --- a/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java +++ b/compiler-plugin/src/main/java/org/ballerinax/azurefunctions/validators/HttpListenerValidator.java @@ -31,12 +31,19 @@ import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; 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.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.IdentifierToken; +import io.ballerina.compiler.syntax.tree.MappingConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.MappingFieldNode; +import io.ballerina.compiler.syntax.tree.MetadataNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.projects.plugins.SyntaxNodeAnalysisContext; +import io.ballerina.tools.diagnostics.DiagnosticFactory; +import io.ballerina.tools.diagnostics.DiagnosticInfo; import io.ballerina.tools.diagnostics.Location; import org.ballerinax.azurefunctions.AzureDiagnosticCodes; import org.wso2.ballerinalang.compiler.diagnostic.properties.BSymbolicProperty; @@ -47,9 +54,13 @@ import java.util.Optional; import java.util.stream.Collectors; +import static org.ballerinax.azurefunctions.AzureDiagnosticCodes.AF_008; import static org.ballerinax.azurefunctions.Constants.AZURE_FUNCTIONS_MODULE_NAME; +import static org.ballerinax.azurefunctions.Constants.COLON; import static org.ballerinax.azurefunctions.Constants.HEADER_ANNOTATION_TYPE; import static org.ballerinax.azurefunctions.Constants.HTTP; +import static org.ballerinax.azurefunctions.Constants.SERVICE_CONFIG_ANNOTATION; +import static org.ballerinax.azurefunctions.Constants.TREAT_NILABLE_AS_OPTIONAL; import static org.ballerinax.azurefunctions.Util.updateDiagnostic; /** @@ -76,7 +87,36 @@ private static void validateResourceFunction(SyntaxNodeAnalysisContext ctx, Func private static void extractServiceAnnotationAndValidate(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext, ServiceDeclarationNode serviceDeclarationNode) { - //TODO : Validate service annotation fields + //HTTP serviceconfig validation currently supports only for treatNilableAsTrue field + Optional metadataNodeOptional = serviceDeclarationNode.metadata(); + if (metadataNodeOptional.isEmpty()) { + return; + } + NodeList annotations = metadataNodeOptional.get().annotations(); + for (AnnotationNode annotation : annotations) { + Node annotReference = annotation.annotReference(); + String annotName = annotReference.toString(); + Optional annotValue = annotation.annotValue(); + if (annotReference.kind() != SyntaxKind.QUALIFIED_NAME_REFERENCE) { + continue; + } + String[] annotStrings = annotName.split(COLON); + if (SERVICE_CONFIG_ANNOTATION.equals(annotStrings[annotStrings.length - 1].trim()) + && (annotValue.isPresent())) { + MappingConstructorExpressionNode mapping = annotValue.get(); + NodeList fields = mapping.fields(); + if (fields.size() == 1) { + MappingFieldNode field = (MappingFieldNode) fields.get(0); + String fieldName = ((IdentifierToken) (field.children()).get(0)).text(); + if (!TREAT_NILABLE_AS_OPTIONAL.equals(fieldName)) { + warnInvalidServiceConfig(syntaxNodeAnalysisContext, field); + } + } else { + warnInvalidServiceConfig(syntaxNodeAnalysisContext, mapping); + } + } + } + } private static void validateInputParameters(SyntaxNodeAnalysisContext ctx, FunctionDefinitionNode member) { @@ -90,9 +130,6 @@ private static void validateInputParameters(SyntaxNodeAnalysisContext ctx, Funct if (parametersOptional.isEmpty()) { return; } - if (parametersOptional.get().size() == 0) { - return; - } for (ParameterSymbol param : parametersOptional.get()) { Optional paramLocationOptional = param.getLocation(); @@ -315,6 +352,11 @@ private static void reportInvalidMultipleAnnotation(SyntaxNodeAnalysisContext ct String paramName) { updateDiagnostic(ctx, location, AzureDiagnosticCodes.AF_007, paramName); } + + private static void warnInvalidServiceConfig(SyntaxNodeAnalysisContext ctx, Node node) { + DiagnosticInfo diagInfo = new DiagnosticInfo(AF_008.getCode(), AF_008.getMessage(), AF_008.getSeverity()); + ctx.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagInfo, node.location())); + } } diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index a92fc88f..05e9eeb2 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -212,7 +212,6 @@ private Optional processHeaderParam(ResourceMethodType resource return Optional.of(new HeaderParameter(i, parameter, headerParam)); } else { throw new RuntimeException("Header annotation can have only one name field."); - //TODO :- add proper exception } } return Optional.empty(); From 22d98e0bfe40cfc1dee0c1ce0201c122e14aedb1 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Thu, 13 Oct 2022 23:08:47 +0530 Subject: [PATCH 58/59] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 75c93166..defa38f6 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -139,20 +139,6 @@ dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "lang.__internal"} ] -modules = [ - {org = "ballerina", packageName = "lang.array", moduleName = "lang.array"} -] - -[[package]] -org = "ballerina" -name = "lang.boolean" -version = "0.0.0" -dependencies = [ - {org = "ballerina", name = "jballerina.java"} -] -modules = [ - {org = "ballerina", packageName = "lang.boolean", moduleName = "lang.boolean"} -] [[package]] org = "ballerina" @@ -193,9 +179,6 @@ version = "0.0.0" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] -modules = [ - {org = "ballerina", packageName = "lang.string", moduleName = "lang.string"} -] [[package]] org = "ballerina" @@ -299,10 +282,7 @@ version = "3.0.0-alpha.1" dependencies = [ {org = "ballerina", name = "http"}, {org = "ballerina", name = "jballerina.java"}, - {org = "ballerina", name = "lang.array"}, - {org = "ballerina", name = "lang.boolean"}, {org = "ballerina", name = "lang.int"}, - {org = "ballerina", name = "lang.string"}, {org = "ballerina", name = "os"} ] modules = [ From 40f9acdae2101c3b3bda9deb096388f476438273 Mon Sep 17 00:00:00 2001 From: Anjana Supun Date: Thu, 13 Oct 2022 23:11:00 +0530 Subject: [PATCH 59/59] Address several review suggestions --- ballerina/code.bal | 690 ------------------ ballerina/http_listener.bal | 1 + ballerina/records.bal | 72 ++ ballerina/service_types.bal | 23 + .../azure/functions/FunctionCallback.java | 16 +- .../stdlib/azure/functions/HttpResource.java | 1 + .../functions/NativeHttpToAzureAdaptor.java | 3 +- .../azure/functions/NativeRemoteAdapter.java | 2 +- .../stdlib/azure/functions/PathParameter.java | 1 + 9 files changed, 111 insertions(+), 698 deletions(-) delete mode 100644 ballerina/code.bal create mode 100644 ballerina/records.bal diff --git a/ballerina/code.bal b/ballerina/code.bal deleted file mode 100644 index 0daa3b53..00000000 --- a/ballerina/code.bal +++ /dev/null @@ -1,690 +0,0 @@ -// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -// -// WSO2 Inc. 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; -import ballerina/os; -import ballerina/lang.'int as ints; -import ballerina/lang.'array as arrays; -import ballerina/lang.'string as strings; -import ballerina/lang.'boolean as booleans; - -http:Listener httpListener = check new (check ints:fromString(os:getEnv("FUNCTIONS_CUSTOMHANDLER_PORT"))); - -public type TimerMetadata record { - TimerSchedule Schedule; - anydata ScheduleStatus?; - boolean IsPastDue; -}; - -public type TimerSchedule record { - boolean AdjustForDST; -}; - -type Payload record { - map Data; - Metadata Metadata; -}; - -type Metadata record { - map Query; - map Headers; - Sys sys; -}; - -type Sys record { - string MethodName; - string UtcNow; - string RandGuid; -}; - -type HttpPayload record { - string Url; - string Method; - map Query; - map Headers; - map Params; - Identity[] Identities; - anydata? Body?; -}; - -type Identity record { - // anydata? AuthenticationType?; - // boolean IsAuthenticated?; - // anydata? Actor?; - // anydata BootstrapContext?; - // anydata[] Claims?; - // anydata? Label?; - // anydata? Name?; - // string NameClaimType?; - // string RoleClaimType?; -}; - -# HTTP binding data. -# -# + statusCode - The HTTP response status code -# + payload - The HTTP response payload -public type HTTPBinding record { - int statusCode = 200; - string payload?; -}; - -# String output binding data. -# -# + value - The string value -public type StringOutputBinding record { - string value?; -}; - -# Byte array output binding data. -# -# + value - The byte[] value -public type BytesOutputBinding record { - byte[] value?; -}; - -# Twilion SMS output binding data. -# -# + to - The SMS recipient phone number -# + body - The message body -public type TwilioSmsOutputBinding record { - string to?; - string body?; -}; - -# HTTP request binding data. -# -# + url - The request URL -# + method - The request HTTP method -# + query - The request query parameter map -# + headers - The request HTTP header map -# + params - The request parameters -# + identities - The request identities -# + body - The request body -public type HTTPRequest record { - string url; - string method; - map query; - map headers; - map params; - json[] identities; - string body; -}; - -# INTERNAL stucture - the request handler parameter data. -# -# + request - The HTTP request -# + response - The HTTP response -# + pure - The flag to mention if it's a pure HTTP request -# + result - The result JSON -public type HandlerParams record { - http:Request request; - http:Response response; - boolean pure = false; - json result = { Outputs: {}, Logs: [] }; -}; - -# The request context holder. -# -# + metadata - The context metadata -public class Context { - - HandlerParams hparams; - - public json metadata; - - public isolated function init(HandlerParams hparams, boolean populateMetadata) returns error? { - self.hparams = hparams; - if populateMetadata { - self.metadata = check getMetadata(self.hparams); - } else { - self.metadata = {}; - } - } - - # Enters to function invocation logs. - # - # + msg - The log message - public isolated function log(string msg) { - log(self.hparams, msg); - } - -} - -# INTERNAL usage - Enters to function invocation logs. -# -# + hparams - The handler parameters -# + msg - The log message -public isolated function log(HandlerParams hparams, string msg) { - json[] logs = checkpanic hparams.result.Logs; - logs.push(msg); -} - -# INTERNAL usage - Checks if request tracing is enabled. -# -# + return - The request tracing flag -public isolated function isRequestTrace() returns boolean { - string? value = os:getEnv("BALLERINA_AZURE_FUNCTIONS_REQUEST_TRACE"); - if value is string { - var flag = booleans:fromString(value); - if flag is boolean { - return flag; - } else { - return false; - } - } else { - return false; - } -} - -public isolated function logError(HandlerParams hparams, error err) { - log(hparams, "ERROR: " + err.toString()); -} - -public isolated function logRequest(HandlerParams hparams, http:Request request) { - var payload = request.getTextPayload(); - string val = payload is error ? payload.toString() : payload.toString(); - log(hparams, "REQUEST: " + val); -} - -# Function handler type. -type FunctionHandler (function (HandlerParams) returns error?); - -@untainted public listener http:Listener hl = new(9000); - -public isolated function handleFunctionResposne(error? err, HandlerParams hparams) { - http:Request request = hparams.request; - http:Response response = hparams.response; - if err is error { - logError(hparams, err); - logRequest(hparams, request); - response.setJsonPayload(<@untainted> hparams.result); - } else { - if !hparams.pure { - if isRequestTrace() { - logRequest(hparams, request); - } - response.setJsonPayload(<@untainted> hparams.result); - } - } -} - -# INTERNAL usage - extracts the metadata. -# -# + hparams - The handler parameters -# + return - The metadata JSON -public isolated function getMetadata(HandlerParams hparams) returns json|error { - json payload = check <@untainted> hparams.request.getJsonPayload(); - json metadata = check payload.Metadata; - return metadata; -} - -# INTERNAL usage - creates function context. -# -# + hparams - The handler parameters -# + populateMetadata - The flag to populate metadata -# + return - The function context -public isolated function createContext(HandlerParams hparams, boolean populateMetadata) returns Context|error { - return new Context(hparams, populateMetadata); -} - -# INTERNAL usage - Sets the HTTP output. -# -# + params - The handler parameters -# + name - The parameter name -# + binding - The binding data -# + return - An error in failure -public isolated function setHTTPOutput(HandlerParams params, string name, HTTPBinding binding) returns error? { - string? payload = binding?.payload; - if (payload is string) { - json content = params.result; - json outputs = check content.Outputs; - map bvals = { }; - bvals[name] = { statusCode: binding.statusCode, body: payload }; - _ = check outputs.mergeJson(bvals); - } -} - -# INTERNAL usage - Sets the string output. -# -# + params - The handler parameters -# + name - The parameter name -# + binding - The binding data -# + return - An error in failure -public isolated function setStringOutput(HandlerParams params, string name, StringOutputBinding binding) returns error? { - string? value = binding?.value; - if (value is string) { - json content = params.result; - json outputs = check content.Outputs; - map bvals = { }; - bvals[name] = value; - _ = check outputs.mergeJson(bvals); - } -} - -# INTERNAL usage - Sets the Blob output. -# -# + params - The handler parameters -# + name - The parameter name -# + binding - The binding data -# + return - An error in failure -public isolated function setBlobOutput(HandlerParams params, string name, any binding) returns error? { - string? value = (); - if binding is BytesOutputBinding { - byte[]? bytes = binding?.value; - if bytes is byte[] { - value = bytes.toBase64(); - } - } else if binding is StringOutputBinding { - string? text = binding?.value; - if text is string { - value = text.toBytes().toBase64(); - } - } - if value is string { - json content = params.result; - json outputs = check content.Outputs; - map bvals = { }; - bvals[name] = value; - _ = check outputs.mergeJson(bvals); - } -} - -# INTERNAL usage - Sets the Twilio output. -# -# + params - The handler parameters -# + name - The parameter name -# + binding - The binding data -# + return - An error in failure -public isolated function setTwilioSmsOutput(HandlerParams params, string name, TwilioSmsOutputBinding binding) returns error? { - string? to = binding?.to; - string? body = binding?.body; - if to is string && body is string { - json content = params.result; - json outputs = check content.Outputs; - map bvals = { }; - bvals[name] = { body, to }; - _ = check outputs.mergeJson(bvals); - } -} - -# INTERNAL usage - Sets the pure HTTP output. -# -# + params - The handler parameters -# + binding - The binding data -# + return - An error in failure -public isolated function setPureHTTPOutput(HandlerParams params, HTTPBinding binding) returns error? { - string? payload = binding?.payload; - if payload is string { - params.response.statusCode = binding.statusCode; - params.response.setTextPayload(payload); - } - params.pure = true; -} - -# INTERNAL usage - Sets the pure string output. -# -# + params - The handler parameters -# + value - The value -# + return - An error in failure -public isolated function setPureStringOutput(HandlerParams params, string value) returns error? { - params.response.setTextPayload(value); - params.pure = true; -} - -# INTERNAL usage - Returns the HTTP request data. -# -# + params - The handler parameters -# + return - The HTTP request -public isolated function getHTTPRequestFromParams(HandlerParams params) returns http:Request|error { - return params.request; -} - -# INTERNAL usage - Returns the string payload from the HTTP request. -# -# + params - The handler parameters -# + return - The string payload -public isolated function getStringFromHTTPReq(HandlerParams params) returns string|error { - return check <@untainted> params.request.getTextPayload(); -} - -# INTERNAL usage - Returns a parsed JSON value. -# -# + input - The escaped JSON value -# + return - The parsed JSON value -isolated function parseJson(string input) returns json|error { - json x = check input.fromJsonString(); - return x; -} - -# INTERNAL usage - Returns a json value from metadata. -# -# + params - The handler parameters -# + name - The metadata entry name -# + return - The metadata entry value -public isolated function getJsonFromMetadata(HandlerParams params, string name) returns json|error { - map metadata = > check getMetadata(params); - return parseJson(metadata[name].toString()); -} - -# INTERNAL usage - Returns a string value from metadata. -# -# + params - The handler parameters -# + name - The metadata entry name -# + return - The metadata entry value -public isolated function getStringFromMetadata(HandlerParams params, string name) returns string|error { - json result = check getJsonFromMetadata(params, name); - return result.toString(); -} - -# INTERNAL usage - Returns the JSON payload from the HTTP request. -# -# + params - The handler parameters -# + return - The JSON payload -public isolated function getJsonFromHTTPReq(HandlerParams params) returns json|error { - return check <@untainted> params.request.getJsonPayload(); -} - -# INTERNAL usage - Returns the binary payload from the HTTP request. -# -# + params - The handler parameters -# + return - The binary payload -public isolated function getBinaryFromHTTPReq(HandlerParams params) returns byte[]|error { - return check <@untainted> params.request.getBinaryPayload(); -} - -# INTERNAL usage - Returns the string value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The string value -public isolated function getStringFromInputData(HandlerParams params, string name) returns string|error { - json payload = check getJsonFromHTTPReq(params); - map data = > check payload.Data; - return data[name].toString(); -} - -# INTERNAL usage - Returns the JSON string value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The string value -public isolated function getJsonStringFromInputData(HandlerParams params, string name) returns string|error { - return (check getJsonFromInputData(params, name)).toString(); -} - -# INTERNAL usage - Returns the optional string value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The optional string value -public isolated function getOptionalStringFromInputData(HandlerParams params, string name) returns string?|error { - json payload = check getJsonFromHTTPReq(params); - map data = > check payload.Data; - json result = data[name]; - if result == () { - return (); - } - return result.toString(); -} - -# INTERNAL usage - Returns the binary value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The binary value -public isolated function getBytesFromInputData(HandlerParams params, string name) returns byte[]|error { - string data = check getStringFromInputData(params, name); - return arrays:fromBase64(data.toString()); -} - -# INTERNAL usage - Returns the optional binary value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The optional string value -public isolated function getOptionalBytesFromInputData(HandlerParams params, string name) returns byte[]?|error { - string? data = check getOptionalStringFromInputData(params, name); - if data == () { - return (); - } else { - return arrays:fromBase64(data.toString()); - } -} - -# INTERNAL usage - Returns the string value converted from input binary data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The string value -public isolated function getStringConvertedBytesFromInputData(HandlerParams params, string name) returns string|error { - string data = check getStringFromInputData(params, name); - var result = arrays:fromBase64(data.toString()); - if result is error { - return result; - } else { - return check strings:fromBytes(result); - } -} - -# INTERNAL usage - Returns the optional string value converted from input binary data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The optional binary value -public isolated function getOptionalStringConvertedBytesFromInputData(HandlerParams params, string name) returns string?|error { - string? data = check getOptionalStringFromInputData(params, name); - if data == () { - return (); - } else { - var result = arrays:fromBase64(data.toString()); - if result is error { - return result; - } else { - return check strings:fromBytes(result); - } - } -} - -# INTERNAL usage - Returns the HTTP body value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The HTTP body -public isolated function getBodyFromHTTPInputData(HandlerParams params, string name) returns string|error { - HTTPRequest req = check getHTTPRequestFromInputData(params, name); - return req.body; -} - -# INTERNAL usage - Extracts HTTP headers from the JSON value. -# -# + headers - The headers JSON -# + return - The headers map -isolated function extractHTTPHeaders(json headers) returns map { - map headerMap = > headers; - map result = {}; - foreach var key in headerMap.keys() { - json[] values = headerMap[key]; - string[] headerVals = values.map(isolated function (json j) returns string { return j.toString(); } ); - result[key] = headerVals; - } - return result; -} - -# INTERNAL usage - Extracts string map from the JSON value. -# -# + params - The params JSON -# + return - The string map -isolated function extractStringMap(json params) returns map { - map paramMap = > params; - map result = {}; - foreach var key in paramMap.keys() { - result[key] = paramMap[key].toString(); - } - return result; -} - -# INTERNAL usage - Populates the HTTP request structure from an input data entry. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The HTTP request -public isolated function getHTTPRequestFromInputData(HandlerParams params, string name) returns HTTPRequest|error { - json payload = check getJsonFromHTTPReq(params); - map data = > check payload.Data; - json hreq = data[name]; - var urlVal = hreq.Url; - string url = urlVal is error ? urlVal.toString() : urlVal.toString(); - var methodVal = hreq.Method; - string method = methodVal is error ? methodVal.toString() : methodVal.toString(); - map headers = extractHTTPHeaders(check hreq.Headers); - map hparams = extractStringMap(check hreq.Params); - json qx = check hreq.Query; - map query = extractStringMap(check parseJson(qx.toString())); - json idx = check hreq.Identities; - json identitiesTemp = check parseJson(idx.toString()); - json[] identities = identitiesTemp; - var bodyVal = hreq.Body; - string body = bodyVal is error ? bodyVal.toString() : bodyVal.toString(); - HTTPRequest req = { url: url, method: method, query: query, headers: headers, - params: hparams, identities: identities, body: body }; - return req; -} - -# INTERNAL usage - Returns the JSON value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The JSON value -public isolated function getJsonFromInputData(HandlerParams params, string name) returns json|error { - return parseJson(check getStringFromInputData(params, name)); -} - -# INTERNAL usage - JSON parse the string value available from "getJsonStringFromInputData". -# -# + params - The handler parameters -# + name - The input data entry name -# + return - The JSON value -public isolated function getParsedJsonFromJsonStringFromInputData(HandlerParams params, string name) returns json|error { - return parseJson(check getJsonStringFromInputData(params, name)); -} - -# INTERNAL usage - Returns a converted Ballerina value from input data. -# -# + params - The handler parameters -# + name - The input data entry name -# + recordType - The record type descriptor -# + return - The JSON value -public isolated function getBallerinaValueFromInputData(HandlerParams params, string name, - typedesc recordType) returns anydata|error { - var result = getJsonFromInputData(params, name); - if result is error { - return result; - } else { - return result.cloneWithType(recordType); - } -} - -# INTERNAL usage - Returns the optional converted Ballerina value from "getParsedJsonFromJsonStringFromInputData". -# -# + params - The handler parameters -# + name - The input data entry name -# + recordType - The record type descriptor -# + return - The JSON value -public isolated function getOptionalBallerinaValueFromInputData(HandlerParams params, string name, - typedesc recordType) returns anydata?|error { - var result = getParsedJsonFromJsonStringFromInputData(params, name); - if result is error { - return result; - } else if result == () { - return (); - } else { - return result.cloneWithType(recordType); - } -} - -# INTERNAL usage - Sets the string return value. -# -# + params - The handler parameters -# + value - The string return value -# + return - An error in failure -public isolated function setStringReturn(HandlerParams params, string value) returns error? { - json content = params.result; - _ = check content.mergeJson({ ReturnValue: value }); -} - -# INTERNAL usage - Sets the JSON return value. -# -# + params - The handler parameters -# + value - The JSON return value -# + return - An error in failure -public isolated function setJsonReturn(HandlerParams params, json value) returns error? { - json content = params.result; - _ = check content.mergeJson({ ReturnValue: value }); -} - -# INTERNAL usage - Sets the CosmosDS JSON return value. -# -# + params - The handler parameters -# + value - The JSON return value -# + partitionKey - The partition key -# + return - An error in failure -public isolated function setCosmosDBJsonReturn(HandlerParams params, json value, string partitionKey) returns error? { - json content = params.result; - if partitionKey.length() > 0 { - if value is json[] { - json[] valArray = value; - foreach json valEntry in valArray { - _ = check valEntry.mergeJson({ pk: partitionKey }); - } - } else { - _ = check value.mergeJson({ pk: partitionKey }); - } - } - _ = check content.mergeJson({ ReturnValue: value }); -} - -# INTERNAL usage - Converts a Ballerina value to a JSON and set the return value. -# -# + params - The handler parameters -# + value - The value -# + return - An error in failure -public isolated function setBallerinaValueAsJsonReturn(HandlerParams params, anydata value) returns error? { - check setJsonReturn(params, check value.cloneWithType(json)); -} - -# INTERNAL usage - Converts a CosmosDS Ballerina value to a JSON and set the return value. -# -# + params - The handler parameters -# + value - The value -# + partitionKey - The partition key -# + return - An error in failure -public isolated function setCosmosDBBallerinaValueAsJsonReturn(HandlerParams params, anydata value, - string partitionKey) returns error? { - check setCosmosDBJsonReturn(params, check value.cloneWithType(json), partitionKey); -} - -# INTERNAL usage - Sets the HTTP binding return value. -# -# + params - The handler parameters -# + binding - The HTTP binding return value -# + return - An error in failure -public isolated function setHTTPReturn(HandlerParams params, HTTPBinding binding) returns error? { - string? payload = binding?.payload; - if (payload is string) { - json content = params.result; - _ = check content.mergeJson({ ReturnValue: { statusCode: binding.statusCode, body: payload } }); - } -} diff --git a/ballerina/http_listener.bal b/ballerina/http_listener.bal index 827e2162..2091e8ff 100644 --- a/ballerina/http_listener.bal +++ b/ballerina/http_listener.bal @@ -14,6 +14,7 @@ // specific language governing permissions and limitations // under the License. +//TODO See if unused methods are required for a listener declaration public class HttpListener { ResourceService[] httpServices; diff --git a/ballerina/records.bal b/ballerina/records.bal new file mode 100644 index 00000000..88dacaff --- /dev/null +++ b/ballerina/records.bal @@ -0,0 +1,72 @@ +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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. + +# Represents details about the Timer trigger event +# +# + Schedule - Schedule the timer is being executed on +# + ScheduleStatus - Status of the Schedule +# + IsPastDue - Weather the timer is past its due time +public type TimerMetadata record { + TimerSchedule Schedule; + anydata ScheduleStatus?; + boolean IsPastDue; +}; + +# Represents the details about timer schedule +# +# + AdjustForDST - shows weather time is adjusted for DST +public type TimerSchedule record { + boolean AdjustForDST; +}; + +type Payload record { + map Data; + Metadata Metadata; +}; + +type Metadata record { + map Query; + map Headers; + Sys sys; +}; + +type Sys record { + string MethodName; + string UtcNow; + string RandGuid; +}; + +type HttpPayload record { + string Url; + string Method; + map Query; + map Headers; + map Params; + Identity[] Identities; + anydata? Body?; +}; + +type Identity record { +}; + +# Twilion SMS output binding data. +# +# + to - The SMS recipient phone number +# + body - The message body +public type TwilioSmsOutputBinding record { + string to?; + string body?; +}; diff --git a/ballerina/service_types.bal b/ballerina/service_types.bal index 26a1c94e..fe91f82c 100644 --- a/ballerina/service_types.bal +++ b/ballerina/service_types.bal @@ -1,3 +1,26 @@ +// Copyright (c) 2022 WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +// +// WSO2 Inc. 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; +import ballerina/os; +import ballerina/lang.'int as ints; + +//TODO rename file and docs +http:Listener httpListener = check new (check ints:fromString(os:getEnv("FUNCTIONS_CUSTOMHANDLER_PORT"))); + public type RemoteService QueueService|CosmosService|TimerService|BlobService; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java index 37e22a72..9a936d89 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/FunctionCallback.java @@ -95,12 +95,9 @@ public void notifySuccess(Object result) { BMap mapValue = ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + //Refactor to readable if (result == null) { - BString statusCode = StringUtils.fromString(Constants.ACCEPTED); - BMap respMap = - ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); - respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); - mapValue.put(StringUtils.fromString(Constants.RESPONSE_FIELD), respMap); + handleNilReturnType(mapValue); future.complete(mapValue); return; } @@ -127,6 +124,14 @@ public void notifySuccess(Object result) { future.complete(mapValue); } + private void handleNilReturnType(BMap mapValue) { + BString statusCode = StringUtils.fromString(Constants.ACCEPTED); + BMap respMap = + ValueCreator.createMapValue(TypeCreator.createMapType(PredefinedTypes.TYPE_ANYDATA)); + respMap.put(StringUtils.fromString(Constants.STATUS_CODE), statusCode); + mapValue.put(StringUtils.fromString(Constants.RESPONSE_FIELD), respMap); + } + @Override public void notifyFailure(BError bError) { bError.printStackTrace(); @@ -145,7 +150,6 @@ private boolean isModuleDefinedError(BError error) { } private boolean isHTTPStatusCodeResponse(Object result) { -// Module resultPkg = TypeUtils.getType(result).getPackage(); return (result instanceof BMap) && (((BMap) result).containsKey(fromString(Constants.STATUS))); //TODO : Check inheritance //(https://github.com/ballerina-platform/module-ballerinax-azure.functions/issues/490) diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java index 05e9eeb2..b18562b2 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/HttpResource.java @@ -131,6 +131,7 @@ private PathParameter[] getPathParams(ResourceMethodType resourceMethod, BMap body, BString functionName) { Future balFuture = env.markAsync(); diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java index df76714f..85a00193 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/NativeRemoteAdapter.java @@ -66,7 +66,7 @@ private static Object invokeRemoteFunction(Environment env, BObject bHubService, parentFunctionName); ServiceType serviceType = (ServiceType) bHubService.getType(); List argList = new ArrayList<>(); - RemoteMethodType methodType = getRemoteMethod(serviceType, remoteFuncName).orElseThrow(); //TODO handle error + RemoteMethodType methodType = getRemoteMethod(serviceType, remoteFuncName).orElseThrow(); Parameter[] parameters = methodType.getParameters(); for (Parameter parameter : parameters) { String name = parameter.name; diff --git a/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java b/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java index 3674b8ea..fc2fbe2d 100644 --- a/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java +++ b/native/src/main/java/io/ballerina/stdlib/azure/functions/PathParameter.java @@ -27,6 +27,7 @@ */ public class PathParameter extends AZFParameter { private String value; + public PathParameter(int index, Parameter parameter, String value) { super(index, parameter); this.value = value;