Repository: ballerina-platform/ballerina-distribution
Branch: master
Commit: ff2dd242f9b1
Files: 3128
Total size: 2.8 MB
Directory structure:
gitextract_3c7m_6fq/
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug.yml
│ │ ├── config.yml
│ │ ├── improvement.yml
│ │ ├── new-feature.yml
│ │ └── task.yml
│ └── workflows/
│ ├── add_reason_labels.yml
│ ├── bbe-on-demand.yml
│ ├── daily-build-2201.12.x.yml
│ ├── daily-build-2201.13.x.yml
│ ├── daily-build-editor.yml
│ ├── daily-build.yml
│ ├── fossa_scan.yml
│ ├── language_server_simulator_fhir.yml
│ ├── language_server_simulator_nballerina.yml
│ ├── main.yml
│ ├── publish-release-artifacts-1.2.x.yml
│ ├── publish-release-artifacts.yml
│ ├── publish-release.yml
│ ├── publish_release_bi.yml
│ ├── pull-request.yml
│ ├── sign-installers.yml
│ ├── test-installers.yml
│ └── trigger-bbe-generation.yml
├── .gitignore
├── .trivyignore
├── LICENSE
├── README.md
├── ballerina/
│ ├── COPYRIGHT
│ ├── LICENSE
│ ├── README
│ ├── build.gradle
│ └── lib/
│ ├── run.bat
│ ├── run.sh
│ └── version.txt
├── ballerina-test/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── ballerinalang/
│ │ └── distribution/
│ │ ├── lengthValidation/
│ │ │ ├── LengthValidator.java
│ │ │ └── LineLengthExceededException.java
│ │ ├── test/
│ │ │ ├── ArtifactBuildTest.java
│ │ │ ├── BallerinaCommandTest.java
│ │ │ ├── DistributionArtifactCheckTest.java
│ │ │ ├── GraphqlToolTest.java
│ │ │ ├── GrpcToolingTest.java
│ │ │ ├── OpenAPIArtifactBuildTest.java
│ │ │ ├── OpenAPIDistributionArtifactCheck.java
│ │ │ └── PlatformDistributionArtifactCheckTest.java
│ │ └── utils/
│ │ └── TestUtils.java
│ └── resources/
│ ├── graphql/
│ │ ├── client-gen/
│ │ │ ├── project_1/
│ │ │ │ ├── graphql_endpoint.config.yaml
│ │ │ │ └── query_country.graphql
│ │ │ └── project_2/
│ │ │ ├── graphql_schema.config.yaml
│ │ │ ├── queries/
│ │ │ │ └── query_country.graphql
│ │ │ └── schema_country.graphql
│ │ ├── schema-gen/
│ │ │ ├── project_1/
│ │ │ │ ├── Ballerina.toml
│ │ │ │ └── main.bal
│ │ │ └── service.bal
│ │ └── service-gen/
│ │ ├── schema_book.graphql
│ │ └── schema_starwars.graphql
│ ├── grpc/
│ │ ├── expected-files/
│ │ │ └── route_guide_pb.bal
│ │ └── proto-files/
│ │ └── route_guide.proto
│ ├── openapi/
│ │ ├── expected/
│ │ │ ├── client.bal
│ │ │ ├── filtered_tags.bal
│ │ │ ├── types.bal
│ │ │ └── utils.bal
│ │ ├── integration-tests/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── testFiles/
│ │ │ ├── openapi-validator-off.bal
│ │ │ ├── openapi-validator-on.bal
│ │ │ ├── openapi_validator_off.yaml
│ │ │ └── openapi_validator_on.yaml
│ │ ├── openapi_client.yaml
│ │ ├── petstore.bal
│ │ ├── petstore.yaml
│ │ └── petstoreTags.yaml
│ ├── testing-line-length.xml
│ └── testng.xml
├── ballerina-test-automation/
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── installer-test/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── test/
│ │ ├── java/
│ │ │ └── io/
│ │ │ └── ballerina/
│ │ │ └── installer/
│ │ │ └── test/
│ │ │ ├── CentralTest.java
│ │ │ ├── InstallerTest.java
│ │ │ ├── ProjectTest.java
│ │ │ └── TestUtils.java
│ │ └── resources/
│ │ └── testng.xml
│ ├── settings.gradle
│ ├── test-automation/
│ │ ├── build.gradle
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── io/
│ │ └── ballerina/
│ │ └── test/
│ │ ├── CentOS.java
│ │ ├── Executor.java
│ │ ├── MacOS.java
│ │ ├── Ubuntu.java
│ │ ├── Utils.java
│ │ └── Windows.java
│ └── update-tool-test/
│ ├── build.gradle
│ └── src/
│ └── test/
│ ├── java/
│ │ └── io/
│ │ └── ballerina/
│ │ └── tool/
│ │ └── test/
│ │ ├── FetchDependencyTest.java
│ │ ├── UpdateDistTest.java
│ │ └── UpdateToolTest.java
│ └── resources/
│ └── testng.xml
├── build-time-tests/
│ ├── build.gradle
│ ├── samples/
│ │ ├── build-samples.sh
│ │ ├── helloservice/
│ │ │ ├── Ballerina.toml
│ │ │ └── service.bal
│ │ └── helloworld/
│ │ ├── Ballerina.toml
│ │ └── helloworld.bal
│ └── src/
│ └── main/
│ └── java/
│ └── org/
│ └── ballerina/
│ └── packages/
│ └── buildtime/
│ └── GenerateBuildDataCsv.java
├── build.gradle
├── cache-generator/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ ├── io/
│ │ │ └── ballerina/
│ │ │ └── gencache/
│ │ │ └── cmd/
│ │ │ └── GenCacheCmd.java
│ │ └── module-info.java
│ └── resources/
│ └── META-INF/
│ └── services/
│ └── io.ballerina.cli.BLauncherCmd
├── config/
│ └── checkstyle/
│ ├── build.gradle
│ ├── checkstyle.xml
│ └── suppressions.xml
├── devtools-integration-tests/
│ ├── build.gradle
│ ├── index.json
│ ├── src/
│ │ └── test/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── ballerina/
│ │ │ └── devtools/
│ │ │ └── debug/
│ │ │ ├── BaseTestCase.java
│ │ │ ├── ExpressionEvaluationTest.java
│ │ │ ├── ModuleBreakpointTest.java
│ │ │ └── ServiceDebugTest.java
│ │ └── resources/
│ │ ├── debug/
│ │ │ ├── project-based-tests/
│ │ │ │ ├── breakpoint-tests/
│ │ │ │ │ ├── Ballerina.toml
│ │ │ │ │ └── main.bal
│ │ │ │ ├── evaluation-tests/
│ │ │ │ │ ├── Ballerina.toml
│ │ │ │ │ └── main.bal
│ │ │ │ └── service-tests/
│ │ │ │ ├── Ballerina.toml
│ │ │ │ ├── hello_service.bal
│ │ │ │ └── tests/
│ │ │ │ └── hello_service_test.bal
│ │ │ └── single-file-tests/
│ │ │ └── hello_service.bal
│ │ └── testng.xml
│ └── testerina-object-mocking-test/
│ ├── client.bal
│ ├── service.bal
│ └── tests/
│ └── main_test.bal
├── dist-repo-builder/
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── io/
│ └── ballerina/
│ └── dist/
│ └── DistRepoBuilder.java
├── doc-guidelines.md
├── docs/
│ └── build-ballerina-from-source.md
├── examples/
│ ├── access-json-elements/
│ │ ├── access_json_elements.bal
│ │ ├── access_json_elements.md
│ │ ├── access_json_elements.metatags
│ │ └── access_json_elements.out
│ ├── access-optional-json-elements/
│ │ ├── access_optional_json_elements.bal
│ │ ├── access_optional_json_elements.md
│ │ ├── access_optional_json_elements.metatags
│ │ └── access_optional_json_elements.out
│ ├── advanced-conflict-handling/
│ │ ├── advanced_conflict_handling.bal
│ │ ├── advanced_conflict_handling.md
│ │ ├── advanced_conflict_handling.metatags
│ │ └── advanced_conflict_handling.out
│ ├── aggregation/
│ │ ├── aggregation.bal
│ │ ├── aggregation.md
│ │ ├── aggregation.metatags
│ │ └── aggregation.out
│ ├── ai-agent-external-endpoint-integration/
│ │ ├── ai_agent_external_endpoint_integration.bal
│ │ ├── ai_agent_external_endpoint_integration.md
│ │ ├── ai_agent_external_endpoint_integration.metatags
│ │ └── ai_agent_external_endpoint_integration.out
│ ├── ai-agent-local-tools/
│ │ ├── ai_agent_local_tools.bal
│ │ ├── ai_agent_local_tools.md
│ │ ├── ai_agent_local_tools.metatags
│ │ └── ai_agent_local_tools.out
│ ├── ai-agent-mcp-integration/
│ │ ├── ai_agent_mcp_integration.bal
│ │ ├── ai_agent_mcp_integration.md
│ │ ├── ai_agent_mcp_integration.metatags
│ │ └── ai_agent_mcp_integration.out
│ ├── ai-agent-tool-kit/
│ │ ├── ai_agent_tool_kit.bal
│ │ ├── ai_agent_tool_kit.md
│ │ ├── ai_agent_tool_kit.metatags
│ │ └── ai_agent_tool_kit.out
│ ├── alternate-receive/
│ │ ├── alternate_receive.bal
│ │ ├── alternate_receive.md
│ │ ├── alternate_receive.metatags
│ │ └── alternate_receive.out
│ ├── alternate-wait/
│ │ ├── alternate_wait.bal
│ │ ├── alternate_wait.md
│ │ ├── alternate_wait.metatags
│ │ └── alternate_wait.out
│ ├── annotations/
│ │ ├── annotations.bal
│ │ ├── annotations.md
│ │ ├── annotations.metatags
│ │ └── annotations.out
│ ├── anonymous-function/
│ │ ├── anonymous_function.bal
│ │ ├── anonymous_function.md
│ │ ├── anonymous_function.metatags
│ │ └── anonymous_function.out
│ ├── any-type/
│ │ ├── any_type.bal
│ │ ├── any_type.md
│ │ ├── any_type.metatags
│ │ └── any_type.out
│ ├── anydata-to-yaml-string/
│ │ ├── anydata_to_yaml_string.bal
│ │ ├── anydata_to_yaml_string.md
│ │ ├── anydata_to_yaml_string.metatags
│ │ └── anydata_to_yaml_string.out
│ ├── anydata-type/
│ │ ├── anydata_type.bal
│ │ ├── anydata_type.md
│ │ ├── anydata_type.metatags
│ │ └── anydata_type.out
│ ├── array-map-symmetry/
│ │ ├── array_map_symmetry.bal
│ │ ├── array_map_symmetry.md
│ │ ├── array_map_symmetry.metatags
│ │ └── array_map_symmetry.out
│ ├── arrays/
│ │ ├── arrays.bal
│ │ ├── arrays.md
│ │ ├── arrays.metatags
│ │ └── arrays.out
│ ├── asynchronize-message-passing/
│ │ ├── asynchronize_message_passing.bal
│ │ ├── asynchronize_message_passing.md
│ │ ├── asynchronize_message_passing.metatags
│ │ └── asynchronize_message_passing.out
│ ├── asynchronous-function-calls/
│ │ ├── asynchronous_function_calls.bal
│ │ ├── asynchronous_function_calls.md
│ │ ├── asynchronous_function_calls.metatags
│ │ └── asynchronous_function_calls.out
│ ├── avro-serdes/
│ │ ├── avro_serdes.bal
│ │ ├── avro_serdes.md
│ │ ├── avro_serdes.metatags
│ │ └── avro_serdes.out
│ ├── aws-lambda-dynamodb-trigger/
│ │ ├── aws_deploy.out
│ │ ├── aws_lambda_dynamodb_trigger.bal
│ │ ├── aws_lambda_dynamodb_trigger.md
│ │ ├── aws_lambda_dynamodb_trigger.metatags
│ │ ├── bal_build.out
│ │ └── bal_new.out
│ ├── aws-lambda-execution-context/
│ │ ├── aws_deploy.out
│ │ ├── aws_lambda_execution_context.bal
│ │ ├── aws_lambda_execution_context.md
│ │ ├── aws_lambda_execution_context.metatags
│ │ ├── bal_build.out
│ │ ├── bal_new.out
│ │ └── invoke_functions.out
│ ├── aws-lambda-hello-world/
│ │ ├── aws_deploy.out
│ │ ├── aws_lambda_hello_world.bal
│ │ ├── aws_lambda_hello_world.md
│ │ ├── aws_lambda_hello_world.metatags
│ │ ├── bal_build.out
│ │ ├── bal_new.out
│ │ └── invoke_functions.out
│ ├── aws-lambda-s3-trigger/
│ │ ├── aws_deploy.out
│ │ ├── aws_lambda_s3_trigger.bal
│ │ ├── aws_lambda_s3_trigger.md
│ │ ├── aws_lambda_s3_trigger.metatags
│ │ ├── bal_build.out
│ │ └── bal_new.out
│ ├── azure-functions-cosmosdb-trigger/
│ │ ├── az_deploy.out
│ │ ├── azure_functions_cosmosdb_trigger.bal
│ │ ├── azure_functions_cosmosdb_trigger.md
│ │ ├── azure_functions_cosmosdb_trigger.metatags
│ │ ├── bal_build.out
│ │ └── bal_new.out
│ ├── azure-functions-hello-world/
│ │ ├── az_deploy.out
│ │ ├── azure_functions_hello_world.bal
│ │ ├── azure_functions_hello_world.md
│ │ ├── azure_functions_hello_world.metatags
│ │ ├── bal_build.out
│ │ ├── bal_new.out
│ │ └── execute_function.out
│ ├── azure-functions-http-trigger-with-queue/
│ │ ├── az_deploy.out
│ │ ├── azure_functions_http_trigger_with_queue.bal
│ │ ├── azure_functions_http_trigger_with_queue.md
│ │ ├── azure_functions_http_trigger_with_queue.metatags
│ │ ├── bal_build.out
│ │ ├── bal_new.out
│ │ └── execute_function.out
│ ├── azure-functions-timer-trigger/
│ │ ├── az_deploy.out
│ │ ├── azure_functions_timer_trigger.bal
│ │ ├── azure_functions_timer_trigger.md
│ │ ├── azure_functions_timer_trigger.metatags
│ │ ├── bal_build.out
│ │ └── bal_new.out
│ ├── binary-data/
│ │ ├── binary_data.bal
│ │ ├── binary_data.md
│ │ ├── binary_data.metatags
│ │ └── binary_data.out
│ ├── binary-operators/
│ │ ├── binary_operators.bal
│ │ ├── binary_operators.md
│ │ ├── binary_operators.metatags
│ │ └── binary_operators.out
│ ├── binding-patterns/
│ │ ├── binding_patterns.bal
│ │ ├── binding_patterns.md
│ │ ├── binding_patterns.metatags
│ │ └── binding_patterns.out
│ ├── binding-patterns-in-match-statement/
│ │ ├── binding_patterns_in_match_statement.bal
│ │ ├── binding_patterns_in_match_statement.md
│ │ ├── binding_patterns_in_match_statement.metatags
│ │ └── binding_patterns_in_match_statement.out
│ ├── boolean/
│ │ ├── boolean.bal
│ │ ├── boolean.md
│ │ ├── boolean.metatags
│ │ └── boolean.out
│ ├── break-statement/
│ │ ├── break_statement.bal
│ │ ├── break_statement.md
│ │ ├── break_statement.metatags
│ │ └── break_statement.out
│ ├── built-in-integer-subtypes/
│ │ ├── built_in_integer_subtypes.bal
│ │ ├── built_in_integer_subtypes.md
│ │ ├── built_in_integer_subtypes.metatags
│ │ └── built_in_integer_subtypes.out
│ ├── built-in-string-subtype/
│ │ ├── built_in_string_subtype.bal
│ │ ├── built_in_string_subtype.md
│ │ ├── built_in_string_subtype.metatags
│ │ └── built_in_string_subtype.out
│ ├── byte-type/
│ │ ├── byte_type.bal
│ │ ├── byte_type.md
│ │ ├── byte_type.metatags
│ │ └── byte_type.out
│ ├── c2c-docker-deployment/
│ │ ├── Cloud.toml
│ │ ├── build_output.out
│ │ ├── c2c_docker_deployment.bal
│ │ ├── c2c_docker_deployment.md
│ │ ├── docker_images.out
│ │ ├── docker_run.out
│ │ └── execute_curl.out
│ ├── c2c-k8s-deployment/
│ │ ├── Cloud.toml
│ │ ├── build_output.out
│ │ ├── c2c_k8s_deployment.bal
│ │ ├── c2c_k8s_deployment.md
│ │ ├── docker_push.out
│ │ ├── execute_curl.out
│ │ ├── kubectl_apply.out
│ │ ├── kubectl_expose.out
│ │ ├── kubectl_pods.out
│ │ ├── kubectl_svc.out
│ │ └── minikube_ip.out
│ ├── cache-basics/
│ │ ├── cache_basics.bal
│ │ ├── cache_basics.md
│ │ ├── cache_basics.metatags
│ │ ├── cache_basics.out
│ │ └── tests/
│ │ └── cache_test.bal
│ ├── cache-invalidation/
│ │ ├── cache_invalidation.bal
│ │ ├── cache_invalidation.md
│ │ ├── cache_invalidation.metatags
│ │ ├── cache_invalidation.out
│ │ └── tests/
│ │ └── cache_test.bal
│ ├── casting-json-to-user-defined-type/
│ │ ├── casting_json_to_user_defined_type.bal
│ │ ├── casting_json_to_user_defined_type.md
│ │ ├── casting_json_to_user_defined_type.metatags
│ │ └── casting_json_to_user_defined_type.out
│ ├── cdc-advanced-service/
│ │ ├── cdc_advanced_service.bal
│ │ ├── cdc_advanced_service.md
│ │ ├── cdc_advanced_service.metatags
│ │ └── cdc_advanced_service.out
│ ├── cdc-prerequisite/
│ │ ├── README.md
│ │ ├── setup_finance_db.bal
│ │ ├── setup_store_db.bal
│ │ ├── test_cdc_advanced_service.bal
│ │ └── test_cdc_service.bal
│ ├── cdc-service/
│ │ ├── cdc_service.bal
│ │ ├── cdc_service.md
│ │ ├── cdc_service.metatags
│ │ └── cdc_service.out
│ ├── chat-agents/
│ │ ├── chat_agents.bal
│ │ ├── chat_agents.md
│ │ ├── chat_agents.metatags
│ │ └── chat_agents.out
│ ├── check-expression/
│ │ ├── check_expression.bal
│ │ ├── check_expression.md
│ │ ├── check_expression.metatags
│ │ └── check_expression.out
│ ├── check-semantics/
│ │ ├── check_semantics.bal
│ │ ├── check_semantics.md
│ │ ├── check_semantics.metatags
│ │ └── check_semantics.out
│ ├── child-loggers-with-context/
│ │ ├── child_loggers_with_context.bal
│ │ ├── child_loggers_with_context.md
│ │ ├── child_loggers_with_context.metatags
│ │ └── child_loggers_with_context.out
│ ├── client-class/
│ │ ├── client_class.bal
│ │ ├── client_class.md
│ │ ├── client_class.metatags
│ │ └── client_class.out
│ ├── combining-isolated-functions-and-lock/
│ │ ├── combining_isolated_functions_and_lock.bal
│ │ ├── combining_isolated_functions_and_lock.md
│ │ ├── combining_isolated_functions_and_lock.metatags
│ │ └── combining_isolated_functions_and_lock.out
│ ├── commit-rollback-handlers/
│ │ ├── commit_rollback_handlers.bal
│ │ ├── commit_rollback_handlers.md
│ │ ├── commit_rollback_handlers.metatags
│ │ └── commit_rollback_handlers.out
│ ├── computed-field-key/
│ │ ├── computed_field_key.bal
│ │ ├── computed_field_key.md
│ │ ├── computed_field_key.metatags
│ │ └── computed_field_key.out
│ ├── conditional-send/
│ │ ├── conditional_send.bal
│ │ ├── conditional_send.md
│ │ ├── conditional_send.metatags
│ │ └── conditional_send.out
│ ├── configurable-variables/
│ │ ├── configurable_variables.bal
│ │ ├── configurable_variables.md
│ │ ├── configurable_variables.metatags
│ │ └── configurable_variables.out
│ ├── configuring-via-cli/
│ │ ├── configuring_via_cli.bal
│ │ ├── configuring_via_cli.md
│ │ ├── configuring_via_cli.metatags
│ │ └── configuring_via_cli.out
│ ├── configuring-via-toml/
│ │ ├── configuring-via-toml.md
│ │ ├── configuring_via_toml.bal
│ │ ├── configuring_via_toml.metatags
│ │ └── configuring_via_toml.out
│ ├── const-and-final/
│ │ ├── const_and_final.bal
│ │ ├── const_and_final.md
│ │ ├── const_and_final.metatags
│ │ └── const_and_final.out
│ ├── constraint-validations/
│ │ ├── constraint_validations.bal
│ │ ├── constraint_validations.md
│ │ ├── constraint_validations.metatags
│ │ └── constraint_validations.out
│ ├── consuming-services/
│ │ ├── consuming_services.bal
│ │ ├── consuming_services.md
│ │ ├── consuming_services.metatags
│ │ └── consuming_services.out
│ ├── continue-statement/
│ │ ├── continue_statement.bal
│ │ ├── continue_statement.md
│ │ ├── continue_statement.metatags
│ │ └── continue_statement.out
│ ├── controlling-openness/
│ │ ├── controlling_openness.bal
│ │ ├── controlling_openness.md
│ │ ├── controlling_openness.metatags
│ │ └── controlling_openness.out
│ ├── convert-from-json-to-user-defined-type/
│ │ ├── convert_from_json_to_user_defined_type.bal
│ │ ├── convert_from_json_to_user_defined_type.md
│ │ ├── convert_from_json_to_user_defined_type.metatags
│ │ └── convert_from_json_to_user_defined_type.out
│ ├── converting-from-table-and-xml-to-json/
│ │ ├── converting_from_table_and_xml_to_json.bal
│ │ ├── converting_from_table_and_xml_to_json.md
│ │ ├── converting_from_table_and_xml_to_json.metatags
│ │ └── converting_from_table_and_xml_to_json.out
│ ├── converting-from-user-defined-type-to-json/
│ │ ├── converting_from_user_defined_type_to_json.bal
│ │ ├── converting_from_user_defined_type_to_json.md
│ │ ├── converting_from_user_defined_type_to_json.metatags
│ │ └── converting_from_user_defined_type_to_json.out
│ ├── counter-metrics/
│ │ ├── counter_metrics.bal
│ │ ├── counter_metrics.client.out
│ │ ├── counter_metrics.md
│ │ ├── counter_metrics.metatags
│ │ ├── counter_metrics.server.out
│ │ └── tests/
│ │ └── counter_metrics_test.bal
│ ├── covariance/
│ │ ├── covariance.bal
│ │ ├── covariance.md
│ │ ├── covariance.metatags
│ │ └── covariance.out
│ ├── create-maps-with-query/
│ │ ├── create_maps_with_query.bal
│ │ ├── create_maps_with_query.md
│ │ ├── create_maps_with_query.metatags
│ │ └── create_maps_with_query.out
│ ├── create-streams-with-query/
│ │ ├── create_streams_with_query.bal
│ │ ├── create_streams_with_query.md
│ │ ├── create_streams_with_query.metatags
│ │ └── create_streams_with_query.out
│ ├── create-tables-with-query/
│ │ ├── create_tables_with_query.bal
│ │ ├── create_tables_with_query.md
│ │ ├── create_tables_with_query.metatags
│ │ └── create_tables_with_query.out
│ ├── csv-streams-to-record-array/
│ │ ├── csv_streams_to_record_array.bal
│ │ ├── csv_streams_to_record_array.md
│ │ ├── csv_streams_to_record_array.metatags
│ │ └── csv_streams_to_record_array.out
│ ├── csv-string-to-anydata-array/
│ │ ├── csv_string_to_anydata_array.bal
│ │ ├── csv_string_to_anydata_array.md
│ │ ├── csv_string_to_anydata_array.metatags
│ │ └── csv_string_to_anydata_array.out
│ ├── csv-string-to-record-array/
│ │ ├── csv_string_to_record_array.bal
│ │ ├── csv_string_to_record_array.md
│ │ ├── csv_string_to_record_array.metatags
│ │ └── csv_string_to_record_array.out
│ ├── csv-user-configurations/
│ │ ├── csv_user_configurations.bal
│ │ ├── csv_user_configurations.md
│ │ ├── csv_user_configurations.metatags
│ │ └── csv_user_configurations.out
│ ├── custom-logger/
│ │ ├── custom_logger.bal
│ │ ├── custom_logger.md
│ │ ├── custom_logger.metatags
│ │ └── custom_logger.out
│ ├── custom-prefetch-methods/
│ │ ├── custom_prefetch_methods.bal
│ │ ├── custom_prefetch_methods.client.out
│ │ ├── custom_prefetch_methods.graphql
│ │ ├── custom_prefetch_methods.md
│ │ ├── custom_prefetch_methods.metatags
│ │ └── custom_prefetch_methods.server.out
│ ├── decimal-type/
│ │ ├── decimal_type.bal
│ │ ├── decimal_type.md
│ │ ├── decimal_type.metatags
│ │ └── decimal_type.out
│ ├── default-values-for-function-parameters/
│ │ ├── default_values_for_function_parameters.bal
│ │ ├── default_values_for_function_parameters.md
│ │ ├── default_values_for_function_parameters.metatags
│ │ └── default_values_for_function_parameters.out
│ ├── default-values-for-record-fields/
│ │ ├── default_values_for_record_fields.bal
│ │ ├── default_values_for_record_fields.md
│ │ ├── default_values_for_record_fields.metatags
│ │ └── default_values_for_record_fields.out
│ ├── defining-classes/
│ │ ├── defining_classes.bal
│ │ ├── defining_classes.md
│ │ ├── defining_classes.metatags
│ │ └── defining_classes.out
│ ├── dependent-types/
│ │ ├── dependent_types.bal
│ │ ├── dependent_types.md
│ │ ├── dependent_types.metatags
│ │ └── dependent_types.out
│ ├── destructure-records-using-query/
│ │ ├── destructure_records_using_query.bal
│ │ ├── destructure_records_using_query.md
│ │ ├── destructure_records_using_query.metatags
│ │ └── destructure_records_using_query.out
│ ├── direct-llm-calls/
│ │ ├── direct_llm_calls.bal
│ │ ├── direct_llm_calls.md
│ │ ├── direct_llm_calls.metatags
│ │ └── direct_llm_calls.out
│ ├── direct-llm-calls-with-history/
│ │ ├── direct_llm_calls_with_history.bal
│ │ ├── direct_llm_calls_with_history.md
│ │ ├── direct_llm_calls_with_history.metatags
│ │ └── direct_llm_calls_with_history.out
│ ├── direct-llm-calls-with-multimodal-input/
│ │ ├── direct_llm_calls_with_multimodal_input.bal
│ │ ├── direct_llm_calls_with_multimodal_input.md
│ │ ├── direct_llm_calls_with_multimodal_input.metatags
│ │ └── direct_llm_calls_with_multimodal_input.out
│ ├── directories/
│ │ ├── directories.bal
│ │ ├── directories.md
│ │ ├── directories.metatags
│ │ └── directories.out
│ ├── directory-listener/
│ │ ├── directory_listener.bal
│ │ ├── directory_listener.md
│ │ ├── directory_listener.metatags
│ │ └── directory_listener.out
│ ├── distinct-object-types/
│ │ ├── distinct_object_types.bal
│ │ ├── distinct_object_types.md
│ │ ├── distinct_object_types.metatags
│ │ └── distinct_object_types.out
│ ├── docker-hello-world/
│ │ ├── Cloud.toml
│ │ ├── build_output.out
│ │ ├── docker_hello_world.bal
│ │ ├── docker_hello_world.md
│ │ ├── docker_images.out
│ │ ├── docker_run.out
│ │ └── execute_curl.out
│ ├── documentation/
│ │ ├── documentation.bal
│ │ ├── documentation.md
│ │ ├── documentation.metatags
│ │ └── documentation.out
│ ├── dynamic-listener/
│ │ ├── dynamic_listener.bal
│ │ ├── dynamic_listener.client.out
│ │ ├── dynamic_listener.md
│ │ ├── dynamic_listener.metatags
│ │ └── dynamic_listener.server.out
│ ├── edi-to-record/
│ │ ├── bal_project.out
│ │ ├── codegen_command.out
│ │ ├── edi_to_record.bal
│ │ ├── edi_to_record.md
│ │ ├── edi_to_record.metatags
│ │ ├── order.edi
│ │ ├── output.out
│ │ ├── package_structure.out
│ │ ├── schema.json
│ │ └── tool_pull_command.out
│ ├── email-client-ssl-tls/
│ │ ├── email_client_ssl_tls.bal
│ │ ├── email_client_ssl_tls.md
│ │ ├── email_client_ssl_tls.metatags
│ │ └── email_client_ssl_tls.out
│ ├── email-service-ssl-tls/
│ │ ├── email_service_ssl_tls.bal
│ │ ├── email_service_ssl_tls.md
│ │ ├── email_service_ssl_tls.metatags
│ │ └── email_service_ssl_tls.out
│ ├── ensureType-function/
│ │ ├── ensureType_function.bal
│ │ ├── ensureType_function.md
│ │ ├── ensureType_function.metatags
│ │ └── ensureType_function.out
│ ├── enumerations/
│ │ ├── enumerations.bal
│ │ ├── enumerations.md
│ │ ├── enumerations.metatags
│ │ └── enumerations.out
│ ├── environment-variables/
│ │ ├── environment_variables.bal
│ │ ├── environment_variables.md
│ │ ├── environment_variables.metatags
│ │ └── environment_variables.out
│ ├── error-binding-pattern/
│ │ ├── error_binding_pattern.bal
│ │ ├── error_binding_pattern.md
│ │ ├── error_binding_pattern.metatags
│ │ └── error_binding_pattern.out
│ ├── error-binding-pattern-in-match-statement/
│ │ ├── error_binding_pattern_in_match_statement.bal
│ │ ├── error_binding_pattern_in_match_statement.md
│ │ ├── error_binding_pattern_in_match_statement.metatags
│ │ └── error_binding_pattern_in_match_statement.out
│ ├── error-cause/
│ │ ├── error_cause.bal
│ │ ├── error_cause.md
│ │ ├── error_cause.metatags
│ │ └── error_cause.out
│ ├── error-detail/
│ │ ├── error_detail.bal
│ │ ├── error_detail.md
│ │ ├── error_detail.metatags
│ │ └── error_detail.out
│ ├── error-handling/
│ │ ├── error_handling.bal
│ │ ├── error_handling.md
│ │ ├── error_handling.metatags
│ │ └── error_handling.out
│ ├── error-logging/
│ │ ├── error_logging.bal
│ │ ├── error_logging.md
│ │ ├── error_logging.metatags
│ │ └── error_logging.out
│ ├── error-reporting/
│ │ ├── error_reporting.bal
│ │ ├── error_reporting.md
│ │ ├── error_reporting.metatags
│ │ └── error_reporting.out
│ ├── error-subtyping/
│ │ ├── error_subtyping.bal
│ │ ├── error_subtyping.md
│ │ ├── error_subtyping.metatags
│ │ └── error_subtyping.out
│ ├── error-type-intersection/
│ │ ├── error_type_intersection.bal
│ │ ├── error_type_intersection.md
│ │ ├── error_type_intersection.metatags
│ │ └── error_type_intersection.out
│ ├── expression-equality/
│ │ ├── expression_equality.bal
│ │ ├── expression_equality.md
│ │ ├── expression_equality.metatags
│ │ └── expression_equality.out
│ ├── expression-oriented-style/
│ │ ├── expression_oriented_style.bal
│ │ ├── expression_oriented_style.md
│ │ ├── expression_oriented_style.metatags
│ │ └── expression_oriented_style.out
│ ├── filepaths/
│ │ ├── filepaths.bal
│ │ ├── filepaths.md
│ │ ├── filepaths.metatags
│ │ ├── filepaths.out
│ │ └── tests/
│ │ └── filepath_test.bal
│ ├── files/
│ │ ├── files.bal
│ │ ├── files.md
│ │ ├── files.metatags
│ │ └── files.out
│ ├── filler-values-of-a-list/
│ │ ├── filler_values_of_a_list.bal
│ │ ├── filler_values_of_a_list.md
│ │ ├── filler_values_of_a_list.metatags
│ │ └── filler_values_of_a_list.out
│ ├── floating-point-numbers/
│ │ ├── floating_point_numbers.bal
│ │ ├── floating_point_numbers.md
│ │ ├── floating_point_numbers.metatags
│ │ └── floating_point_numbers.out
│ ├── flush/
│ │ ├── flush.bal
│ │ ├── flush.md
│ │ ├── flush.metatags
│ │ └── flush.out
│ ├── foreach-statement/
│ │ ├── foreach_statement.bal
│ │ ├── foreach_statement.md
│ │ ├── foreach_statement.metatags
│ │ └── foreach_statement.out
│ ├── fork/
│ │ ├── fork.bal
│ │ ├── fork.md
│ │ ├── fork.metatags
│ │ └── fork.out
│ ├── ftp-client-receive-file/
│ │ ├── ftp_client_receive_file.bal
│ │ ├── ftp_client_receive_file.md
│ │ ├── ftp_client_receive_file.metatags
│ │ └── ftp_client_receive_file.out
│ ├── ftp-client-send-file/
│ │ ├── ftp_client_send_file.bal
│ │ ├── ftp_client_send_file.md
│ │ ├── ftp_client_send_file.metatags
│ │ └── ftp_client_send_file.out
│ ├── ftp-service-receive-file/
│ │ ├── ftp_service_receive_file.bal
│ │ ├── ftp_service_receive_file.md
│ │ ├── ftp_service_receive_file.metatags
│ │ └── ftp_service_receive_file.out
│ ├── ftp-service-send-file/
│ │ ├── ftp_service_send_file.bal
│ │ ├── ftp_service_send_file.md
│ │ ├── ftp_service_send_file.metatags
│ │ └── ftp_service_send_file.out
│ ├── function-closure/
│ │ ├── function_closure.bal
│ │ ├── function_closure.md
│ │ ├── function_closure.metatags
│ │ └── function_closure.out
│ ├── function-pointers/
│ │ ├── function_pointers.bal
│ │ ├── function_pointers.md
│ │ ├── function_pointers.metatags
│ │ └── function_pointers.out
│ ├── function-types/
│ │ ├── function_types.bal
│ │ ├── function_types.md
│ │ ├── function_types.metatags
│ │ └── function_types.out
│ ├── function-values/
│ │ ├── function_values.bal
│ │ ├── function_values.md
│ │ ├── function_values.metatags
│ │ └── function_values.out
│ ├── functions/
│ │ ├── functions.bal
│ │ ├── functions.md
│ │ ├── functions.metatags
│ │ └── functions.out
│ ├── gauge-metrics/
│ │ ├── gauge_metrics.bal
│ │ ├── gauge_metrics.client.out
│ │ ├── gauge_metrics.md
│ │ ├── gauge_metrics.metatags
│ │ ├── gauge_metrics.server.out
│ │ └── tests/
│ │ └── gauge_metrics_test.bal
│ ├── graphql-client-error-handling/
│ │ ├── graphql_client_error_handling.bal
│ │ ├── graphql_client_error_handling.md
│ │ ├── graphql_client_error_handling.metatags
│ │ └── graphql_client_error_handling.out
│ ├── graphql-client-handle-partial-response/
│ │ ├── graphql_client_handle_partial_response.bal
│ │ ├── graphql_client_handle_partial_response.md
│ │ ├── graphql_client_handle_partial_response.metatags
│ │ └── graphql_client_handle_partial_response.out
│ ├── graphql-client-query-endpoint/
│ │ ├── graphql_client_query_endpoint.bal
│ │ ├── graphql_client_query_endpoint.md
│ │ ├── graphql_client_query_endpoint.metatags
│ │ └── graphql_client_query_endpoint.out
│ ├── graphql-client-security-basic-auth/
│ │ ├── graphql_client_security_basic_auth.bal
│ │ ├── graphql_client_security_basic_auth.md
│ │ ├── graphql_client_security_basic_auth.metatags
│ │ └── graphql_client_security_basic_auth.out
│ ├── graphql-client-security-jwt-authentication/
│ │ ├── graphql_client_security_jwt_authentication.bal
│ │ ├── graphql_client_security_jwt_authentication.md
│ │ ├── graphql_client_security_jwt_authentication.metatags
│ │ └── graphql_client_security_jwt_authentication.out
│ ├── graphql-client-security-mutual-ssl/
│ │ ├── graphql_client_security_mutual_ssl.bal
│ │ ├── graphql_client_security_mutual_ssl.md
│ │ ├── graphql_client_security_mutual_ssl.metatags
│ │ └── graphql_client_security_mutual_ssl.out
│ ├── graphql-client-security-oauth2-password-grant-type/
│ │ ├── graphql_client_security_oauth2_password_grant_type.bal
│ │ ├── graphql_client_security_oauth2_password_grant_type.md
│ │ ├── graphql_client_security_oauth2_password_grant_type.metatags
│ │ └── graphql_client_security_oauth2_password_grant_type.out
│ ├── graphql-client-security-ssl-tls/
│ │ ├── graphql_client_security_ssl_tls.bal
│ │ ├── graphql_client_security_ssl_tls.md
│ │ ├── graphql_client_security_ssl_tls.metatags
│ │ └── graphql_client_security_ssl_tls.out
│ ├── graphql-context/
│ │ ├── graphql_context.1.client.out
│ │ ├── graphql_context.2.client.out
│ │ ├── graphql_context.bal
│ │ ├── graphql_context.graphql
│ │ ├── graphql_context.md
│ │ ├── graphql_context.metatags
│ │ └── graphql_context.server.out
│ ├── graphql-dataloader/
│ │ ├── graphql_dataloader.bal
│ │ ├── graphql_dataloader.client.out
│ │ ├── graphql_dataloader.graphql
│ │ ├── graphql_dataloader.md
│ │ ├── graphql_dataloader.metatags
│ │ └── graphql_dataloader.server.out
│ ├── graphql-directives/
│ │ ├── graphql_directives.1.client.out
│ │ ├── graphql_directives.1.graphql
│ │ ├── graphql_directives.2.client.out
│ │ ├── graphql_directives.2.graphql
│ │ ├── graphql_directives.3.client.out
│ │ ├── graphql_directives.3.graphql
│ │ ├── graphql_directives.bal
│ │ ├── graphql_directives.md
│ │ ├── graphql_directives.metatags
│ │ └── graphql_directives.server.out
│ ├── graphql-documentation/
│ │ ├── graphql_documentation.bal
│ │ ├── graphql_documentation.client.out
│ │ ├── graphql_documentation.graphql
│ │ ├── graphql_documentation.md
│ │ ├── graphql_documentation.metatags
│ │ └── graphql_documentation.server.out
│ ├── graphql-field-interceptors/
│ │ ├── graphql_field_interceptors.bal
│ │ ├── graphql_field_interceptors.client.out
│ │ ├── graphql_field_interceptors.graphql
│ │ ├── graphql_field_interceptors.md
│ │ ├── graphql_field_interceptors.metatags
│ │ └── graphql_field_interceptors.server.out
│ ├── graphql-file-upload/
│ │ ├── graphql_file_upload.bal
│ │ ├── graphql_file_upload.client.out
│ │ ├── graphql_file_upload.md
│ │ ├── graphql_file_upload.metatags
│ │ └── graphql_file_upload.server.out
│ ├── graphql-graphiql/
│ │ ├── graphql_graphiql.bal
│ │ ├── graphql_graphiql.md
│ │ ├── graphql_graphiql.metatags
│ │ └── graphql_graphiql.out
│ ├── graphql-hello-world/
│ │ ├── graphql_hello_world.bal
│ │ ├── graphql_hello_world.client.out
│ │ ├── graphql_hello_world.graphql
│ │ ├── graphql_hello_world.md
│ │ ├── graphql_hello_world.metatags
│ │ └── graphql_hello_world.server.out
│ ├── graphql-hierarchical-resource-paths/
│ │ ├── graphql_hierarchical_resource_paths.bal
│ │ ├── graphql_hierarchical_resource_paths.client.out
│ │ ├── graphql_hierarchical_resource_paths.graphql
│ │ ├── graphql_hierarchical_resource_paths.md
│ │ ├── graphql_hierarchical_resource_paths.metatags
│ │ └── graphql_hierarchical_resource_paths.server.out
│ ├── graphql-id-scalar-type/
│ │ ├── graphql_id_scalar_type.bal
│ │ ├── graphql_id_scalar_type.client.out
│ │ ├── graphql_id_scalar_type.graphql
│ │ ├── graphql_id_scalar_type.md
│ │ ├── graphql_id_scalar_type.metatags
│ │ └── graphql_id_scalar_type.server.out
│ ├── graphql-input-constraint-validation/
│ │ ├── graphql_input_constraint_validation.bal
│ │ ├── graphql_input_constraint_validation.client.out
│ │ ├── graphql_input_constraint_validation.graphql
│ │ ├── graphql_input_constraint_validation.md
│ │ ├── graphql_input_constraint_validation.metatags
│ │ └── graphql_input_constraint_validation.server.out
│ ├── graphql-input-objects/
│ │ ├── graphql_input_objects.bal
│ │ ├── graphql_input_objects.client.out
│ │ ├── graphql_input_objects.graphql
│ │ ├── graphql_input_objects.md
│ │ ├── graphql_input_objects.metatags
│ │ └── graphql_input_objects.server.out
│ ├── graphql-input-types/
│ │ ├── graphql_input_types.bal
│ │ ├── graphql_input_types.client.out
│ │ ├── graphql_input_types.graphql
│ │ ├── graphql_input_types.md
│ │ ├── graphql_input_types.metatags
│ │ └── graphql_input_types.server.out
│ ├── graphql-interceptor-configurations/
│ │ ├── graphql_interceptor_configurations.bal
│ │ ├── graphql_interceptor_configurations.client.out
│ │ ├── graphql_interceptor_configurations.graphql
│ │ ├── graphql_interceptor_configurations.md
│ │ ├── graphql_interceptor_configurations.metatags
│ │ └── graphql_interceptor_configurations.server.out
│ ├── graphql-interfaces/
│ │ ├── graphql_interfaces.bal
│ │ ├── graphql_interfaces.client.out
│ │ ├── graphql_interfaces.graphql
│ │ ├── graphql_interfaces.md
│ │ ├── graphql_interfaces.metatags
│ │ └── graphql_interfaces.server.out
│ ├── graphql-interfaces-implementing-interfaces/
│ │ ├── graphql_interfaces_implementing_interfaces.bal
│ │ ├── graphql_interfaces_implementing_interfaces.client.out
│ │ ├── graphql_interfaces_implementing_interfaces.graphql
│ │ ├── graphql_interfaces_implementing_interfaces.md
│ │ ├── graphql_interfaces_implementing_interfaces.metatags
│ │ └── graphql_interfaces_implementing_interfaces.server.out
│ ├── graphql-mutations/
│ │ ├── graphql_mutations.bal
│ │ ├── graphql_mutations.client.out
│ │ ├── graphql_mutations.graphql
│ │ ├── graphql_mutations.md
│ │ ├── graphql_mutations.metatags
│ │ └── graphql_mutations.server.out
│ ├── graphql-returning-record-values/
│ │ ├── graphql_returning_record_values.bal
│ │ ├── graphql_returning_record_values.client.out
│ │ ├── graphql_returning_record_values.graphql
│ │ ├── graphql_returning_record_values.md
│ │ ├── graphql_returning_record_values.metatags
│ │ └── graphql_returning_record_values.server.out
│ ├── graphql-returning-service-objects/
│ │ ├── graphql_returning_service_objects.bal
│ │ ├── graphql_returning_service_objects.client.out
│ │ ├── graphql_returning_service_objects.graphql
│ │ ├── graphql_returning_service_objects.md
│ │ ├── graphql_returning_service_objects.metatags
│ │ └── graphql_returning_service_objects.server.out
│ ├── graphql-service-basic-auth-file-user-store/
│ │ ├── graphql_service_basic_auth_file_user_store.bal
│ │ ├── graphql_service_basic_auth_file_user_store.md
│ │ ├── graphql_service_basic_auth_file_user_store.metatags
│ │ └── graphql_service_basic_auth_file_user_store.server.out
│ ├── graphql-service-basic-auth-ldap-user-store/
│ │ ├── graphql_service_basic_auth_ldap_user_store.bal
│ │ ├── graphql_service_basic_auth_ldap_user_store.md
│ │ ├── graphql_service_basic_auth_ldap_user_store.metatags
│ │ └── graphql_service_basic_auth_ldap_user_store.server.out
│ ├── graphql-service-cache-invalidation/
│ │ ├── graphql_service_cache_invalidation.bal
│ │ ├── graphql_service_cache_invalidation.client.out
│ │ ├── graphql_service_cache_invalidation.graphql
│ │ ├── graphql_service_cache_invalidation.md
│ │ ├── graphql_service_cache_invalidation.metatags
│ │ └── graphql_service_cache_invalidation.server.out
│ ├── graphql-service-error-handling/
│ │ ├── graphql_service_error_handling.1.client.out
│ │ ├── graphql_service_error_handling.1.graphql
│ │ ├── graphql_service_error_handling.2.client.out
│ │ ├── graphql_service_error_handling.2.graphql
│ │ ├── graphql_service_error_handling.bal
│ │ ├── graphql_service_error_handling.md
│ │ ├── graphql_service_error_handling.metatags
│ │ └── graphql_service_error_handling.server.out
│ ├── graphql-service-field-level-caching/
│ │ ├── graphql_service_field_level_caching.bal
│ │ ├── graphql_service_field_level_caching.client.out
│ │ ├── graphql_service_field_level_caching.graphql
│ │ ├── graphql_service_field_level_caching.md
│ │ ├── graphql_service_field_level_caching.metatags
│ │ └── graphql_service_field_level_caching.server.out
│ ├── graphql-service-field-object/
│ │ ├── graphql_service_field_object.1.client.out
│ │ ├── graphql_service_field_object.1.graphql
│ │ ├── graphql_service_field_object.2.client.out
│ │ ├── graphql_service_field_object.2.graphql
│ │ ├── graphql_service_field_object.bal
│ │ ├── graphql_service_field_object.md
│ │ ├── graphql_service_field_object.metatags
│ │ ├── graphql_service_field_object.server.1.out
│ │ ├── graphql_service_field_object.server.2.out
│ │ └── graphql_service_field_object.server.3.out
│ ├── graphql-service-interceptors/
│ │ ├── graphql_service_interceptors.bal
│ │ ├── graphql_service_interceptors.client.out
│ │ ├── graphql_service_interceptors.graphql
│ │ ├── graphql_service_interceptors.md
│ │ ├── graphql_service_interceptors.metatags
│ │ └── graphql_service_interceptors.server.out
│ ├── graphql-service-jwt-auth/
│ │ ├── graphql_service_jwt_auth.bal
│ │ ├── graphql_service_jwt_auth.md
│ │ ├── graphql_service_jwt_auth.metatags
│ │ └── graphql_service_jwt_auth.server.out
│ ├── graphql-service-mutual-ssl/
│ │ ├── graphql_service_mutual_ssl.bal
│ │ ├── graphql_service_mutual_ssl.md
│ │ ├── graphql_service_mutual_ssl.metatags
│ │ └── graphql_service_mutual_ssl.server.out
│ ├── graphql-service-oauth2/
│ │ ├── graphql_service_oauth2.bal
│ │ ├── graphql_service_oauth2.md
│ │ ├── graphql_service_oauth2.metatags
│ │ └── graphql_service_oauth2.server.out
│ ├── graphql-service-operation-level-caching/
│ │ ├── graphql_service_operation_level_caching.bal
│ │ ├── graphql_service_operation_level_caching.client.out
│ │ ├── graphql_service_operation_level_caching.graphql
│ │ ├── graphql_service_operation_level_caching.md
│ │ ├── graphql_service_operation_level_caching.metatags
│ │ └── graphql_service_operation_level_caching.server.out
│ ├── graphql-service-query-complexity/
│ │ ├── graphql_service_query_complexity.1.graphql
│ │ ├── graphql_service_query_complexity.2.graphql
│ │ ├── graphql_service_query_complexity.bal
│ │ ├── graphql_service_query_complexity.client.1.out
│ │ ├── graphql_service_query_complexity.client.2.out
│ │ ├── graphql_service_query_complexity.md
│ │ ├── graphql_service_query_complexity.metatags
│ │ └── graphql_service_query_complexity.server.out
│ ├── graphql-service-ssl-tls/
│ │ ├── graphql_service_ssl_tls.bal
│ │ ├── graphql_service_ssl_tls.md
│ │ ├── graphql_service_ssl_tls.metatags
│ │ └── graphql_service_ssl_tls.server.out
│ ├── graphql-service-union-types/
│ │ ├── graphql_service_union_types.bal
│ │ ├── graphql_service_union_types.client.out
│ │ ├── graphql_service_union_types.graphql
│ │ ├── graphql_service_union_types.md
│ │ ├── graphql_service_union_types.metatags
│ │ └── graphql_service_union_types.server.out
│ ├── graphql-subscriptions/
│ │ ├── graphql_subscriptions.bal
│ │ ├── graphql_subscriptions.client.out
│ │ ├── graphql_subscriptions.graphql
│ │ ├── graphql_subscriptions.md
│ │ ├── graphql_subscriptions.metatags
│ │ └── graphql_subscriptions.server.out
│ ├── grpc-client-basic-auth/
│ │ ├── grpc_client_basic_auth.bal
│ │ ├── grpc_client_basic_auth.md
│ │ ├── grpc_client_basic_auth.metatags
│ │ ├── grpc_client_basic_auth.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-bearer-token-auth/
│ │ ├── grpc_client_bearer_token_auth.bal
│ │ ├── grpc_client_bearer_token_auth.md
│ │ ├── grpc_client_bearer_token_auth.metatags
│ │ ├── grpc_client_bearer_token_auth.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-bidirectional-streaming/
│ │ ├── grpc_bidirectional_streaming.md
│ │ ├── grpc_bidirectional_streaming.metatags
│ │ ├── grpc_bidirectional_streaming.out
│ │ ├── grpc_bidirectional_streaming.proto
│ │ ├── grpc_bidirectional_streaming_service_client.bal
│ │ └── grpc_bidirectional_streaming_service_client.out
│ ├── grpc-client-client-streaming/
│ │ ├── grpc_client_streaming.md
│ │ ├── grpc_client_streaming.metatags
│ │ ├── grpc_client_streaming.out
│ │ ├── grpc_client_streaming.proto
│ │ ├── grpc_client_streaming_service_client.bal
│ │ └── grpc_client_streaming_service_client.out
│ ├── grpc-client-headers/
│ │ ├── grpc_simple.proto
│ │ ├── grpc_simple_with_headers.md
│ │ ├── grpc_simple_with_headers.metatags
│ │ ├── grpc_simple_with_headers_service_client.bal
│ │ └── grpc_simple_with_headers_service_client.out
│ ├── grpc-client-mutual-ssl/
│ │ ├── grpc_client_mutual_ssl.bal
│ │ ├── grpc_client_mutual_ssl.md
│ │ ├── grpc_client_mutual_ssl.metatags
│ │ ├── grpc_client_mutual_ssl.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-oauth2-client-credentials-grant-type/
│ │ ├── grpc_client_oauth2_client_credentials_grant_type.bal
│ │ ├── grpc_client_oauth2_client_credentials_grant_type.md
│ │ ├── grpc_client_oauth2_client_credentials_grant_type.metatags
│ │ ├── grpc_client_oauth2_client_credentials_grant_type.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-oauth2-jwt-bearer-grant-type/
│ │ ├── grpc_client_oauth2_jwt_bearer_grant_type.bal
│ │ ├── grpc_client_oauth2_jwt_bearer_grant_type.md
│ │ ├── grpc_client_oauth2_jwt_bearer_grant_type.metatags
│ │ ├── grpc_client_oauth2_jwt_bearer_grant_type.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-oauth2-password-grant-type/
│ │ ├── grpc_client_oauth2_password_grant_type.bal
│ │ ├── grpc_client_oauth2_password_grant_type.md
│ │ ├── grpc_client_oauth2_password_grant_type.metatags
│ │ ├── grpc_client_oauth2_password_grant_type.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-oauth2-refresh-token-grant-type/
│ │ ├── grpc_client_oauth2_refresh_token_grant_type.bal
│ │ ├── grpc_client_oauth2_refresh_token_grant_type.md
│ │ ├── grpc_client_oauth2_refresh_token_grant_type.metatags
│ │ ├── grpc_client_oauth2_refresh_token_grant_type.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-self-signed-jwt-auth/
│ │ ├── grpc_client_self_signed_jwt_auth.bal
│ │ ├── grpc_client_self_signed_jwt_auth.md
│ │ ├── grpc_client_self_signed_jwt_auth.metatags
│ │ ├── grpc_client_self_signed_jwt_auth.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-server-streaming/
│ │ ├── grpc_server_streaming.md
│ │ ├── grpc_server_streaming.metatags
│ │ ├── grpc_server_streaming.out
│ │ ├── grpc_server_streaming.proto
│ │ ├── grpc_server_streaming_service_client.bal
│ │ └── grpc_server_streaming_service_client.out
│ ├── grpc-client-set-deadline/
│ │ ├── grpc_client_set_deadline.bal
│ │ ├── grpc_client_set_deadline.md
│ │ ├── grpc_client_set_deadline.metatags
│ │ ├── grpc_client_set_deadline.out
│ │ └── grpc_simple.proto
│ ├── grpc-client-simple/
│ │ ├── grpc_client_simple.bal
│ │ ├── grpc_client_simple.md
│ │ ├── grpc_client_simple.metatags
│ │ ├── grpc_client_simple.out
│ │ ├── grpc_client_simple.proto
│ │ └── grpc_simple.out
│ ├── grpc-client-ssl-tls/
│ │ ├── grpc_client_ssl_tls.bal
│ │ ├── grpc_client_ssl_tls.md
│ │ ├── grpc_client_ssl_tls.metatags
│ │ ├── grpc_client_ssl_tls.out
│ │ └── grpc_simple.proto
│ ├── grpc-server-reflection/
│ │ ├── grpc_server_reflection.bal
│ │ ├── grpc_server_reflection.md
│ │ ├── grpc_server_reflection.metatags
│ │ ├── grpc_server_reflection.out
│ │ ├── grpc_server_reflection_grpcurl.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-basic-auth-file-user-store/
│ │ ├── grpc_service_basic_auth_file_user_store.bal
│ │ ├── grpc_service_basic_auth_file_user_store.md
│ │ ├── grpc_service_basic_auth_file_user_store.metatags
│ │ ├── grpc_service_basic_auth_file_user_store.server.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-basic-auth-ldap-user-store/
│ │ ├── grpc_service_basic_auth_ldap_user_store.bal
│ │ ├── grpc_service_basic_auth_ldap_user_store.md
│ │ ├── grpc_service_basic_auth_ldap_user_store.metatags
│ │ ├── grpc_service_basic_auth_ldap_user_store.server.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-bidirectional-streaming/
│ │ ├── grpc_bidirectional_streaming.md
│ │ ├── grpc_bidirectional_streaming.metatags
│ │ ├── grpc_bidirectional_streaming.out
│ │ ├── grpc_bidirectional_streaming.proto
│ │ ├── grpc_bidirectional_streaming_service.bal
│ │ ├── grpc_bidirectional_streaming_service.out
│ │ └── tests/
│ │ └── grpc_bidirectional_streaming_test.bal
│ ├── grpc-service-check-deadline/
│ │ ├── grpc_service_check_deadline.bal
│ │ ├── grpc_service_check_deadline.md
│ │ ├── grpc_service_check_deadline.metatags
│ │ ├── grpc_service_check_deadline.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-client-streaming/
│ │ ├── grpc_client_streaming.md
│ │ ├── grpc_client_streaming.metatags
│ │ ├── grpc_client_streaming.out
│ │ ├── grpc_client_streaming.proto
│ │ ├── grpc_client_streaming_service.bal
│ │ ├── grpc_client_streaming_service.out
│ │ └── tests/
│ │ └── grpc_client_streaming_test.bal
│ ├── grpc-service-headers/
│ │ ├── grpc_simple.proto
│ │ ├── grpc_simple_with_headers.md
│ │ ├── grpc_simple_with_headers.metatags
│ │ ├── grpc_simple_with_headers_service.bal
│ │ ├── grpc_simple_with_headers_service.out
│ │ └── tests/
│ │ └── grpc_simple_with_headers_test.bal
│ ├── grpc-service-jwt-auth/
│ │ ├── grpc_service_jwt_auth.bal
│ │ ├── grpc_service_jwt_auth.md
│ │ ├── grpc_service_jwt_auth.metatags
│ │ ├── grpc_service_jwt_auth.server.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-mutual-ssl/
│ │ ├── grpc_service_mutual_ssl.bal
│ │ ├── grpc_service_mutual_ssl.md
│ │ ├── grpc_service_mutual_ssl.metatags
│ │ ├── grpc_service_mutual_ssl.server.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-oauth2/
│ │ ├── grpc_service_oauth2.bal
│ │ ├── grpc_service_oauth2.md
│ │ ├── grpc_service_oauth2.metatags
│ │ ├── grpc_service_oauth2.server.out
│ │ └── grpc_simple.proto
│ ├── grpc-service-server-streaming/
│ │ ├── grpc_server_streaming.md
│ │ ├── grpc_server_streaming.metatags
│ │ ├── grpc_server_streaming.out
│ │ ├── grpc_server_streaming.proto
│ │ ├── grpc_server_streaming_service.bal
│ │ ├── grpc_server_streaming_service.out
│ │ └── tests/
│ │ └── grpc_server_streaming_test.bal
│ ├── grpc-service-simple/
│ │ ├── grpc_service_simple.bal
│ │ ├── grpc_service_simple.md
│ │ ├── grpc_service_simple.metatags
│ │ ├── grpc_service_simple.out
│ │ ├── grpc_service_simple.proto
│ │ ├── grpc_simple.out
│ │ └── tests/
│ │ └── grpc_simple_test.bal
│ ├── grpc-service-ssl-tls/
│ │ ├── grpc_service_ssl_tls.bal
│ │ ├── grpc_service_ssl_tls.md
│ │ ├── grpc_service_ssl_tls.metatags
│ │ ├── grpc_service_ssl_tls.server.out
│ │ └── grpc_simple.proto
│ ├── hello-world/
│ │ ├── hello_world.bal
│ │ ├── hello_world.md
│ │ ├── hello_world.metatags
│ │ └── hello_world.out
│ ├── hello-world-service/
│ │ ├── hello_world_service.bal
│ │ ├── hello_world_service.client.out
│ │ ├── hello_world_service.md
│ │ ├── hello_world_service.metatags
│ │ └── hello_world_service.server.out
│ ├── hierarchical-resources/
│ │ ├── hierarchical_resources.bal
│ │ ├── hierarchical_resources.client.out
│ │ ├── hierarchical_resources.md
│ │ ├── hierarchical_resources.metatags
│ │ └── hierarchical_resources.server.out
│ ├── http-1-1-to-2-0-protocol-switch/
│ │ └── http_1_1_to_2_0_protocol_switch.md
│ ├── http-100-continue/
│ │ ├── http_100_continue.bal
│ │ ├── http_100_continue.client.out
│ │ ├── http_100_continue.md
│ │ ├── http_100_continue.metatags
│ │ ├── http_100_continue.server.out
│ │ └── tests/
│ │ └── http_100_continue_test.bal
│ ├── http-2-0-client-server-push/
│ │ ├── http_2_0_client_server_push.bal
│ │ ├── http_2_0_client_server_push.md
│ │ ├── http_2_0_client_server_push.metatags
│ │ └── http_2_0_client_server_push.out
│ ├── http-2-0-server-push/
│ │ ├── http_2_0_server_push.bal
│ │ ├── http_2_0_server_push.md
│ │ ├── http_2_0_server_push.metatags
│ │ └── http_2_0_server_push.out
│ ├── http-2-prior-knowledge-client/
│ │ ├── http_2_prior_knowledge_client.bal
│ │ ├── http_2_prior_knowledge_client.md
│ │ ├── http_2_prior_knowledge_client.metatags
│ │ └── http_2_prior_knowledge_client.out
│ ├── http-2-to-1-1-downgrade-client/
│ │ ├── http_2_to_1_1_downgrade_client.bal
│ │ ├── http_2_to_1_1_downgrade_client.md
│ │ ├── http_2_to_1_1_downgrade_client.metatags
│ │ └── http_2_to_1_1_downgrade_client.out
│ ├── http-2-to-1-1-downgrade-service/
│ │ ├── http_2_to_1_1_downgrade_service.bal
│ │ ├── http_2_to_1_1_downgrade_service.client.out
│ │ ├── http_2_to_1_1_downgrade_service.md
│ │ ├── http_2_to_1_1_downgrade_service.metatags
│ │ ├── http_2_to_1_1_downgrade_service.server.out
│ │ └── tests/
│ │ └── http_2_0_to_1_1_downgrade_service_test.bal
│ ├── http-access-logs/
│ │ ├── http_access_logs.bal
│ │ ├── http_access_logs.client.out
│ │ ├── http_access_logs.md
│ │ ├── http_access_logs.metatags
│ │ └── http_access_logs.server.out
│ ├── http-basic-rest-service/
│ │ ├── http_basic_rest_service.bal
│ │ ├── http_basic_rest_service.client.1.out
│ │ ├── http_basic_rest_service.client.2.out
│ │ ├── http_basic_rest_service.md
│ │ ├── http_basic_rest_service.metatags
│ │ ├── http_basic_rest_service.server.out
│ │ └── tests/
│ │ └── http_basic_rest_service_test.bal
│ ├── http-caching-client/
│ │ ├── http_caching_client.bal
│ │ ├── http_caching_client.md
│ │ ├── http_caching_client.metatags
│ │ ├── http_caching_client.out
│ │ └── tests/
│ │ └── http_caching_client_test.bal
│ ├── http-caller/
│ │ ├── http_caller.bal
│ │ ├── http_caller.client.out
│ │ ├── http_caller.md
│ │ ├── http_caller.metatags
│ │ ├── http_caller.server.out
│ │ └── tests/
│ │ └── http_caller_test.bal
│ ├── http-circuit-breaker/
│ │ ├── http_circuit_breaker.bal
│ │ ├── http_circuit_breaker.md
│ │ ├── http_circuit_breaker.metatags
│ │ ├── http_circuit_breaker.out
│ │ └── tests/
│ │ └── http_circuit_breaker_test.bal
│ ├── http-client-basic-authentication/
│ │ ├── http_client_basic_authentication.bal
│ │ ├── http_client_basic_authentication.md
│ │ ├── http_client_basic_authentication.metatags
│ │ └── http_client_basic_authentication.out
│ ├── http-client-bearer-token-authentication/
│ │ ├── http_client_bearer_token_authentication.bal
│ │ ├── http_client_bearer_token_authentication.md
│ │ ├── http_client_bearer_token_authentication.metatags
│ │ └── http_client_bearer_token_authentication.out
│ ├── http-client-chunking/
│ │ ├── http_client_chunking.bal
│ │ ├── http_client_chunking.md
│ │ ├── http_client_chunking.metatags
│ │ ├── http_client_chunking.out
│ │ └── tests/
│ │ └── http_client_chunking_test.bal
│ ├── http-client-data-binding/
│ │ ├── http_client_data_binding.bal
│ │ ├── http_client_data_binding.md
│ │ ├── http_client_data_binding.metatags
│ │ └── http_client_data_binding.out
│ ├── http-client-file-upload/
│ │ ├── http_client_file_upload.bal
│ │ ├── http_client_file_upload.md
│ │ ├── http_client_file_upload.metatags
│ │ └── http_client_file_upload.out
│ ├── http-client-header-parameter/
│ │ ├── http_client_header_parameter.bal
│ │ ├── http_client_header_parameter.md
│ │ ├── http_client_header_parameter.metatags
│ │ ├── http_client_header_parameter.out
│ │ └── http_client_header_parameter_post.bal
│ ├── http-client-mutual-ssl/
│ │ ├── http_client_mutual_ssl.bal
│ │ ├── http_client_mutual_ssl.md
│ │ ├── http_client_mutual_ssl.metatags
│ │ └── http_client_mutual_ssl.out
│ ├── http-client-oauth2-client-credentials-grant-type/
│ │ ├── http_client_oauth2_client_credentials_grant_type.bal
│ │ ├── http_client_oauth2_client_credentials_grant_type.md
│ │ ├── http_client_oauth2_client_credentials_grant_type.metatags
│ │ └── http_client_oauth2_client_credentials_grant_type.out
│ ├── http-client-oauth2-jwt-bearer-grant-type/
│ │ ├── http_client_oauth2_jwt_bearer_grant_type.bal
│ │ ├── http_client_oauth2_jwt_bearer_grant_type.md
│ │ ├── http_client_oauth2_jwt_bearer_grant_type.metatags
│ │ └── http_client_oauth2_jwt_bearer_grant_type.out
│ ├── http-client-oauth2-password-grant-type/
│ │ ├── http_client_oauth2_password_grant_type.bal
│ │ ├── http_client_oauth2_password_grant_type.md
│ │ ├── http_client_oauth2_password_grant_type.metatags
│ │ └── http_client_oauth2_password_grant_type.out
│ ├── http-client-oauth2-refresh-token-grant-type/
│ │ ├── http_client_oauth2_refresh_token_grant_type.bal
│ │ ├── http_client_oauth2_refresh_token_grant_type.md
│ │ ├── http_client_oauth2_refresh_token_grant_type.metatags
│ │ └── http_client_oauth2_refresh_token_grant_type.out
│ ├── http-client-path-parameter/
│ │ ├── http_client_path_parameter.bal
│ │ ├── http_client_path_parameter.md
│ │ ├── http_client_path_parameter.metatags
│ │ └── http_client_path_parameter.out
│ ├── http-client-payload-constraint-validation/
│ │ ├── http_client_payload_constraint_validation.bal
│ │ ├── http_client_payload_constraint_validation.md
│ │ ├── http_client_payload_constraint_validation.metatags
│ │ └── http_client_payload_constraint_validation.out
│ ├── http-client-query-parameter/
│ │ ├── http_client_query_parameter.bal
│ │ ├── http_client_query_parameter.md
│ │ ├── http_client_query_parameter.metatags
│ │ ├── http_client_query_parameter.out
│ │ └── http_client_query_parameter_post.bal
│ ├── http-client-redirects/
│ │ ├── http_client_redirects.bal
│ │ ├── http_client_redirects.md
│ │ ├── http_client_redirects.metatags
│ │ ├── http_client_redirects.out
│ │ └── tests/
│ │ └── http_client_redirects_test.bal
│ ├── http-client-self-signed-jwt-authentication/
│ │ ├── http_client_self_signed_jwt_authentication.bal
│ │ ├── http_client_self_signed_jwt_authentication.md
│ │ ├── http_client_self_signed_jwt_authentication.metatags
│ │ └── http_client_self_signed_jwt_authentication.out
│ ├── http-client-send-request-receive-response/
│ │ ├── http_client_send_request_receive_response.bal
│ │ ├── http_client_send_request_receive_response.md
│ │ ├── http_client_send_request_receive_response.metatags
│ │ ├── http_client_send_request_receive_response.out
│ │ └── tests/
│ │ └── http_client_send_request_receive_response_test.bal
│ ├── http-client-ssl-tls/
│ │ ├── http_client_ssl_tls.bal
│ │ ├── http_client_ssl_tls.md
│ │ ├── http_client_ssl_tls.metatags
│ │ └── http_client_ssl_tls.out
│ ├── http-compression/
│ │ ├── http_compression.bal
│ │ ├── http_compression.client.out
│ │ ├── http_compression.md
│ │ ├── http_compression.metatags
│ │ ├── http_compression.server.out
│ │ └── tests/
│ │ └── http_compression_test.bal
│ ├── http-cookies-client/
│ │ ├── http_cookies_client.bal
│ │ ├── http_cookies_client.md
│ │ ├── http_cookies_client.metatags
│ │ └── http_cookies_client.out
│ ├── http-cookies-service/
│ │ ├── http_cookies_service.bal
│ │ ├── http_cookies_service.md
│ │ ├── http_cookies_service.metatags
│ │ └── http_cookies_service.out
│ ├── http-cors/
│ │ ├── http_cors.bal
│ │ ├── http_cors.client.out
│ │ ├── http_cors.md
│ │ ├── http_cors.metatags
│ │ ├── http_cors.server.out
│ │ └── tests/
│ │ └── http_cors_test.bal
│ ├── http-default-error-handling/
│ │ ├── http_default_error_handling.bal
│ │ ├── http_default_error_handling.client.out
│ │ ├── http_default_error_handling.md
│ │ ├── http_default_error_handling.metatags
│ │ ├── http_default_error_handling.server.out
│ │ └── tests/
│ │ └── http_default_error_handling_test.bal
│ ├── http-default-resource/
│ │ ├── http_default_resource.bal
│ │ ├── http_default_resource.client.out
│ │ ├── http_default_resource.md
│ │ ├── http_default_resource.metatags
│ │ ├── http_default_resource.server.out
│ │ └── tests/
│ │ └── http_default_resource_test.bal
│ ├── http-error-handling/
│ │ ├── http_error_handling.bal
│ │ ├── http_error_handling.client.out
│ │ ├── http_error_handling.md
│ │ ├── http_error_handling.metatags
│ │ ├── http_error_handling.server.out
│ │ └── tests/
│ │ └── http_error_handling_test.bal
│ ├── http-failover/
│ │ ├── http_failover.bal
│ │ ├── http_failover.md
│ │ ├── http_failover.metatags
│ │ ├── http_failover.out
│ │ └── tests/
│ │ └── http_failover_test.bal
│ ├── http-header-param/
│ │ ├── http_header_param.bal
│ │ ├── http_header_param.client.out
│ │ ├── http_header_param.md
│ │ ├── http_header_param.metatags
│ │ ├── http_header_param.server.out
│ │ └── tests/
│ │ └── http_header_param_test.bal
│ ├── http-interceptor-error-handling/
│ │ ├── http_interceptor_error_handling.bal
│ │ ├── http_interceptor_error_handling.client.out
│ │ ├── http_interceptor_error_handling.md
│ │ ├── http_interceptor_error_handling.metatags
│ │ ├── http_interceptor_error_handling.server.out
│ │ └── tests/
│ │ └── http_interceptor_error_handling_test.bal
│ ├── http-load-balancer/
│ │ ├── http_load_balancer.bal
│ │ ├── http_load_balancer.md
│ │ ├── http_load_balancer.metatags
│ │ ├── http_load_balancer.out
│ │ └── tests/
│ │ └── http_load_balancer_test.bal
│ ├── http-matrix-param/
│ │ ├── http_matrix_param.bal
│ │ ├── http_matrix_param.client.out
│ │ ├── http_matrix_param.md
│ │ ├── http_matrix_param.metatags
│ │ ├── http_matrix_param.server.out
│ │ └── tests/
│ │ └── http_matrix_param_test.bal
│ ├── http-passthrough/
│ │ ├── http_passthrough.bal
│ │ ├── http_passthrough.client.out
│ │ ├── http_passthrough.md
│ │ ├── http_passthrough.metatags
│ │ ├── http_passthrough.server.out
│ │ └── tests/
│ │ └── http_passthrough_test.bal
│ ├── http-path-param/
│ │ ├── http_path_param.bal
│ │ ├── http_path_param.client.out
│ │ ├── http_path_param.md
│ │ ├── http_path_param.metatags
│ │ ├── http_path_param.server.out
│ │ └── tests/
│ │ └── http_path_param_test.bal
│ ├── http-query-parameter/
│ │ ├── http_query_parameter.bal
│ │ ├── http_query_parameter.client.out
│ │ ├── http_query_parameter.md
│ │ ├── http_query_parameter.metatags
│ │ ├── http_query_parameter.server.out
│ │ └── tests/
│ │ └── http_query_parameter_test.bal
│ ├── http-request-interceptor/
│ │ ├── http_request_interceptor.bal
│ │ ├── http_request_interceptor.client.out
│ │ ├── http_request_interceptor.md
│ │ ├── http_request_interceptor.metatags
│ │ ├── http_request_interceptor.server.out
│ │ └── tests/
│ │ └── http_request_interceptor_test.bal
│ ├── http-request-response/
│ │ ├── http_request_response.bal
│ │ ├── http_request_response.client.out
│ │ ├── http_request_response.md
│ │ ├── http_request_response.metatags
│ │ ├── http_request_response.server.out
│ │ └── tests/
│ │ └── http_request_response_test.bal
│ ├── http-request-with-multiparts/
│ │ ├── files/
│ │ │ └── test.xml
│ │ ├── http_request_with_multiparts.1.client.out
│ │ ├── http_request_with_multiparts.2.client.out
│ │ ├── http_request_with_multiparts.bal
│ │ ├── http_request_with_multiparts.md
│ │ ├── http_request_with_multiparts.metatags
│ │ ├── http_request_with_multiparts.server.out
│ │ └── tests/
│ │ └── http_request_with_multiparts_test.bal
│ ├── http-response-interceptor/
│ │ ├── http_response_interceptor.bal
│ │ ├── http_response_interceptor.client.out
│ │ ├── http_response_interceptor.md
│ │ ├── http_response_interceptor.metatags
│ │ ├── http_response_interceptor.server.out
│ │ └── tests/
│ │ └── http_response_interceptor_test.bal
│ ├── http-response-with-multiparts/
│ │ ├── files/
│ │ │ └── test.xml
│ │ ├── http_response_with_multiparts.1.client.out
│ │ ├── http_response_with_multiparts.2.client.out
│ │ ├── http_response_with_multiparts.bal
│ │ ├── http_response_with_multiparts.md
│ │ ├── http_response_with_multiparts.metatags
│ │ ├── http_response_with_multiparts.server.out
│ │ └── tests/
│ │ └── http_response_with_multiparts_test.bal
│ ├── http-restrict-by-media-type/
│ │ ├── http_restrict_by_media_type.bal
│ │ ├── http_restrict_by_media_type.client.out
│ │ ├── http_restrict_by_media_type.md
│ │ ├── http_restrict_by_media_type.metatags
│ │ ├── http_restrict_by_media_type.server.out
│ │ └── tests/
│ │ └── http_restrict_by_media_type_test.bal
│ ├── http-retry/
│ │ ├── http_retry.bal
│ │ ├── http_retry.md
│ │ ├── http_retry.metatags
│ │ ├── http_retry.out
│ │ └── tests/
│ │ └── http_retry_test.bal
│ ├── http-send-different-status-codes/
│ │ ├── http_send_different_status_codes.bal
│ │ ├── http_send_different_status_codes.client.out
│ │ ├── http_send_different_status_codes.md
│ │ ├── http_send_different_status_codes.metatags
│ │ ├── http_send_different_status_codes.server.out
│ │ └── tests/
│ │ └── http_send_different_status_codes_test.bal
│ ├── http-send-different-status-codes-with-payload/
│ │ ├── http_send_different_status_codes_with_payload.bal
│ │ ├── http_send_different_status_codes_with_payload.client.out
│ │ ├── http_send_different_status_codes_with_payload.md
│ │ ├── http_send_different_status_codes_with_payload.metatags
│ │ ├── http_send_different_status_codes_with_payload.server.out
│ │ └── tests/
│ │ └── http_send_different_status_codes_with_payload_test.bal
│ ├── http-send-header/
│ │ ├── http_send_header.bal
│ │ ├── http_send_header.client.out
│ │ ├── http_send_header.md
│ │ ├── http_send_header.metatags
│ │ ├── http_send_header.server.out
│ │ └── tests/
│ │ └── http_send_header_test.bal
│ ├── http-send-response/
│ │ ├── http_send_response.bal
│ │ ├── http_send_response.client.out
│ │ ├── http_send_response.md
│ │ ├── http_send_response.metatags
│ │ ├── http_send_response.server.out
│ │ └── tests/
│ │ └── http_send_response_test.bal
│ ├── http-service-and-resource-paths/
│ │ ├── http_service_and_resource_paths.bal
│ │ ├── http_service_and_resource_paths.client.out
│ │ ├── http_service_and_resource_paths.md
│ │ ├── http_service_and_resource_paths.metatags
│ │ ├── http_service_and_resource_paths.server.out
│ │ └── tests/
│ │ └── http_service_and_resource_paths_test.bal
│ ├── http-service-basic-authentication-file-user-store/
│ │ ├── http_service_basic_authentication_file_user_store.bal
│ │ ├── http_service_basic_authentication_file_user_store.md
│ │ ├── http_service_basic_authentication_file_user_store.metatags
│ │ └── http_service_basic_authentication_file_user_store.server.out
│ ├── http-service-basic-authentication-ldap-user-store/
│ │ ├── http_service_basic_authentication_ldap_user_store.bal
│ │ ├── http_service_basic_authentication_ldap_user_store.md
│ │ ├── http_service_basic_authentication_ldap_user_store.metatags
│ │ └── http_service_basic_authentication_ldap_user_store.server.out
│ ├── http-service-cache-response/
│ │ ├── http_service_cache_response.bal
│ │ ├── http_service_cache_response.md
│ │ ├── http_service_cache_response.metatags
│ │ ├── http_service_cache_response.server.out
│ │ └── tests/
│ │ └── http_service_cache_response_test.bal
│ ├── http-service-chunking/
│ │ ├── http_service_chunking.bal
│ │ ├── http_service_chunking.client.out
│ │ ├── http_service_chunking.md
│ │ ├── http_service_chunking.metatags
│ │ ├── http_service_chunking.server.out
│ │ └── tests/
│ │ └── http_service_chunking_test.bal
│ ├── http-service-data-binding/
│ │ ├── http_service_data_binding.bal
│ │ ├── http_service_data_binding.client.out
│ │ ├── http_service_data_binding.md
│ │ ├── http_service_data_binding.metatags
│ │ ├── http_service_data_binding.server.out
│ │ └── tests/
│ │ └── http_service_data_binding_test.bal
│ ├── http-service-file-upload/
│ │ ├── http_service_file_upload.bal
│ │ ├── http_service_file_upload.md
│ │ ├── http_service_file_upload.metatags
│ │ └── http_service_file_upload.server.out
│ ├── http-service-jwt-authentication/
│ │ ├── http_service_jwt_authentication.bal
│ │ ├── http_service_jwt_authentication.md
│ │ ├── http_service_jwt_authentication.metatags
│ │ └── http_service_jwt_authentication.server.out
│ ├── http-service-mutual-ssl/
│ │ ├── http_service_mutual_ssl.bal
│ │ ├── http_service_mutual_ssl.client.out
│ │ ├── http_service_mutual_ssl.md
│ │ ├── http_service_mutual_ssl.metatags
│ │ └── http_service_mutual_ssl.server.out
│ ├── http-service-oauth2/
│ │ ├── http_service_oauth2.bal
│ │ ├── http_service_oauth2.md
│ │ ├── http_service_oauth2.metatags
│ │ └── http_service_oauth2.server.out
│ ├── http-service-payload-constraint-validation/
│ │ ├── http_service_payload_constraint_validation.bal
│ │ ├── http_service_payload_constraint_validation.client.out
│ │ ├── http_service_payload_constraint_validation.md
│ │ ├── http_service_payload_constraint_validation.metatags
│ │ ├── http_service_payload_constraint_validation.server.out
│ │ └── tests/
│ │ └── http_service_payload_constraint_validation.bal
│ ├── http-service-redirects/
│ │ ├── http_service_redirects.bal
│ │ ├── http_service_redirects.client.out
│ │ ├── http_service_redirects.md
│ │ ├── http_service_redirects.metatags
│ │ ├── http_service_redirects.server.out
│ │ └── tests/
│ │ └── http_service_redirects_test.bal
│ ├── http-service-ssl-tls/
│ │ ├── http_service_ssl_tls.bal
│ │ ├── http_service_ssl_tls.client.out
│ │ ├── http_service_ssl_tls.md
│ │ ├── http_service_ssl_tls.metatags
│ │ └── http_service_ssl_tls.server.out
│ ├── http-sse-client/
│ │ ├── http_sse_client.bal
│ │ ├── http_sse_client.client.out
│ │ ├── http_sse_client.md
│ │ └── http_sse_client.metatags
│ ├── http-sse-service/
│ │ ├── http_sse_service.bal
│ │ ├── http_sse_service.client.out
│ │ ├── http_sse_service.md
│ │ ├── http_sse_service.metatags
│ │ └── http_sse_service.server.out
│ ├── http-timeout/
│ │ ├── http_timeout.bal
│ │ ├── http_timeout.md
│ │ ├── http_timeout.metatags
│ │ ├── http_timeout.out
│ │ └── tests/
│ │ └── http_timeout_test.bal
│ ├── http-trace-logs/
│ │ ├── http_trace_logs.bal
│ │ ├── http_trace_logs.client.out
│ │ ├── http_trace_logs.md
│ │ ├── http_trace_logs.metatags
│ │ └── http_trace_logs.server.out
│ ├── identifiers/
│ │ ├── identifiers.bal
│ │ ├── identifiers.md
│ │ ├── identifiers.metatags
│ │ └── identifiers.out
│ ├── identity/
│ │ ├── identity.bal
│ │ ├── identity.md
│ │ ├── identity.metatags
│ │ └── identity.out
│ ├── if-statement/
│ │ ├── if_statement.bal
│ │ ├── if_statement.md
│ │ ├── if_statement.metatags
│ │ └── if_statement.out
│ ├── ignoring-return-values-and-errors/
│ │ ├── ignoring_return_values_and_errors.bal
│ │ ├── ignoring_return_values_and_errors.md
│ │ ├── ignoring_return_values_and_errors.metatags
│ │ └── ignoring_return_values_and_errors.out
│ ├── immutability/
│ │ ├── immutability.bal
│ │ ├── immutability.md
│ │ ├── immutability.metatags
│ │ └── immutability.out
│ ├── in-memory-message-store/
│ │ ├── in_memory_message_store.bal
│ │ ├── in_memory_message_store.md
│ │ ├── in_memory_message_store.metatags
│ │ └── in_memory_message_store.out
│ ├── included-record-parameters/
│ │ ├── included_record_parameters.bal
│ │ ├── included_record_parameters.md
│ │ ├── included_record_parameters.metatags
│ │ └── included_record_parameters.out
│ ├── index.json
│ ├── inferring-isolated/
│ │ ├── inferring_isolated.bal
│ │ ├── inferring_isolated.md
│ │ ├── inferring_isolated.metatags
│ │ └── inferring_isolated.out
│ ├── init-function/
│ │ ├── init_function.bal
│ │ ├── init_function.md
│ │ ├── init_function.metatags
│ │ └── init_function.out
│ ├── init-return-type/
│ │ ├── init_return_type.bal
│ │ ├── init_return_type.md
│ │ ├── init_return_type.metatags
│ │ └── init_return_type.out
│ ├── int-range/
│ │ ├── int_range.bal
│ │ ├── int_range.md
│ │ ├── int_range.metatags
│ │ └── int_range.out
│ ├── integers/
│ │ ├── integers.bal
│ │ ├── integers.md
│ │ ├── integers.metatags
│ │ └── integers.out
│ ├── inter-worker-failure-propagation/
│ │ ├── inter_worker_failure_propagation.bal
│ │ ├── inter_worker_failure_propagation.md
│ │ ├── inter_worker_failure_propagation.metatags
│ │ └── inter_worker_failure_propagation.out
│ ├── inter-worker-message-passing/
│ │ ├── inter_worker_message_passing.bal
│ │ ├── inter_worker_message_passing.md
│ │ ├── inter_worker_message_passing.metatags
│ │ └── inter_worker_message_passing.out
│ ├── interface-to-external-code/
│ │ ├── interface_to_external_code.bal
│ │ ├── interface_to_external_code.md
│ │ ├── interface_to_external_code.metatags
│ │ └── interface_to_external_code.out
│ ├── io-bytes/
│ │ ├── io_bytes.bal
│ │ ├── io_bytes.md
│ │ ├── io_bytes.metatags
│ │ └── io_bytes.out
│ ├── io-csv/
│ │ ├── io_csv.bal
│ │ ├── io_csv.md
│ │ ├── io_csv.metatags
│ │ └── io_csv.out
│ ├── io-csv-datamapping/
│ │ ├── io_csv_datamapping.bal
│ │ ├── io_csv_datamapping.md
│ │ ├── io_csv_datamapping.metatags
│ │ └── io_csv_datamapping.out
│ ├── io-json/
│ │ ├── io_json.bal
│ │ ├── io_json.md
│ │ ├── io_json.metatags
│ │ └── io_json.out
│ ├── io-strings/
│ │ ├── io_strings.bal
│ │ ├── io_strings.md
│ │ ├── io_strings.metatags
│ │ └── io_strings.out
│ ├── io-xml/
│ │ ├── io_xml.bal
│ │ ├── io_xml.md
│ │ ├── io_xml.metatags
│ │ └── io_xml.out
│ ├── isolated-functions/
│ │ ├── isolated_functions.bal
│ │ ├── isolated_functions.md
│ │ ├── isolated_functions.metatags
│ │ └── isolated_functions.out
│ ├── isolated-methods/
│ │ ├── isolated_methods.bal
│ │ ├── isolated_methods.md
│ │ ├── isolated_methods.metatags
│ │ └── isolated_methods.out
│ ├── isolated-objects/
│ │ ├── isolated_objects.bal
│ │ ├── isolated_objects.md
│ │ ├── isolated_objects.metatags
│ │ └── isolated_objects.out
│ ├── isolated-variables/
│ │ ├── isolated_variables.bal
│ │ ├── isolated_variables.md
│ │ ├── isolated_variables.metatags
│ │ └── isolated_variables.out
│ ├── iterating-over-xml-with-query/
│ │ ├── iterating_over_xml_with_query.bal
│ │ ├── iterating_over_xml_with_query.md
│ │ ├── iterating_over_xml_with_query.metatags
│ │ └── iterating_over_xml_with_query.out
│ ├── iterative-use-of-typed-binding/
│ │ ├── iterative_use_of_typed_binding.bal
│ │ ├── iterative_use_of_typed_binding.md
│ │ ├── iterative_use_of_typed_binding.metatags
│ │ └── iterative_use_of_typed_binding.out
│ ├── jdbc-atomic-transaction/
│ │ └── jdbc_atomic_transaction.out
│ ├── jms-consumer-acknowledgement/
│ │ ├── jms_consumer_acknowledgement.bal
│ │ ├── jms_consumer_acknowledgement.md
│ │ ├── jms_consumer_acknowledgement.metatags
│ │ └── jms_consumer_acknowledgement.out
│ ├── jms-consumer-consume-message/
│ │ ├── jms_consumer_consume_message.bal
│ │ ├── jms_consumer_consume_message.md
│ │ ├── jms_consumer_consume_message.metatags
│ │ └── jms_consumer_consume_message.out
│ ├── jms-producer-produce-message/
│ │ ├── jms_producer_produce_message.bal
│ │ ├── jms_producer_produce_message.curl.out
│ │ ├── jms_producer_produce_message.md
│ │ ├── jms_producer_produce_message.metatags
│ │ └── jms_producer_produce_message.out
│ ├── jms-producer-transaction/
│ │ ├── jms_producer_transaction.bal
│ │ ├── jms_producer_transaction.curl.out
│ │ ├── jms_producer_transaction.md
│ │ ├── jms_producer_transaction.metatags
│ │ └── jms_producer_transaction.out
│ ├── jms-service-consume-message/
│ │ ├── jms_service_consume_message.bal
│ │ ├── jms_service_consume_message.md
│ │ ├── jms_service_consume_message.metatags
│ │ └── jms_service_consume_message.out
│ ├── joining-iterable-objects/
│ │ ├── joining_iterable_objects.bal
│ │ ├── joining_iterable_objects.md
│ │ ├── joining_iterable_objects.metatags
│ │ └── joining_iterable_objects.out
│ ├── json-numbers/
│ │ ├── json_numbers.bal
│ │ ├── json_numbers.md
│ │ ├── json_numbers.metatags
│ │ └── json_numbers.out
│ ├── json-to-record/
│ │ ├── json_to_record.bal
│ │ ├── json_to_record.md
│ │ ├── json_to_record.metatags
│ │ └── json_to_record.out
│ ├── json-to-record-with-projection/
│ │ ├── json_to_record_with_projection.bal
│ │ ├── json_to_record_with_projection.md
│ │ ├── json_to_record_with_projection.metatags
│ │ └── json_to_record_with_projection.out
│ ├── json-type/
│ │ ├── json_type.bal
│ │ ├── json_type.md
│ │ ├── json_type.metatags
│ │ └── json_type.out
│ ├── jsonpath-expressions/
│ │ ├── jsonpath_expressions.bal
│ │ ├── jsonpath_expressions.md
│ │ ├── jsonpath_expressions.metatags
│ │ └── jsonpath_expressions.out
│ ├── kafka-consumer-constraint-validation/
│ │ ├── kafka_consumer_constraint_validation.bal
│ │ ├── kafka_consumer_constraint_validation.md
│ │ ├── kafka_consumer_constraint_validation.metatags
│ │ └── kafka_consumer_constraint_validation.out
│ ├── kafka-consumer-consumer-record-data-binding/
│ │ ├── kafka_consumer_consumer_record_data_binding.bal
│ │ ├── kafka_consumer_consumer_record_data_binding.md
│ │ ├── kafka_consumer_consumer_record_data_binding.metatags
│ │ └── kafka_consumer_consumer_record_data_binding.out
│ ├── kafka-consumer-payload-data-binding/
│ │ ├── kafka_consumer_payload_data_binding.bal
│ │ ├── kafka_consumer_payload_data_binding.md
│ │ ├── kafka_consumer_payload_data_binding.metatags
│ │ └── kafka_consumer_payload_data_binding.out
│ ├── kafka-consumer-sasl/
│ │ ├── kafka_consumer_sasl.bal
│ │ ├── kafka_consumer_sasl.md
│ │ ├── kafka_consumer_sasl.metatags
│ │ └── kafka_consumer_sasl.out
│ ├── kafka-consumer-ssl/
│ │ ├── kafka_consumer_ssl.bal
│ │ ├── kafka_consumer_ssl.md
│ │ ├── kafka_consumer_ssl.metatags
│ │ └── kafka_consumer_ssl.out
│ ├── kafka-producer-produce-message/
│ │ ├── kafka_producer_produce_message.bal
│ │ ├── kafka_producer_produce_message.curl.out
│ │ ├── kafka_producer_produce_message.md
│ │ ├── kafka_producer_produce_message.metatags
│ │ └── kafka_producer_produce_message.out
│ ├── kafka-producer-sasl/
│ │ ├── kafka_producer_sasl.bal
│ │ ├── kafka_producer_sasl.curl.out
│ │ ├── kafka_producer_sasl.md
│ │ ├── kafka_producer_sasl.metatags
│ │ └── kafka_producer_sasl.out
│ ├── kafka-producer-ssl/
│ │ ├── kafka_producer_ssl.bal
│ │ ├── kafka_producer_ssl.curl.out
│ │ ├── kafka_producer_ssl.md
│ │ ├── kafka_producer_ssl.metatags
│ │ └── kafka_producer_ssl.out
│ ├── kafka-service-constraint-validation/
│ │ ├── kafka_service_constraint_validation.bal
│ │ ├── kafka_service_constraint_validation.md
│ │ ├── kafka_service_constraint_validation.metatags
│ │ └── kafka_service_constraint_validation.out
│ ├── kafka-service-consume-message/
│ │ ├── kafka_service_consume_message.bal
│ │ ├── kafka_service_consume_message.md
│ │ ├── kafka_service_consume_message.metatags
│ │ └── kafka_service_consume_message.out
│ ├── kafka-service-error-handling/
│ │ ├── kafka_service_error_handling.bal
│ │ ├── kafka_service_error_handling.md
│ │ ├── kafka_service_error_handling.metatags
│ │ └── kafka_service_error_handling.out
│ ├── kafka-service-sasl/
│ │ ├── kafka_service_sasl.bal
│ │ ├── kafka_service_sasl.md
│ │ ├── kafka_service_sasl.metatags
│ │ └── kafka_service_sasl.out
│ ├── kafka-service-ssl/
│ │ ├── kafka_service_ssl.bal
│ │ ├── kafka_service_ssl.md
│ │ ├── kafka_service_ssl.metatags
│ │ └── kafka_service_ssl.out
│ ├── kubernetes-hello-world/
│ │ ├── Cloud.toml
│ │ ├── build_output.out
│ │ ├── docker_push.out
│ │ ├── execute_curl.out
│ │ ├── kubectl_apply.out
│ │ ├── kubectl_expose.out
│ │ ├── kubectl_pods.out
│ │ ├── kubectl_svc.out
│ │ ├── kubernetes_hello_world.bal
│ │ ├── kubernetes_hello_world.md
│ │ └── minikube_ip.out
│ ├── langlib-functions/
│ │ ├── langlib_functions.bal
│ │ ├── langlib_functions.md
│ │ ├── langlib_functions.metatags
│ │ └── langlib_functions.out
│ ├── ldap-add-remove-entry/
│ │ ├── ldap_add_remove_entry.bal
│ │ ├── ldap_add_remove_entry.md
│ │ ├── ldap_add_remove_entry.metatags
│ │ └── ldap_add_remove_entry.server.out
│ ├── ldap-search-entry/
│ │ ├── ldap_search_entry.bal
│ │ ├── ldap_search_entry.md
│ │ ├── ldap_search_entry.metatags
│ │ └── ldap_search_entry.server.out
│ ├── let-clause/
│ │ ├── let_clause.bal
│ │ ├── let_clause.md
│ │ ├── let_clause.metatags
│ │ └── let_clause.out
│ ├── limit-clause/
│ │ ├── limit_clause.bal
│ │ ├── limit_clause.md
│ │ ├── limit_clause.metatags
│ │ └── limit_clause.out
│ ├── list-binding-pattern/
│ │ ├── list_binding_pattern.bal
│ │ ├── list_binding_pattern.md
│ │ ├── list_binding_pattern.metatags
│ │ └── list_binding_pattern.out
│ ├── list-binding-pattern-in-match-statement/
│ │ ├── list_binding_pattern_in_match_statement.bal
│ │ ├── list_binding_pattern_in_match_statement.md
│ │ ├── list_binding_pattern_in_match_statement.metatags
│ │ └── list_binding_pattern_in_match_statement.out
│ ├── list-equality/
│ │ ├── list_equality.bal
│ │ ├── list_equality.md
│ │ ├── list_equality.metatags
│ │ └── list_equality.out
│ ├── list-subtyping/
│ │ ├── list_subtyping.bal
│ │ ├── list_subtyping.md
│ │ ├── list_subtyping.metatags
│ │ └── list_subtyping.out
│ ├── lock-statement/
│ │ ├── lock_statement.bal
│ │ ├── lock_statement.md
│ │ ├── lock_statement.metatags
│ │ └── lock_statement.out
│ ├── log-file-rotation/
│ │ ├── app_log_rotation.out
│ │ ├── log_file_rotation.bal
│ │ ├── log_file_rotation.md
│ │ ├── log_file_rotation.metatags
│ │ └── log_file_rotation.out
│ ├── logger-from-config/
│ │ ├── audit_logs.out
│ │ ├── logger_from_config.bal
│ │ ├── logger_from_config.md
│ │ ├── logger_from_config.metatags
│ │ ├── logger_from_config.out
│ │ └── metrics_logs.out
│ ├── logging/
│ │ ├── logging.bal
│ │ ├── logging.md
│ │ ├── logging.metatags
│ │ ├── logging.out
│ │ └── tests/
│ │ └── log_api_test.bal
│ ├── logging-configuration/
│ │ ├── ModuleConfig.toml
│ │ ├── logging_configuration.bal
│ │ ├── logging_configuration.md
│ │ ├── logging_configuration.metatags
│ │ └── logging_configuration.out
│ ├── logging-with-context/
│ │ ├── logging_with_context.bal
│ │ ├── logging_with_context.md
│ │ ├── logging_with_context.metatags
│ │ └── logging_with_context.out
│ ├── main-function/
│ │ ├── main_function.bal
│ │ ├── main_function.md
│ │ ├── main_function.metatags
│ │ └── main_function.out
│ ├── manage-scheduled-jobs/
│ │ ├── manage_scheduled_jobs.bal
│ │ ├── manage_scheduled_jobs.md
│ │ ├── manage_scheduled_jobs.metatags
│ │ └── manage_scheduled_jobs.out
│ ├── mapping-binding-pattern/
│ │ ├── mapping_binding_pattern.bal
│ │ ├── mapping_binding_pattern.md
│ │ ├── mapping_binding_pattern.metatags
│ │ └── mapping_binding_pattern.out
│ ├── mapping-binding-pattern-in-match-statement/
│ │ ├── mapping_binding_pattern_in_match_statement.bal
│ │ ├── mapping_binding_pattern_in_match_statement.md
│ │ ├── mapping_binding_pattern_in_match_statement.metatags
│ │ └── mapping_binding_pattern_in_match_statement.out
│ ├── maps/
│ │ ├── maps.bal
│ │ ├── maps.md
│ │ ├── maps.metatags
│ │ └── maps.out
│ ├── match-guard-in-match-statement/
│ │ ├── match_guard_in_match_statement.bal
│ │ ├── match_guard_in_match_statement.md
│ │ ├── match_guard_in_match_statement.metatags
│ │ └── match_guard_in_match_statement.out
│ ├── match-statement/
│ │ ├── match_statement.bal
│ │ ├── match_statement.md
│ │ ├── match_statement.metatags
│ │ └── match_statement.out
│ ├── match-statement-with-maps/
│ │ ├── match_statement_with_maps.bal
│ │ ├── match_statement_with_maps.md
│ │ ├── match_statement_with_maps.metatags
│ │ └── match_statement_with_maps.out
│ ├── mcp-service/
│ │ ├── mcp_service.bal
│ │ ├── mcp_service.md
│ │ ├── mcp_service.metatags
│ │ └── mcp_service.out
│ ├── mcp-service-advanced/
│ │ ├── mcp_service_advanced.bal
│ │ ├── mcp_service_advanced.md
│ │ ├── mcp_service_advanced.metatags
│ │ └── mcp_service_advanced.out
│ ├── message-store-listener/
│ │ ├── message_store_listener.bal
│ │ ├── message_store_listener.md
│ │ ├── message_store_listener.metatags
│ │ └── message_store_listener.out
│ ├── message-store-type/
│ │ ├── message_store_type.bal
│ │ ├── message_store_type.md
│ │ ├── message_store_type.metatags
│ │ └── message_store_type.out
│ ├── meta.json
│ ├── module-lifecycle/
│ │ ├── module_lifecycle.bal
│ │ ├── module_lifecycle.md
│ │ ├── module_lifecycle.metatags
│ │ └── module_lifecycle.out
│ ├── mqtt-client-basic-authentication/
│ │ ├── mqtt_client_basic_authentication.bal
│ │ ├── mqtt_client_basic_authentication.curl.out
│ │ ├── mqtt_client_basic_authentication.md
│ │ ├── mqtt_client_basic_authentication.metatags
│ │ └── mqtt_client_basic_authentication.out
│ ├── mqtt-client-publish-message/
│ │ ├── mqtt_client_publish_message.bal
│ │ ├── mqtt_client_publish_message.curl.out
│ │ ├── mqtt_client_publish_message.md
│ │ ├── mqtt_client_publish_message.metatags
│ │ └── mqtt_client_publish_message.out
│ ├── mqtt-client-ssl/
│ │ ├── mqtt_client_ssl.bal
│ │ ├── mqtt_client_ssl.curl.out
│ │ ├── mqtt_client_ssl.md
│ │ ├── mqtt_client_ssl.metatags
│ │ └── mqtt_client_ssl.out
│ ├── mqtt-service-basic-authentication/
│ │ ├── mqtt_service_basic_authentication.bal
│ │ ├── mqtt_service_basic_authentication.md
│ │ ├── mqtt_service_basic_authentication.metatags
│ │ └── mqtt_service_basic_authentication.out
│ ├── mqtt-service-ssl/
│ │ ├── mqtt_service_ssl.bal
│ │ ├── mqtt_service_ssl.md
│ │ ├── mqtt_service_ssl.metatags
│ │ └── mqtt_service_ssl.out
│ ├── mqtt-service-subscribe-message/
│ │ ├── mqtt_service_subscribe_message.bal
│ │ ├── mqtt_service_subscribe_message.md
│ │ ├── mqtt_service_subscribe_message.metatags
│ │ └── mqtt_service_subscribe_message.out
│ ├── multiple-key-fields/
│ │ ├── multiple_key_fields.bal
│ │ ├── multiple_key_fields.md
│ │ ├── multiple_key_fields.metatags
│ │ └── multiple_key_fields.out
│ ├── multiple-receive/
│ │ ├── multiple_receive.bal
│ │ ├── multiple_receive.md
│ │ ├── multiple_receive.metatags
│ │ └── multiple_receive.out
│ ├── multiple-wait/
│ │ ├── multiple_wait.bal
│ │ ├── multiple_wait.md
│ │ ├── multiple_wait.metatags
│ │ └── multiple_wait.out
│ ├── mysql-atomic-transaction/
│ │ ├── mysql_atomic_transaction.bal
│ │ ├── mysql_atomic_transaction.client.out
│ │ ├── mysql_atomic_transaction.md
│ │ ├── mysql_atomic_transaction.metatags
│ │ ├── mysql_atomic_transaction.server.out
│ │ └── mysql_atomic_xa_transaction.bal
│ ├── mysql-batch-execute-operation/
│ │ ├── mysql_batch_execute_operation.bal
│ │ ├── mysql_batch_execute_operation.client.out
│ │ ├── mysql_batch_execute_operation.md
│ │ ├── mysql_batch_execute_operation.metatags
│ │ └── mysql_batch_execute_operation.server.out
│ ├── mysql-call-stored-procedures/
│ │ ├── mysql_call_stored_procedures.bal
│ │ ├── mysql_call_stored_procedures.client.out
│ │ ├── mysql_call_stored_procedures.md
│ │ ├── mysql_call_stored_procedures.metatags
│ │ └── mysql_call_stored_procedures.server.out
│ ├── mysql-execute-operation/
│ │ ├── mysql_execute_operation.bal
│ │ ├── mysql_execute_operation.client.out
│ │ ├── mysql_execute_operation.md
│ │ ├── mysql_execute_operation.metatags
│ │ └── mysql_execute_operation.server.out
│ ├── mysql-prerequisite/
│ │ ├── README.md
│ │ ├── create_stored_procedure.bal
│ │ └── setup_database.bal
│ ├── mysql-query-column-mapping/
│ │ ├── mysql_query_column_mapping.bal
│ │ ├── mysql_query_column_mapping.client.out
│ │ ├── mysql_query_column_mapping.md
│ │ ├── mysql_query_column_mapping.metatags
│ │ └── mysql_query_column_mapping.server.out
│ ├── mysql-query-operation/
│ │ ├── mysql_query_operation.md
│ │ ├── mysql_query_operation.metatags
│ │ ├── mysql_simple_query.bal
│ │ ├── mysql_simple_query.client.out
│ │ └── mysql_simple_query.server.out
│ ├── mysql-query-row-operation/
│ │ ├── mysql_query_row.bal
│ │ ├── mysql_query_row.client.out
│ │ ├── mysql_query_row.server.out
│ │ ├── mysql_query_row_operation.md
│ │ └── mysql_query_row_operation.metatags
│ ├── named-worker-return-values/
│ │ ├── named_worker_return_values.bal
│ │ ├── named_worker_return_values.md
│ │ ├── named_worker_return_values.metatags
│ │ └── named_worker_return_values.out
│ ├── named-worker-with-on-fail-clause/
│ │ ├── named_worker_with_on_fail_clause.bal
│ │ ├── named_worker_with_on_fail_clause.md
│ │ ├── named_worker_with_on_fail_clause.metatags
│ │ └── named_worker_with_on_fail_clause.out
│ ├── named-workers/
│ │ ├── named_workers.bal
│ │ ├── named_workers.md
│ │ ├── named_workers.metatags
│ │ └── named_workers.out
│ ├── named-workers-and-futures/
│ │ ├── named_workers_and_futures.bal
│ │ ├── named_workers_and_futures.md
│ │ ├── named_workers_and_futures.metatags
│ │ └── named_workers_and_futures.out
│ ├── nats-basic-pub/
│ │ ├── nats_basic_pub.bal
│ │ ├── nats_basic_pub.client.out
│ │ ├── nats_basic_pub.md
│ │ ├── nats_basic_pub.metatags
│ │ └── nats_basic_pub.server.out
│ ├── nats-basic-reply/
│ │ ├── nats_basic_reply.bal
│ │ ├── nats_basic_reply.md
│ │ ├── nats_basic_reply.metatags
│ │ └── nats_basic_reply.out
│ ├── nats-basic-request/
│ │ ├── nats_basic_request.bal
│ │ ├── nats_basic_request.client.out
│ │ ├── nats_basic_request.md
│ │ ├── nats_basic_request.metatags
│ │ └── nats_basic_request.server.out
│ ├── nats-basic-sub/
│ │ ├── nats_basic_sub.bal
│ │ ├── nats_basic_sub.md
│ │ ├── nats_basic_sub.metatags
│ │ └── nats_basic_sub.out
│ ├── nats-client-basic-auth/
│ │ ├── nats_client_basic_auth.bal
│ │ ├── nats_client_basic_auth.client.out
│ │ ├── nats_client_basic_auth.md
│ │ ├── nats_client_basic_auth.metatags
│ │ └── nats_client_basic_auth.server.out
│ ├── nats-client-secure-connection/
│ │ ├── nats_client_secure_connection.bal
│ │ ├── nats_client_secure_connection.client.out
│ │ ├── nats_client_secure_connection.md
│ │ ├── nats_client_secure_connection.metatags
│ │ └── nats_client_secure_connection.server.out
│ ├── nats-jetstream-pub/
│ │ ├── nats_jetstream_pub.bal
│ │ ├── nats_jetstream_pub.client.out
│ │ ├── nats_jetstream_pub.md
│ │ ├── nats_jetstream_pub.metatags
│ │ └── nats_jetstream_pub.server.out
│ ├── nats-jetstream-sub/
│ │ ├── nats_jestream_sub.bal
│ │ ├── nats_jestream_sub.md
│ │ ├── nats_jestream_sub.metatags
│ │ └── nats_jestream_sub.out
│ ├── nats-service-basic-auth/
│ │ ├── nats_service_basic_auth.bal
│ │ ├── nats_service_basic_auth.md
│ │ ├── nats_service_basic_auth.metatags
│ │ └── nats_service_basic_auth.out
│ ├── nats-service-constraint-validation/
│ │ ├── nats_service_constraint_validation.bal
│ │ ├── nats_service_constraint_validation.md
│ │ ├── nats_service_constraint_validation.metatags
│ │ └── nats_service_constraint_validation.out
│ ├── nats-service-secure-connection/
│ │ ├── nats_service_secure_connection.bal
│ │ ├── nats_service_secure_connection.md
│ │ ├── nats_service_secure_connection.metatags
│ │ └── nats_service_secure_connection.out
│ ├── natural-expressions/
│ │ ├── natural_expressions.bal
│ │ ├── natural_expressions.md
│ │ ├── natural_expressions.metatags
│ │ └── natural_expressions.out
│ ├── nested-arrays/
│ │ ├── nested_arrays.bal
│ │ ├── nested_arrays.md
│ │ ├── nested_arrays.metatags
│ │ └── nested_arrays.out
│ ├── nested-query-expressions/
│ │ ├── nested_query_expressions.bal
│ │ ├── nested_query_expressions.md
│ │ ├── nested_query_expressions.metatags
│ │ └── nested_query_expressions.out
│ ├── never-type/
│ │ ├── never_type.bal
│ │ ├── never_type.md
│ │ ├── never_type.metatags
│ │ └── never_type.out
│ ├── nil/
│ │ ├── nil.bal
│ │ ├── nil.md
│ │ ├── nil.metatags
│ │ └── nil.out
│ ├── object/
│ │ ├── object.bal
│ │ ├── object.md
│ │ ├── object.metatags
│ │ └── object.out
│ ├── object-closure/
│ │ ├── object_closure.bal
│ │ ├── object_closure.md
│ │ ├── object_closure.metatags
│ │ └── object_closure.out
│ ├── object-constructor/
│ │ ├── object_constructor.bal
│ │ ├── object_constructor.md
│ │ ├── object_constructor.metatags
│ │ └── object_constructor.out
│ ├── object-type-inclusion/
│ │ ├── object_type_inclusion.bal
│ │ ├── object_type_inclusion.md
│ │ ├── object_type_inclusion.metatags
│ │ └── object_type_inclusion.out
│ ├── object-types/
│ │ ├── object_types.bal
│ │ ├── object_types.md
│ │ ├── object_types.metatags
│ │ └── object_types.out
│ ├── object-value-from-class-definition/
│ │ ├── object_value_from_class_definition.bal
│ │ ├── object_value_from_class_definition.md
│ │ ├── object_value_from_class_definition.metatags
│ │ └── object_value_from_class_definition.out
│ ├── on-conflict-clause/
│ │ ├── on_conflict_clause.bal
│ │ ├── on_conflict_clause.md
│ │ ├── on_conflict_clause.metatags
│ │ └── on_conflict_clause.out
│ ├── open-records/
│ │ ├── open_records.bal
│ │ ├── open_records.md
│ │ ├── open_records.metatags
│ │ └── open_records.out
│ ├── optional-fields/
│ │ ├── optional_fields.bal
│ │ ├── optional_fields.md
│ │ ├── optional_fields.metatags
│ │ └── optional_fields.out
│ ├── outer-join-clause/
│ │ ├── outer_join_clause.bal
│ │ ├── outer_join_clause.md
│ │ ├── outer_join_clause.metatags
│ │ └── outer_join_clause.out
│ ├── panics/
│ │ ├── panics.bal
│ │ ├── panics.md
│ │ ├── panics.metatags
│ │ └── panics.out
│ ├── parse-csv-lists/
│ │ ├── parse_csv_lists.bal
│ │ ├── parse_csv_lists.md
│ │ ├── parse_csv_lists.metatags
│ │ └── parse_csv_lists.out
│ ├── persist-create/
│ │ ├── persist_create.bal
│ │ ├── persist_create.md
│ │ ├── persist_create.metatags
│ │ ├── persist_create.out
│ │ ├── persist_generate.out
│ │ ├── persist_init.out
│ │ └── persist_model.bal
│ ├── persist-delete/
│ │ ├── persist_create.metatags
│ │ ├── persist_delete.bal
│ │ ├── persist_delete.md
│ │ ├── persist_delete.out
│ │ ├── persist_generate.out
│ │ ├── persist_init.out
│ │ └── persist_model.bal
│ ├── persist-filtering/
│ │ ├── persist_filtering.bal
│ │ ├── persist_filtering.md
│ │ ├── persist_filtering.metatags
│ │ ├── persist_filtering.out
│ │ ├── persist_generate.out
│ │ ├── persist_init.out
│ │ └── persist_model.bal
│ ├── persist-get-all/
│ │ ├── persist_generate.out
│ │ ├── persist_get_all.bal
│ │ ├── persist_get_all.md
│ │ ├── persist_get_all.metatags
│ │ ├── persist_get_all.out
│ │ ├── persist_init.out
│ │ └── persist_model.bal
│ ├── persist-get-by-key/
│ │ ├── persist_generate.out
│ │ ├── persist_get_by_key.bal
│ │ ├── persist_get_by_key.md
│ │ ├── persist_get_by_key.metatags
│ │ ├── persist_get_by_key.out
│ │ ├── persist_init.out
│ │ └── persist_model.bal
│ ├── persist-relation-queries/
│ │ ├── persist_generate.out
│ │ ├── persist_init.out
│ │ ├── persist_model.bal
│ │ ├── persist_relation_queries.bal
│ │ ├── persist_relation_queries.md
│ │ ├── persist_relation_queries.metatags
│ │ └── persist_relation_queries.out
│ ├── persist-select-fields/
│ │ ├── persist_generate.out
│ │ ├── persist_init.out
│ │ ├── persist_model.bal
│ │ ├── persist_select_fields.bal
│ │ ├── persist_select_fields.md
│ │ ├── persist_select_fields.metatags
│ │ └── persist_select_fields.out
│ ├── persist-update/
│ │ ├── persist_generate.out
│ │ ├── persist_init.out
│ │ ├── persist_model.bal
│ │ ├── persist_update.bal
│ │ ├── persist_update.md
│ │ ├── persist_update.metatags
│ │ └── persist_update.out
│ ├── programs-and-modules/
│ │ ├── programs_and_modules.bal
│ │ ├── programs_and_modules.md
│ │ ├── programs_and_modules.metatags
│ │ └── programs_and_modules.out
│ ├── provide-function-arguments-by-name/
│ │ ├── provide_function_arguments_by_name.bal
│ │ ├── provide_function_arguments_by_name.md
│ │ ├── provide_function_arguments_by_name.metatags
│ │ └── provide_function_arguments_by_name.out
│ ├── providing-services/
│ │ ├── providing_services.bal
│ │ ├── providing_services.md
│ │ ├── providing_services.metatags
│ │ └── providing_services.out
│ ├── query-actions/
│ │ ├── query_actions.bal
│ │ ├── query_actions.md
│ │ ├── query_actions.metatags
│ │ └── query_actions.out
│ ├── query-expressions/
│ │ ├── query_expressions.bal
│ │ ├── query_expressions.md
│ │ ├── query_expressions.metatags
│ │ └── query_expressions.out
│ ├── querying-tables/
│ │ ├── querying_tables.bal
│ │ ├── querying_tables.md
│ │ ├── querying_tables.metatags
│ │ └── querying_tables.out
│ ├── querying-with-streams/
│ │ ├── querying_with_streams.bal
│ │ ├── querying_with_streams.md
│ │ ├── querying_with_streams.metatags
│ │ └── querying_with_streams.out
│ ├── rabbitmq-client-basic-auth/
│ │ ├── rabbitmq_client_basic_auth.bal
│ │ ├── rabbitmq_client_basic_auth.client.out
│ │ ├── rabbitmq_client_basic_auth.md
│ │ ├── rabbitmq_client_basic_auth.metatags
│ │ └── rabbitmq_client_basic_auth.server.out
│ ├── rabbitmq-client-constraint-validation/
│ │ ├── rabbitmq_client_constraint_validation.bal
│ │ ├── rabbitmq_client_constraint_validation.md
│ │ ├── rabbitmq_client_constraint_validation.metatags
│ │ └── rabbitmq_client_constraint_validation.out
│ ├── rabbitmq-client-secure-connection/
│ │ ├── rabbitmq_client_secure_connection.bal
│ │ ├── rabbitmq_client_secure_connection.client.out
│ │ ├── rabbitmq_client_secure_connection.md
│ │ ├── rabbitmq_client_secure_connection.metatags
│ │ └── rabbitmq_client_secure_connection.server.out
│ ├── rabbitmq-consumer/
│ │ ├── rabbitmq_consumer.bal
│ │ ├── rabbitmq_consumer.md
│ │ ├── rabbitmq_consumer.metatags
│ │ └── rabbitmq_consumer.out
│ ├── rabbitmq-consumer-with-client-acknowledgement/
│ │ ├── rabbitmq_consumer_with_client_acknowledgement.bal
│ │ ├── rabbitmq_consumer_with_client_acknowledgement.md
│ │ ├── rabbitmq_consumer_with_client_acknowledgement.metatags
│ │ └── rabbitmq_consumer_with_client_acknowledgement.out
│ ├── rabbitmq-producer/
│ │ ├── rabbitmq_producer.bal
│ │ ├── rabbitmq_producer.client.out
│ │ ├── rabbitmq_producer.md
│ │ ├── rabbitmq_producer.metatags
│ │ └── rabbitmq_producer.server.out
│ ├── rabbitmq-queue-declare/
│ │ ├── rabbitmq_queue_declare.bal
│ │ ├── rabbitmq_queue_declare.md
│ │ ├── rabbitmq_queue_declare.metatags
│ │ └── rabbitmq_queue_declare.out
│ ├── rabbitmq-service-basic-auth/
│ │ ├── rabbitmq_service_basic_auth.bal
│ │ ├── rabbitmq_service_basic_auth.md
│ │ ├── rabbitmq_service_basic_auth.metatags
│ │ └── rabbitmq_service_basic_auth.out
│ ├── rabbitmq-service-constraint-validation/
│ │ ├── rabbitmq_service_constraint_validation.bal
│ │ ├── rabbitmq_service_constraint_validation.md
│ │ ├── rabbitmq_service_constraint_validation.metatags
│ │ └── rabbitmq_service_constraint_validation.out
│ ├── rabbitmq-service-secure-connection/
│ │ ├── rabbitmq_service_secure_connection.bal
│ │ ├── rabbitmq_service_secure_connection.md
│ │ ├── rabbitmq_service_secure_connection.metatags
│ │ └── rabbitmq_service_secure_connection.out
│ ├── rabbitmq-sync-consumer/
│ │ ├── rabbitmq_sync_consumer.bal
│ │ ├── rabbitmq_sync_consumer.md
│ │ ├── rabbitmq_sync_consumer.metatags
│ │ └── rabbitmq_sync_consumer.out
│ ├── rabbitmq-transaction-consumer/
│ │ ├── rabbitmq_transaction_consumer.bal
│ │ ├── rabbitmq_transaction_consumer.md
│ │ ├── rabbitmq_transaction_consumer.metatags
│ │ └── rabbitmq_transaction_consumer.out
│ ├── rabbitmq-transaction-producer/
│ │ ├── rabbitmq_transaction_producer.bal
│ │ ├── rabbitmq_transaction_producer.md
│ │ ├── rabbitmq_transaction_producer.metatags
│ │ └── rabbitmq_transaction_producer.out
│ ├── rag-ingestion-with-external-vector-store/
│ │ ├── rag_ingestion_with_external_vector_store.bal
│ │ ├── rag_ingestion_with_external_vector_store.md
│ │ ├── rag_ingestion_with_external_vector_store.metatags
│ │ └── rag_ingestion_with_external_vector_store.out
│ ├── rag-query-with-external-vector-store/
│ │ ├── rag_query_with_external_vector_store.bal
│ │ ├── rag_query_with_external_vector_store.md
│ │ ├── rag_query_with_external_vector_store.metatags
│ │ └── rag_query_with_external_vector_store.out
│ ├── rag-with-in-memory-vector-store/
│ │ ├── leave_policy.md
│ │ ├── rag_with_in_memory_vector_store.bal
│ │ ├── rag_with_in_memory_vector_store.md
│ │ ├── rag_with_in_memory_vector_store.metatags
│ │ └── rag_with_in_memory_vector_store.out
│ ├── random-numbers/
│ │ ├── random_numbers.bal
│ │ ├── random_numbers.md
│ │ ├── random_numbers.metatags
│ │ └── random_numbers.out
│ ├── raw-templates/
│ │ ├── raw_templates.bal
│ │ ├── raw_templates.md
│ │ ├── raw_templates.metatags
│ │ └── raw_templates.out
│ ├── readonly-and-isolated/
│ │ ├── readonly_and_isolated.bal
│ │ ├── readonly_and_isolated.md
│ │ ├── readonly_and_isolated.metatags
│ │ └── readonly_and_isolated.out
│ ├── readonly-objects-and-classes/
│ │ ├── readonly_objects_and_classes.bal
│ │ ├── readonly_objects_and_classes.md
│ │ ├── readonly_objects_and_classes.metatags
│ │ └── readonly_objects_and_classes.out
│ ├── readonly-type/
│ │ ├── readonly_type.bal
│ │ ├── readonly_type.md
│ │ ├── readonly_type.metatags
│ │ └── readonly_type.out
│ ├── receive-email-using-client/
│ │ ├── receive_email_using_client.bal
│ │ ├── receive_email_using_client.md
│ │ ├── receive_email_using_client.metatags
│ │ └── receive_email_using_client.out
│ ├── receive-email-using-service/
│ │ ├── receive_email_using_service.bal
│ │ ├── receive_email_using_service.md
│ │ ├── receive_email_using_service.metatags
│ │ └── receive_email_using_service.out
│ ├── record-to-edi/
│ │ ├── bal_project.out
│ │ ├── codegen_command.out
│ │ ├── output.out
│ │ ├── package_structure.out
│ │ ├── record_to_edi.bal
│ │ ├── record_to_edi.md
│ │ ├── record_to_edi.metatags
│ │ ├── schema.json
│ │ └── tool_pull_command.out
│ ├── records/
│ │ ├── records.bal
│ │ ├── records.md
│ │ ├── records.metatags
│ │ └── records.out
│ ├── regexp-find-operations/
│ │ ├── regexp_find_operations.bal
│ │ ├── regexp_find_operations.md
│ │ ├── regexp_find_operations.metatags
│ │ └── regexp_find_operations.out
│ ├── regexp-match-operations/
│ │ ├── regexp_match_operations.bal
│ │ ├── regexp_match_operations.md
│ │ ├── regexp_match_operations.metatags
│ │ └── regexp_match_operations.out
│ ├── regexp-operations-overview/
│ │ ├── regexp_operations_overview.bal
│ │ ├── regexp_operations_overview.md
│ │ ├── regexp_operations_overview.metatags
│ │ └── regexp_operations_overview.out
│ ├── regexp-replace-operations/
│ │ ├── regexp_replace_operations.bal
│ │ ├── regexp_replace_operations.md
│ │ ├── regexp_replace_operations.metatags
│ │ └── regexp_replace_operations.out
│ ├── regexp-type/
│ │ ├── regexp_type.bal
│ │ ├── regexp_type.md
│ │ ├── regexp_type.metatags
│ │ └── regexp_type.out
│ ├── resource/
│ │ └── path/
│ │ └── to/
│ │ ├── ballerinaKeystore.p12
│ │ ├── ballerinaTruststore.p12
│ │ ├── client-private.key
│ │ ├── client-public.crt
│ │ ├── encryptedPrivate.key
│ │ ├── private.key
│ │ ├── public.crt
│ │ ├── server-private.key
│ │ └── server-public.crt
│ ├── resource-method-typing/
│ │ ├── resource_method_typing.bal
│ │ ├── resource_method_typing.client.out
│ │ ├── resource_method_typing.md
│ │ ├── resource_method_typing.metatags
│ │ └── resource_method_typing.server.out
│ ├── resource-methods/
│ │ ├── resource_methods.bal
│ │ ├── resource_methods.client.out
│ │ ├── resource_methods.md
│ │ ├── resource_methods.metatags
│ │ └── resource_methods.server.out
│ ├── resource-path-parameters/
│ │ ├── resource_path_parameters.bal
│ │ ├── resource_path_parameters.client.out
│ │ ├── resource_path_parameters.md
│ │ ├── resource_path_parameters.metatags
│ │ └── resource_path_parameters.server.out
│ ├── rest-arguments/
│ │ ├── rest_arguments.bal
│ │ ├── rest_arguments.md
│ │ ├── rest_arguments.metatags
│ │ └── rest_arguments.out
│ ├── rest-binding-pattern-in-error-binding-pattern/
│ │ ├── rest_binding_pattern_in_error_binding_pattern.bal
│ │ ├── rest_binding_pattern_in_error_binding_pattern.md
│ │ ├── rest_binding_pattern_in_error_binding_pattern.metatags
│ │ └── rest_binding_pattern_in_error_binding_pattern.out
│ ├── rest-binding-pattern-in-list-binding-pattern/
│ │ ├── rest_binding_pattern_in_list_binding_pattern.bal
│ │ ├── rest_binding_pattern_in_list_binding_pattern.md
│ │ ├── rest_binding_pattern_in_list_binding_pattern.metatags
│ │ └── rest_binding_pattern_in_list_binding_pattern.out
│ ├── rest-binding-pattern-in-mapping-binding-pattern/
│ │ ├── rest_binding_pattern_in_mapping_binding_pattern.bal
│ │ ├── rest_binding_pattern_in_mapping_binding_pattern.md
│ │ ├── rest_binding_pattern_in_mapping_binding_pattern.metatags
│ │ └── rest_binding_pattern_in_mapping_binding_pattern.out
│ ├── rest-parameters/
│ │ ├── rest_parameters.bal
│ │ ├── rest_parameters.md
│ │ ├── rest_parameters.metatags
│ │ └── rest_parameters.out
│ ├── rest-type-in-tuples/
│ │ ├── rest_type_in_tuples.bal
│ │ ├── rest_type_in_tuples.md
│ │ ├── rest_type_in_tuples.metatags
│ │ └── rest_type_in_tuples.out
│ ├── retry-transaction-statement/
│ │ ├── retry_transaction_statement.bal
│ │ ├── retry_transaction_statement.md
│ │ ├── retry_transaction_statement.metatags
│ │ └── retry_transaction_statement.out
│ ├── rollback/
│ │ ├── rollback.bal
│ │ ├── rollback.md
│ │ ├── rollback.metatags
│ │ └── rollback.out
│ ├── run-strands-safely-on-separate-threads/
│ │ ├── run_strands_safely_on_separate_threads.bal
│ │ ├── run_strands_safely_on_separate_threads.md
│ │ ├── run_strands_safely_on_separate_threads.metatags
│ │ └── run_strands_safely_on_separate_threads.out
│ ├── security-crypto/
│ │ ├── security_crypto.bal
│ │ ├── security_crypto.md
│ │ ├── security_crypto.metatags
│ │ ├── security_crypto.out
│ │ └── tests/
│ │ └── security_crypto_test.bal
│ ├── security-jwt-issue-validate/
│ │ ├── security_jwt_issue_validate.bal
│ │ ├── security_jwt_issue_validate.md
│ │ ├── security_jwt_issue_validate.metatags
│ │ ├── security_jwt_issue_validate.out
│ │ └── tests/
│ │ └── security_jwt_issue_validate_test.bal
│ ├── send-email/
│ │ ├── send_email.bal
│ │ ├── send_email.md
│ │ ├── send_email.metatags
│ │ └── send_email.out
│ ├── sensitive-data-logging/
│ │ ├── sensitive_data_logging.bal
│ │ ├── sensitive_data_logging.md
│ │ ├── sensitive_data_logging.metatags
│ │ └── sensitive_data_logging.out
│ ├── sequence-diagrams/
│ │ ├── sequence_diagrams.bal
│ │ ├── sequence_diagrams.md
│ │ ├── sequence_diagrams.metatags
│ │ └── sequence_diagrams.out
│ ├── service-declaration/
│ │ ├── service_declaration.bal
│ │ ├── service_declaration.client.out
│ │ ├── service_declaration.md
│ │ ├── service_declaration.metatags
│ │ └── service_declaration.server.out
│ ├── sftp-client-receive-file/
│ │ ├── sftp_client_receive_file.bal
│ │ ├── sftp_client_receive_file.md
│ │ ├── sftp_client_receive_file.metatags
│ │ └── sftp_client_receive_file.out
│ ├── sftp-client-send-file/
│ │ ├── sftp_client_send_file.bal
│ │ ├── sftp_client_send_file.md
│ │ ├── sftp_client_send_file.metatags
│ │ └── sftp_client_send_file.out
│ ├── sftp-service-receive-file/
│ │ ├── sftp_service_receive_file.bal
│ │ ├── sftp_service_receive_file.md
│ │ ├── sftp_service_receive_file.metatags
│ │ └── sftp_service_receive_file.out
│ ├── sftp-service-send-file/
│ │ ├── sftp_service_send_file.bal
│ │ ├── sftp_service_send_file.md
│ │ ├── sftp_service_send_file.metatags
│ │ └── sftp_service_send_file.out
│ ├── single-use-of-typed-binding/
│ │ ├── single_use_of_typed_binding.bal
│ │ ├── single_use_of_typed_binding.md
│ │ ├── single_use_of_typed_binding.metatags
│ │ └── single_use_of_typed_binding.out
│ ├── single-use-with-on-fail-clause/
│ │ ├── single_use_with_on_fail_clause.bal
│ │ ├── single_use_with_on_fail_clause.md
│ │ ├── single_use_with_on_fail_clause.metatags
│ │ └── single_use_with_on_fail_clause.out
│ ├── singleton-types/
│ │ ├── singleton_types.bal
│ │ ├── singleton_types.md
│ │ ├── singleton_types.metatags
│ │ └── singleton_types.out
│ ├── soap-client-security-inbound-security-config/
│ │ ├── soap_client_security_inbound_security_config.bal
│ │ ├── soap_client_security_inbound_security_config.md
│ │ ├── soap_client_security_inbound_security_config.metatags
│ │ └── soap_client_security_inbound_security_config.out
│ ├── soap-client-security-outbound-security-config/
│ │ ├── soap_client_security_outbound_security_config.bal
│ │ ├── soap_client_security_outbound_security_config.md
│ │ ├── soap_client_security_outbound_security_config.metatags
│ │ └── soap_client_security_outbound_security_config.out
│ ├── soap-client-security-ssl-tsl/
│ │ ├── soap_client_security_ssl_tls.bal
│ │ ├── soap_client_security_ssl_tls.md
│ │ ├── soap_client_security_ssl_tls.metatags
│ │ └── soap_client_security_ssl_tls.out
│ ├── soap-client-send-receive/
│ │ ├── soap_client_send_receive.bal
│ │ ├── soap_client_send_receive.md
│ │ ├── soap_client_send_receive.metatags
│ │ └── soap_client_send_receive.out
│ ├── sort-iterable-objects/
│ │ ├── sort_iterable_objects.bal
│ │ ├── sort_iterable_objects.md
│ │ ├── sort_iterable_objects.metatags
│ │ └── sort_iterable_objects.out
│ ├── start-service-from-service-class-definition/
│ │ ├── start_service_from_service_class_definition.bal
│ │ ├── start_service_from_service_class_definition.client.out
│ │ ├── start_service_from_service_class_definition.md
│ │ ├── start_service_from_service_class_definition.metatags
│ │ └── start_service_from_service_class_definition.server.out
│ ├── stop-handler/
│ │ ├── stop_handler.bal
│ │ ├── stop_handler.client.out
│ │ ├── stop_handler.md
│ │ ├── stop_handler.metatags
│ │ └── stop_handler.server.out
│ ├── strands/
│ │ ├── strands.bal
│ │ ├── strands.md
│ │ ├── strands.metatags
│ │ └── strands.out
│ ├── stream-type/
│ │ ├── stream_type.bal
│ │ ├── stream_type.md
│ │ ├── stream_type.metatags
│ │ └── stream_type.out
│ ├── string-templates/
│ │ ├── string_templates.bal
│ │ ├── string_templates.md
│ │ ├── string_templates.metatags
│ │ └── string_templates.out
│ ├── strings/
│ │ ├── strings.bal
│ │ ├── strings.md
│ │ ├── strings.metatags
│ │ └── strings.out
│ ├── structural-typing/
│ │ ├── structural_typing.bal
│ │ ├── structural_typing.md
│ │ ├── structural_typing.metatags
│ │ └── structural_typing.out
│ ├── structured-keys/
│ │ ├── structured_keys.bal
│ │ ├── structured_keys.md
│ │ ├── structured_keys.metatags
│ │ └── structured_keys.out
│ ├── synchronize-message-passing/
│ │ ├── synchronize_message_passing.bal
│ │ ├── synchronize_message_passing.md
│ │ ├── synchronize_message_passing.metatags
│ │ └── synchronize_message_passing.out
│ ├── table/
│ │ ├── table.bal
│ │ ├── table.md
│ │ ├── table.metatags
│ │ └── table.out
│ ├── table-types/
│ │ ├── table_types.bal
│ │ ├── table_types.md
│ │ ├── table_types.metatags
│ │ └── table_types.out
│ ├── task-frequency-job-execution/
│ │ ├── task_frequency_job_execution.bal
│ │ ├── task_frequency_job_execution.md
│ │ ├── task_frequency_job_execution.metatags
│ │ └── task_frequency_job_execution.out
│ ├── task-one-time-job-execution/
│ │ ├── task_one_time_job_execution.bal
│ │ ├── task_one_time_job_execution.md
│ │ ├── task_one_time_job_execution.metatags
│ │ └── task_one_time_job_execution.out
│ ├── tcp-client/
│ │ ├── tcp_client.bal
│ │ ├── tcp_client.md
│ │ ├── tcp_client.metatags
│ │ └── tcp_client.out
│ ├── tcp-client-ssl-tls/
│ │ ├── tcp_client_ssl_tls.bal
│ │ ├── tcp_client_ssl_tls.md
│ │ ├── tcp_client_ssl_tls.metatags
│ │ └── tcp_client_ssl_tls.out
│ ├── tcp-listener/
│ │ ├── tcp_listener.bal
│ │ ├── tcp_listener.md
│ │ ├── tcp_listener.metatags
│ │ └── tcp_listener.out
│ ├── tcp-service-ssl-tls/
│ │ ├── tcp_service_ssl_tls.bal
│ │ ├── tcp_service_ssl_tls.md
│ │ ├── tcp_service_ssl_tls.metatags
│ │ └── tcp_service_ssl_tls.out
│ ├── temp-files-directories/
│ │ ├── temp_files_directories.bal
│ │ ├── temp_files_directories.md
│ │ ├── temp_files_directories.metatags
│ │ └── temp_files_directories.out
│ ├── testerina-assertions/
│ │ ├── testerina_assertions.bal
│ │ ├── testerina_assertions.md
│ │ ├── testerina_assertions.metatags
│ │ └── testerina_assertions.out
│ ├── testerina-before-and-after-each/
│ │ ├── testerina_before_and_after_each.bal
│ │ ├── testerina_before_and_after_each.md
│ │ ├── testerina_before_and_after_each.metatags
│ │ └── testerina_before_and_after_each.out
│ ├── testerina-before-and-after-groups/
│ │ ├── testerina_before_and_after_groups.bal
│ │ ├── testerina_before_and_after_groups.md
│ │ ├── testerina_before_and_after_groups.metatags
│ │ └── testerina_before_and_after_groups.out
│ ├── testerina-before-and-after-suite/
│ │ ├── testerina_before_and_after_suite.bal
│ │ ├── testerina_before_and_after_suite.md
│ │ ├── testerina_before_and_after_suite.metatags
│ │ └── testerina_before_and_after_suite.out
│ ├── testerina-before-and-after-test/
│ │ ├── testerina_before_and_after_test.bal
│ │ ├── testerina_before_and_after_test.md
│ │ ├── testerina_before_and_after_test.metatags
│ │ └── testerina_before_and_after_test.out
│ ├── testerina-data-driven-tests/
│ │ ├── testerina_data_driven_tests.bal
│ │ ├── testerina_data_driven_tests.md
│ │ ├── testerina_data_driven_tests.metadata
│ │ └── testerina_data_driven_tests.out
│ ├── testerina-group-tests/
│ │ ├── testerina_group_disable_groups.out
│ │ ├── testerina_group_tests.bal
│ │ ├── testerina_group_tests.md
│ │ ├── testerina_group_tests.metadata
│ │ ├── testerina_group_tests_groups_g1.out
│ │ └── testerina_group_tests_groups_g1_g2.out
│ ├── testerina-guarantee-test-execution-order/
│ │ ├── testerina_guarantee_test_execution_order.bal
│ │ ├── testerina_guarantee_test_execution_order.md
│ │ ├── testerina_guarantee_test_execution_order.metadata
│ │ └── testerina_guarantee_test_execution_order.out
│ ├── testerina-mocking-functions/
│ │ ├── testerina_mocking_functions.md
│ │ ├── testerina_mocking_functions.metatags
│ │ ├── testerina_mocking_functions_main.bal
│ │ ├── testerina_mocking_functions_test.bal
│ │ └── testerina_mocking_functions_test.out
│ ├── testerina-mocking-objects/
│ │ ├── testerina_mocking_objects.md
│ │ ├── testerina_mocking_objects.metatags
│ │ ├── testerina_mocking_objects_main.bal
│ │ ├── testerina_mocking_objects_test.bal
│ │ └── testerina_mocking_objects_test.out
│ ├── time-formatting-and-parsing/
│ │ ├── tests/
│ │ │ └── time_formatting_and_parsing_test.bal
│ │ ├── time_formatting_and_parsing.bal
│ │ ├── time_formatting_and_parsing.md
│ │ ├── time_formatting_and_parsing.metatags
│ │ └── time_formatting_and_parsing.out
│ ├── time-utc/
│ │ ├── tests/
│ │ │ └── time_utc_test.bal
│ │ ├── time_utc.bal
│ │ ├── time_utc.md
│ │ ├── time_utc.metatags
│ │ └── time_utc.out
│ ├── time-utc-and-civil/
│ │ ├── tests/
│ │ │ └── time_utc_and_civil_test.bal
│ │ ├── time_utc_and_civil.bal
│ │ ├── time_utc_and_civil.md
│ │ ├── time_utc_and_civil.metatags
│ │ └── time_utc_and_civil.out
│ ├── time-zone/
│ │ ├── time_zone.bal
│ │ ├── time_zone.md
│ │ ├── time_zone.metatags
│ │ └── time_zone.out
│ ├── tracing/
│ │ ├── tests/
│ │ │ └── tracing_test.bal
│ │ ├── tracing.bal
│ │ ├── tracing.client.out
│ │ ├── tracing.md
│ │ ├── tracing.metatags
│ │ └── tracing.server.out
│ ├── transaction-statement/
│ │ ├── transaction_statement.bal
│ │ ├── transaction_statement.md
│ │ ├── transaction_statement.metatags
│ │ └── transaction_statement.out
│ ├── transactional-named-workers/
│ │ ├── transactional_named_workers.bal
│ │ ├── transactional_named_workers.md
│ │ ├── transactional_named_workers.metatags
│ │ └── transactional_named_workers.out
│ ├── transactional-qualifier/
│ │ ├── transactional_qualifier.bal
│ │ ├── transactional_qualifier.md
│ │ ├── transactional_qualifier.metatags
│ │ └── transactional_qualifier.out
│ ├── transform-csv-records-to-custom-types/
│ │ ├── transform_csv_records_to_custom_types.bal
│ │ ├── transform_csv_records_to_custom_types.md
│ │ ├── transform_csv_records_to_custom_types.metatags
│ │ └── transform_csv_records_to_custom_types.out
│ ├── trap-expression/
│ │ ├── trap_expression.bal
│ │ ├── trap_expression.md
│ │ ├── trap_expression.metatags
│ │ └── trap_expression.out
│ ├── tuples/
│ │ ├── tuples.bal
│ │ ├── tuples.md
│ │ ├── tuples.metatags
│ │ └── tuples.out
│ ├── type-definitions/
│ │ ├── type_definitions.bal
│ │ ├── type_definitions.md
│ │ ├── type_definitions.metatags
│ │ └── type_definitions.out
│ ├── type-inclusion-for-records/
│ │ ├── type_inclusion_for_records.bal
│ │ ├── type_inclusion_for_records.md
│ │ ├── type_inclusion_for_records.metatags
│ │ └── type_inclusion_for_records.out
│ ├── type-inference/
│ │ ├── type_inference.bal
│ │ ├── type_inference.md
│ │ ├── type_inference.metatags
│ │ └── type_inference.out
│ ├── typed-binding-pattern/
│ │ ├── typed_binding_pattern.bal
│ │ ├── typed_binding_pattern.md
│ │ ├── typed_binding_pattern.metatags
│ │ └── typed_binding_pattern.out
│ ├── typedesc-type/
│ │ ├── typedesc_type.bal
│ │ ├── typedesc_type.md
│ │ ├── typedesc_type.metatags
│ │ └── typedesc_type.out
│ ├── udp-client/
│ │ ├── udp_client.bal
│ │ ├── udp_client.md
│ │ ├── udp_client.metatags
│ │ └── udp_client.out
│ ├── udp-connect-client/
│ │ ├── udp_connect_client.bal
│ │ ├── udp_connect_client.md
│ │ ├── udp_connect_client.metatags
│ │ └── udp_connect_client.out
│ ├── udp-listener/
│ │ ├── udp_listener.bal
│ │ ├── udp_listener.md
│ │ ├── udp_listener.metatags
│ │ └── udp_listener.out
│ ├── unary-operators/
│ │ ├── unary_operators.bal
│ │ ├── unary_operators.md
│ │ ├── unary_operators.metatags
│ │ └── unary_operators.out
│ ├── unions/
│ │ ├── unions.bal
│ │ ├── unions.md
│ │ ├── unions.metatags
│ │ └── unions.out
│ ├── url-encode-decode/
│ │ ├── tests/
│ │ │ └── url_encode_decode_test.bal
│ │ ├── url_encode_decode.bal
│ │ ├── url_encode_decode.md
│ │ ├── url_encode_decode.metatags
│ │ └── url_encode_decode.out
│ ├── uuid-generation/
│ │ ├── uuid_generation.bal
│ │ ├── uuid_generation.md
│ │ ├── uuid_generation.metatags
│ │ └── uuid_generation.out
│ ├── uuid-operations/
│ │ ├── uuid_operations.bal
│ │ ├── uuid_operations.md
│ │ ├── uuid_operations.metatags
│ │ └── uuid_operations.out
│ ├── variables-and-types/
│ │ ├── variables_and_types.bal
│ │ ├── variables_and_types.md
│ │ ├── variables_and_types.metatags
│ │ └── variables_and_types.out
│ ├── visibility-of-object-fields-and-methods/
│ │ ├── visibility_of_object_fields_and_methods.bal
│ │ ├── visibility_of_object_fields_and_methods.md
│ │ ├── visibility_of_object_fields_and_methods.metatags
│ │ └── visibility_of_object_fields_and_methods.out
│ ├── waiting-for-workers/
│ │ ├── waiting_for_workers.bal
│ │ ├── waiting_for_workers.md
│ │ ├── waiting_for_workers.metatags
│ │ └── waiting_for_workers.out
│ ├── websocket-basic-sample/
│ │ ├── tests/
│ │ │ └── websocket_basic_sample_test.bal
│ │ ├── websocket_basic_sample.bal
│ │ ├── websocket_basic_sample.md
│ │ ├── websocket_basic_sample.metatags
│ │ └── websocket_basic_sample.out
│ ├── websocket-binary-client/
│ │ └── websocket_binary_client.md
│ ├── websocket-client/
│ │ ├── websocket_client.bal
│ │ ├── websocket_client.md
│ │ ├── websocket_client.metatags
│ │ └── websocket_client.out
│ ├── websocket-client-basic-auth/
│ │ ├── websocket_client_basic_auth.bal
│ │ ├── websocket_client_basic_auth.md
│ │ ├── websocket_client_basic_auth.metatags
│ │ └── websocket_client_basic_auth.out
│ ├── websocket-client-bearer-token-auth/
│ │ ├── websocket_client_bearer_token_auth.bal
│ │ ├── websocket_client_bearer_token_auth.md
│ │ ├── websocket_client_bearer_token_auth.metatags
│ │ └── websocket_client_bearer_token_auth.out
│ ├── websocket-client-mutual-ssl/
│ │ ├── websocket_client_mutual_ssl.bal
│ │ ├── websocket_client_mutual_ssl.md
│ │ ├── websocket_client_mutual_ssl.metatags
│ │ └── websocket_client_mutual_ssl.out
│ ├── websocket-client-oauth2-client-cred-grant-type/
│ │ ├── websocket_client_oauth2_client_cred_grant_type.bal
│ │ ├── websocket_client_oauth2_client_cred_grant_type.md
│ │ ├── websocket_client_oauth2_client_cred_grant_type.metatags
│ │ └── websocket_client_oauth2_client_cred_grant_type.out
│ ├── websocket-client-oauth2-jwt-bearer-grant-type/
│ │ ├── websocket_client_oauth2_jwt_bearer_grant_type.bal
│ │ ├── websocket_client_oauth2_jwt_bearer_grant_type.md
│ │ ├── websocket_client_oauth2_jwt_bearer_grant_type.metatags
│ │ └── websocket_client_oauth2_jwt_bearer_grant_type.out
│ ├── websocket-client-oauth2-password-grant-type/
│ │ ├── websocket_client_oauth2_password_grant_type.bal
│ │ ├── websocket_client_oauth2_password_grant_type.md
│ │ ├── websocket_client_oauth2_password_grant_type.metatags
│ │ └── websocket_client_oauth2_password_grant_type.out
│ ├── websocket-client-oauth2-refresh-token-grant-type/
│ │ ├── websocket_client_oauth2_refresh_token_grant_type.bal
│ │ ├── websocket_client_oauth2_refresh_token_grant_type.md
│ │ ├── websocket_client_oauth2_refresh_token_grant_type.metatags
│ │ └── websocket_client_oauth2_refresh_token_grant_type.out
│ ├── websocket-client-payload-constraint-validation/
│ │ ├── websocket_client_payload_constraint_validation.bal
│ │ ├── websocket_client_payload_constraint_validation.md
│ │ ├── websocket_client_payload_constraint_validation.metatags
│ │ └── websocket_client_payload_constraint_validation.out
│ ├── websocket-client-self-signed-jwt-auth/
│ │ ├── websocket_client_self_signed_jwt_auth.bal
│ │ ├── websocket_client_self_signed_jwt_auth.md
│ │ ├── websocket_client_self_signed_jwt_auth.metatags
│ │ └── websocket_client_self_signed_jwt_auth.out
│ ├── websocket-client-ssl-tls/
│ │ ├── websocket_client_ssl_tls.bal
│ │ ├── websocket_client_ssl_tls.md
│ │ ├── websocket_client_ssl_tls.metatags
│ │ └── websocket_client_ssl_tls.out
│ ├── websocket-query-parameter/
│ │ ├── websocket_query_parameter.bal
│ │ ├── websocket_query_parameter.md
│ │ ├── websocket_query_parameter.metatags
│ │ └── websocket_query_parameter.server.out
│ ├── websocket-retry-client/
│ │ ├── websocket_retry_client.bal
│ │ ├── websocket_retry_client.md
│ │ ├── websocket_retry_client.metatags
│ │ └── websocket_retry_client.out
│ ├── websocket-service-basic-auth-file-user-store/
│ │ ├── websocket_service_basic_auth_file_user_store.bal
│ │ ├── websocket_service_basic_auth_file_user_store.md
│ │ ├── websocket_service_basic_auth_file_user_store.metatags
│ │ └── websocket_service_basic_auth_file_user_store.server.out
│ ├── websocket-service-basic-auth-ldap-user-store/
│ │ ├── websocket_service_basic_auth_ldap_user_store.bal
│ │ ├── websocket_service_basic_auth_ldap_user_store.md
│ │ ├── websocket_service_basic_auth_ldap_user_store.metatags
│ │ └── websocket_service_basic_auth_ldap_user_store.server.out
│ ├── websocket-service-error-handling/
│ │ ├── websocket_service_error_handling.bal
│ │ ├── websocket_service_error_handling.md
│ │ ├── websocket_service_error_handling.metatags
│ │ └── websocket_service_error_handling.out
│ ├── websocket-service-jwt-auth/
│ │ ├── websocket_service_jwt_auth.bal
│ │ ├── websocket_service_jwt_auth.md
│ │ ├── websocket_service_jwt_auth.metatags
│ │ └── websocket_service_jwt_auth.server.out
│ ├── websocket-service-mutual-ssl/
│ │ ├── websocket_service_mutual_ssl.bal
│ │ ├── websocket_service_mutual_ssl.md
│ │ ├── websocket_service_mutual_ssl.metatags
│ │ └── websocket_service_mutual_ssl.server.out
│ ├── websocket-service-oauth2/
│ │ ├── websocket_service_oauth2.bal
│ │ ├── websocket_service_oauth2.md
│ │ ├── websocket_service_oauth2.metatags
│ │ └── websocket_service_oauth2.server.out
│ ├── websocket-service-payload-constraint-validation/
│ │ ├── websocket_service_payload_constraint_validation.bal
│ │ ├── websocket_service_payload_constraint_validation.md
│ │ ├── websocket_service_payload_constraint_validation.metatags
│ │ └── websocket_service_payload_constraint_validation.out
│ ├── websocket-service-ssl-tls/
│ │ ├── websocket_service_ssl_tls.bal
│ │ ├── websocket_service_ssl_tls.md
│ │ ├── websocket_service_ssl_tls.metatags
│ │ └── websocket_service_ssl_tls.server.out
│ ├── websocket-text-client/
│ │ └── websocket_text_client.md
│ ├── websocket-timeout-client/
│ │ ├── websocket_timeout_client.bal
│ │ ├── websocket_timeout_client.md
│ │ ├── websocket_timeout_client.metatags
│ │ └── websocket_timeout_client.out
│ ├── websub-webhook-sample/
│ │ ├── websub_webhook_sample.bal
│ │ ├── websub_webhook_sample.md
│ │ ├── websub_webhook_sample.metatags
│ │ └── websub_webhook_sample.out
│ ├── while-statement/
│ │ ├── while_statement.bal
│ │ ├── while_statement.md
│ │ ├── while_statement.metatags
│ │ └── while_statement.out
│ ├── wildcard-binding-pattern/
│ │ ├── wildcard_binding_pattern.bal
│ │ ├── wildcard_binding_pattern.md
│ │ ├── wildcard_binding_pattern.metatags
│ │ └── wildcard_binding_pattern.out
│ ├── xml-access/
│ │ ├── xml_access.bal
│ │ ├── xml_access.md
│ │ ├── xml_access.metatags
│ │ └── xml_access.out
│ ├── xml-data-model/
│ │ ├── xml_data_model.bal
│ │ ├── xml_data_model.md
│ │ ├── xml_data_model.metatags
│ │ └── xml_data_model.out
│ ├── xml-from-json-conversion/
│ │ ├── xml_from_json_conversion.bal
│ │ ├── xml_from_json_conversion.md
│ │ ├── xml_from_json_conversion.metatags
│ │ └── xml_from_json_conversion.out
│ ├── xml-from-record-conversion/
│ │ ├── xml_from_record_conversion.bal
│ │ ├── xml_from_record_conversion.md
│ │ ├── xml_from_record_conversion.metatags
│ │ └── xml_from_record_conversion.out
│ ├── xml-iteration/
│ │ ├── xml_iteration.bal
│ │ ├── xml_iteration.md
│ │ ├── xml_iteration.metatags
│ │ └── xml_iteration.out
│ ├── xml-mutation/
│ │ ├── xml_mutation.bal
│ │ ├── xml_mutation.md
│ │ ├── xml_mutation.metatags
│ │ └── xml_mutation.out
│ ├── xml-namespaces/
│ │ ├── xml_namespaces.bal
│ │ ├── xml_namespaces.md
│ │ ├── xml_namespaces.metatags
│ │ └── xml_namespaces.out
│ ├── xml-navigation/
│ │ ├── xml_navigation.bal
│ │ ├── xml_navigation.md
│ │ ├── xml_navigation.metatags
│ │ └── xml_navigation.out
│ ├── xml-operations/
│ │ ├── xml_operations.bal
│ │ ├── xml_operations.md
│ │ ├── xml_operations.metatags
│ │ └── xml_operations.out
│ ├── xml-subtyping/
│ │ ├── xml_subtyping.bal
│ │ ├── xml_subtyping.md
│ │ ├── xml_subtyping.metatags
│ │ └── xml_subtyping.out
│ ├── xml-templates/
│ │ ├── xml_templates.bal
│ │ ├── xml_templates.md
│ │ ├── xml_templates.metatags
│ │ └── xml_templates.out
│ ├── xml-templates-and-query/
│ │ ├── xml_templates_and_query.bal
│ │ ├── xml_templates_and_query.md
│ │ ├── xml_templates_and_query.metatags
│ │ └── xml_templates_and_query.out
│ ├── xml-to-json-conversion/
│ │ ├── xml_to_json_conversion.bal
│ │ ├── xml_to_json_conversion.md
│ │ ├── xml_to_json_conversion.metatags
│ │ └── xml_to_json_conversion.out
│ ├── xml-to-record/
│ │ ├── xml_to_record.bal
│ │ ├── xml_to_record.md
│ │ ├── xml_to_record.metatags
│ │ └── xml_to_record.out
│ ├── xml-to-record-conversion/
│ │ ├── xml_to_record_conversion.bal
│ │ ├── xml_to_record_conversion.md
│ │ ├── xml_to_record_conversion.metatags
│ │ └── xml_to_record_conversion.out
│ ├── xml-to-record-with-projection/
│ │ ├── xml_to_record_with_projection.bal
│ │ ├── xml_to_record_with_projection.md
│ │ ├── xml_to_record_with_projection.metatags
│ │ └── xml_to_record_with_projection.out
│ ├── xmlns-declarations/
│ │ ├── xmlns_declarations.bal
│ │ ├── xmlns_declarations.md
│ │ ├── xmlns_declarations.metatags
│ │ └── xmlns_declarations.out
│ ├── xslt-transformation/
│ │ ├── xslt_transformation.bal
│ │ ├── xslt_transformation.md
│ │ ├── xslt_transformation.metatags
│ │ └── xslt_transformation.out
│ ├── yaml-to-anydata/
│ │ ├── yaml_to_anydata.bal
│ │ ├── yaml_to_anydata.md
│ │ ├── yaml_to_anydata.metatags
│ │ └── yaml_to_anydata.out
│ └── yaml-to-anydata-with-projection/
│ ├── yaml_to_anydata_with_projection.bal
│ ├── yaml_to_anydata_with_projection.md
│ ├── yaml_to_anydata_with_projection.metatags
│ └── yaml_to_anydata_with_projection.out
├── gradle/
│ ├── javaProject.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── installers/
│ ├── linux-deb/
│ │ ├── README.md
│ │ ├── build-ballerina-linux-deb-x64.sh
│ │ └── resources/
│ │ ├── amd/
│ │ │ └── DEBIAN/
│ │ │ ├── control
│ │ │ ├── postinst
│ │ │ └── postrm
│ │ └── arm/
│ │ └── DEBIAN/
│ │ ├── control
│ │ ├── postinst
│ │ └── postrm
│ ├── linux-rpm/
│ │ ├── build-ballerina-linux-rpm-x64.sh
│ │ ├── resources/
│ │ │ ├── ballerina-runtime.spec
│ │ │ └── ballerina-tools.spec
│ │ └── rpmbuild/
│ │ ├── BUILDROOT/
│ │ │ └── .gitkeep
│ │ ├── RPMS/
│ │ │ └── .gitkeep
│ │ ├── SOURCES/
│ │ │ └── .gitkeep
│ │ ├── SPECS/
│ │ │ └── .gitkeep
│ │ └── SRPMS/
│ │ └── .gitkeep
│ ├── mac/
│ │ ├── build-ballerina-macos-x64.sh
│ │ └── darwin/
│ │ ├── Distribution
│ │ ├── app.plist
│ │ └── scripts/
│ │ └── postinstall
│ └── windows/
│ ├── build-ballerina-windows-x64.bat
│ └── resources/
│ ├── cert/
│ │ └── ballerina-cert.pfx
│ ├── en-us.wxl
│ └── installer.wxs
├── issue_template.md
├── language-server-simulator/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── java/
│ │ ├── module-info.java
│ │ └── org/
│ │ └── ballerinalang/
│ │ └── langserver/
│ │ └── simulator/
│ │ ├── Editor.java
│ │ ├── EditorOutputStream.java
│ │ ├── EditorSimulator.java
│ │ ├── EditorTab.java
│ │ └── generators/
│ │ ├── ClassGenerator.java
│ │ ├── CodeSnippetGenerator.java
│ │ ├── FunctionGenerator.java
│ │ ├── Generators.java
│ │ ├── ImportStatementGenerator.java
│ │ ├── MatchStatementGenerator.java
│ │ ├── ServiceGenerator.java
│ │ ├── StatementGenerator.java
│ │ ├── TypeDefinitionGenerator.java
│ │ └── VarDeclarationStatementGenerator.java
│ └── resources/
│ └── META-INF/
│ └── services/
│ └── org.ballerinalang.langserver.simulator.generators.CodeSnippetGenerator
├── project-api-tests/
│ ├── build.gradle
│ └── src/
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── ballerina/
│ │ └── projectapi/
│ │ ├── BalToolTest.java
│ │ ├── BuildTimeTest.java
│ │ ├── CentralNegativeTest.java
│ │ ├── CentralTest.java
│ │ ├── CentralTestUtils.java
│ │ ├── DistributionCompatibilityTest.java
│ │ ├── HierarchicalPackagesTest.java
│ │ ├── MavenCustomRepoTest.java
│ │ ├── MavenCustomRepoTestUtils.java
│ │ └── TestUtils.java
│ └── resources/
│ ├── bal-tool/
│ │ ├── DistTestCommand/
│ │ │ ├── build.gradle
│ │ │ ├── gradlew
│ │ │ ├── gradlew.bat
│ │ │ ├── settings.gradle
│ │ │ └── src/
│ │ │ └── main/
│ │ │ ├── java/
│ │ │ │ └── disttest/
│ │ │ │ └── cli/
│ │ │ │ └── DistTestCommand.java
│ │ │ └── resources/
│ │ │ ├── META-INF/
│ │ │ │ └── services/
│ │ │ │ └── io.ballerina.cli.BLauncherCmd
│ │ │ └── disttest.help
│ │ ├── cmd-outputs/
│ │ │ ├── tool-execute-general-help.txt
│ │ │ ├── tool-execute-specific-help-1.0.0.txt
│ │ │ ├── tool-execute-specific-help-1.1.0.txt
│ │ │ ├── tool-execute-tool.txt
│ │ │ ├── tool-execute-unknown-cmd-non-existing.txt
│ │ │ ├── tool-execute-unknown-cmd.txt
│ │ │ ├── tool-list-with-multiple-tool-versions.txt
│ │ │ ├── tool-list-with-no-tools.txt
│ │ │ ├── tool-pull-again-with-specific-version.txt
│ │ │ ├── tool-pull-again-without-version.txt
│ │ │ ├── tool-pull-with-incompatible-dist.txt
│ │ │ ├── tool-pull-with-non-existing-version.txt
│ │ │ ├── tool-pull-with-specific-version.txt
│ │ │ ├── tool-pull-without-version.txt
│ │ │ ├── tool-remove-active-version.txt
│ │ │ ├── tool-remove-all.txt
│ │ │ ├── tool-remove-non-existing-tool.txt
│ │ │ ├── tool-remove-non-existing-version.txt
│ │ │ ├── tool-remove-specific-version.txt
│ │ │ ├── tool-remove-with-incompatible-dist.txt
│ │ │ ├── tool-search-with-tool-id.txt
│ │ │ ├── tool-update-non-existing.txt
│ │ │ ├── tool-update-with-new-patch-and-minor.txt
│ │ │ ├── tool-update-with-no-new-version.txt
│ │ │ ├── tool-use-active-version.txt
│ │ │ ├── tool-use-new-version.txt
│ │ │ ├── tool-use-non-existent-version.txt
│ │ │ ├── tool-use-old-version.txt
│ │ │ └── tool-use-with-incompatible-dist.txt
│ │ ├── v1.0.0/
│ │ │ └── disttestpackage/
│ │ │ ├── BalTool.toml
│ │ │ ├── Ballerina.toml
│ │ │ └── README.md
│ │ ├── v1.0.1/
│ │ │ └── disttestpackage/
│ │ │ ├── BalTool.toml
│ │ │ ├── Ballerina.toml
│ │ │ └── Package.md
│ │ ├── v1.0.4/
│ │ │ └── disttestpackage/
│ │ │ ├── BalTool.toml
│ │ │ ├── Ballerina.toml
│ │ │ └── README.md
│ │ └── v1.1.0/
│ │ └── disttestpackage/
│ │ ├── BalTool.toml
│ │ ├── Ballerina.toml
│ │ └── README.md
│ ├── build-time/
│ │ ├── Project1/
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── Project2/
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ └── Project3/
│ │ ├── Ballerina.toml
│ │ ├── main.bal
│ │ └── tests/
│ │ └── test.bal
│ ├── central/
│ │ ├── projectA/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── projectB/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── projectC/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── projectD/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── projectE/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── projectF/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── projectG/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ └── projectSnapshot/
│ │ ├── Ballerina.toml
│ │ ├── Package.md
│ │ └── main.bal
│ ├── distribution-tests/
│ │ ├── disttestmultiples/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ ├── bctestorg-disttestmultiples-any-1.1.0.bala
│ │ │ └── disttestmultiples.bal
│ │ ├── disttestpack1/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── disttestpack1.bal
│ │ ├── disttestpack2/
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── disttestpack3/
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── disttestpack4/
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── disttestpack5/
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── disttestpackbeta6/
│ │ │ └── bctestorg-disttestpackbeta6-any-1.1.0.bala
│ │ └── forwardpack1/
│ │ └── bctestorg-forwardpack1-any-1.1.0.bala
│ ├── hierarchical-packages/
│ │ ├── PackageH.test/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ ├── lib.bal
│ │ │ └── modules/
│ │ │ └── mod.api/
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── mod.api.bal
│ │ ├── PackageH.test.mod/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ ├── lib.bal
│ │ │ └── modules/
│ │ │ ├── api/
│ │ │ │ ├── Module.md
│ │ │ │ ├── Package.md
│ │ │ │ └── api.bal
│ │ │ └── api.doc/
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── api.doc.bal
│ │ ├── PackageI/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ └── main.bal
│ │ ├── PackageJ.test/
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── lib.bal
│ │ ├── PackageK/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ └── main.bal
│ │ ├── PackageL.test/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ ├── lib.bal
│ │ │ └── modules/
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── mod.api/
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── mod.api.bal
│ │ ├── PackageM/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ └── main.bal
│ │ ├── PackageN/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ └── main.bal
│ │ ├── PackageO.test/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── lib.bal
│ │ ├── PackageP/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── PackageQ.test/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ ├── lib.bal
│ │ │ └── modules/
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── mod.api/
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── mod.api.bal
│ │ ├── PackageR/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ └── main.bal
│ │ ├── PackageR.test/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── lib.bal
│ │ ├── PackageS/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ └── main.bal
│ │ ├── PackageT.test/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Module.md
│ │ │ ├── Package.md
│ │ │ └── lib.bal
│ │ ├── PackageU/
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Dependencies-template.toml
│ │ │ └── main.bal
│ │ └── Transitive.PackageH.test/
│ │ ├── .gitignore
│ │ ├── Ballerina.toml
│ │ ├── Module.md
│ │ ├── Package.md
│ │ └── lib.bal
│ ├── maven-repos/
│ │ ├── myproject1/
│ │ │ ├── .devcontainer.json
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── pact/
│ │ │ ├── .devcontainer.json
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── pkg1/
│ │ │ ├── .devcontainer.json
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── pkg2/
│ │ │ ├── .devcontainer.json
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ ├── pkg3/
│ │ │ ├── .devcontainer.json
│ │ │ ├── .gitignore
│ │ │ ├── Ballerina.toml
│ │ │ ├── Package.md
│ │ │ └── main.bal
│ │ └── test-resolution/
│ │ ├── .devcontainer.json
│ │ ├── .gitignore
│ │ ├── Ballerina.toml
│ │ └── main.bal
│ └── testng.xml
├── pull_request_template.md
├── release-notes.md
├── resources/
│ └── tools/
│ ├── COPYRIGHT.txt
│ ├── INSTALL.txt
│ ├── LICENSE.txt
│ ├── PATENTS.txt
│ ├── README.txt
│ ├── distributions/
│ │ ├── ballerina-version
│ │ └── installer-version
│ └── scripts/
│ ├── install
│ └── install.bat
├── settings.gradle
└── stdlib-integration-tests/
├── auth/
│ └── tests/
│ ├── Config.toml
│ ├── auth_test.bal
│ └── resources/
│ └── keystore/
│ ├── ballerinaKeystore.p12
│ └── ballerinaTruststore.p12
├── crypto/
│ └── tests/
│ ├── crypto_test.bal
│ └── resources/
│ └── keystore/
│ ├── ballerinaKeystore.p12
│ └── ballerinaTruststore.p12
├── email/
│ └── tests/
│ └── email_test.bal
├── ftp/
│ └── tests/
│ └── ftp_test.bal
├── http/
│ └── tests/
│ ├── http_1_1_passthrough_test.bal
│ ├── http_2_0_server_push_test.bal
│ └── rest_introspection_test.bal
├── index.json
├── jwt/
│ └── tests/
│ ├── jwt_test.bal
│ └── resources/
│ └── keystore/
│ ├── ballerinaKeystore.p12
│ ├── ballerinaTruststore.p12
│ └── expiredTruststore.p12
├── library_package_test/
│ ├── Ballerina.toml
│ └── main.bal
├── mime/
│ └── tests/
│ └── mime_test.bal
├── oauth2/
│ └── tests/
│ ├── oauth2_test.bal
│ └── resources/
│ └── keystore/
│ ├── ballerinaKeystore.p12
│ └── ballerinaTruststore.p12
├── task/
│ └── tests/
│ └── http_service.bal
├── tcp/
│ └── tests/
│ ├── mock_servers.bal
│ ├── resources/
│ │ ├── private.key
│ │ └── public.crt
│ └── tcp_tests.bal
├── transaction/
│ └── tests/
│ └── xa_transactions_test.bal
├── udp/
│ └── tests/
│ ├── mock_servers.bal
│ └── udp_tests.bal
├── url/
│ └── tests/
│ └── url_test.bal
├── websocket/
│ └── tests/
│ └── web_socket_test.bal
├── websub/
│ └── tests/
│ └── web_sub_test.bal
├── websub-advance/
│ └── tests/
│ ├── constants.bal
│ ├── resources/
│ │ └── security/
│ │ ├── ballerinaKeystore.p12
│ │ └── ballerinaTruststore.p12
│ ├── test_subscribers_at_basic_auth_secured_hub.bal
│ ├── test_subscribers_at_persistence_enabled_hub.bal
│ └── utils.bal
└── websub-generic/
└── tests/
├── 01_websub_publisher.bal
├── 02_redirection_publishers.bal
├── constants.bal
├── resources/
│ └── security/
│ ├── ballerinaKeystore.p12
│ └── ballerinaTruststore.p12
├── test_content_negotiation.bal
├── test_custom_subscribers.bal
├── test_different_content_type_subscribers.bal
├── test_multiple_subscribers.bal
├── test_redirected_subscribers.bal
├── test_subscriber.bal
├── test_subscriber_startup.bal
├── test_unsubscription_client.bal
└── utils.bal
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Ensure all Java files use LF.
*.java eol=lf
================================================
FILE: .github/CODEOWNERS
================================================
# This is a comment.
# Each line is a file pattern followed by one or more owners.
# See: https://help.github.com/articles/about-codeowners/
* @keizer619
# Components
/ballerina-test-automation/ @keizer619
/ballerina-test/ @keizer619
/build-time-tests/ @azinneera
/cache-generator/ @warunalakshitha
/config/ @keizer619
/devtools-integration-tests/ @azinneera
/dist-repo-builder/ @keizer619
/examples/ @MaryamZi @NipunaRanasinghe @gimantha
/gradle/ @keizer619
/installers/ @keizer619
/language-server-simulator/ @kavinduZoysa
/project-api-tests/ @azinneera
/resources/ @keizer619
/stdlib-integration-tests/ @NipunaRanasinghe
================================================
FILE: .github/ISSUE_TEMPLATE/bug.yml
================================================
name: "🐞 Report a Bug"
description: Create an issue if something does not work as expected.
labels: ["Type/Bug"]
body:
- type: textarea
id: background
attributes:
label: Description
description: Please share a clear and concise description of the problem.
placeholder: Description
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: List the steps you followed when you encountered the issue. Provide sample source code to reproduce the issue where applicable.
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: Enter product/component version.
validations:
required: true
- type: textarea
id: environment
attributes:
label: Environment Details (with versions)
description: Mention the environment details (OS, Client, etc.) that the product is running on.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: '📚 Documentation Issue'
about: Request a new article, missing topic, or report an issue if a topic is incorrect in the current documentation.
url: https://github.com/ballerina-platform/ballerina-dev-website/issues/new/choose
- name: General Question
url: https://stackoverflow.com/questions/tagged/ballerina
about: "If you have a question then please ask on Stack Overflow using the #ballerina tag."
- name: Chat on Ballerina Discord Channel
url: https://discord.gg/ballerinalang
about: "Chat about anything else with the community."
================================================
FILE: .github/ISSUE_TEMPLATE/improvement.yml
================================================
name: "🚀 Improvement Request"
description: Suggest an improvement to the product.
labels: ["Type/Improvement"]
body:
- type: textarea
id: limitation
attributes:
label: Current Limitation
description: Describe the the current limitation.
validations:
required: true
- type: textarea
id: suggestion
attributes:
label: Suggested Improvement
description: Describe the the improvement you suggest.
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: Enter component version.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/new-feature.yml
================================================
name: "💡 New Feature Request"
description: Suggest new functionality and features for the product.
labels: ["Type/NewFeature"]
body:
- type: textarea
id: problem
attributes:
label: Problem
description: What is the problem this feature will solve?
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed Solution
description: Describe the solution you'd like to have.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: Describe any alternatives have you considered
validations:
required: false
- type: input
id: version
attributes:
label: Version
description: Enter product/component version.
validations:
required: false
================================================
FILE: .github/ISSUE_TEMPLATE/task.yml
================================================
name: "✍️ Create a Task"
description: Create a new task.
labels: ["Type/Task"]
body:
- type: textarea
id: description
attributes:
label: Description
description: A clear description of what needs to be done.
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: Enter product/component version.
validations:
required: false
================================================
FILE: .github/workflows/add_reason_labels.yml
================================================
on:
issues:
types: [closed]
jobs:
check-resolution-label:
runs-on: ubuntu-latest
if:
${{ contains(github.event.issue.labels.*.name, 'Type/Bug') && !(contains(github.event.issue.labels.*.name, 'Reason/EngineeringMistake') ||
contains(github.event.issue.labels.*.name, 'Reason/Complex') ||
contains(github.event.issue.labels.*.name, 'Reason/Regression') ||
contains(github.event.issue.labels.*.name, 'Reason/Other') ||
contains(github.event.issue.labels.*.name, 'Reason/Invalid') ||
contains(github.event.issue.labels.*.name, 'Reason/MultipleComponentInteraction')) }}
steps:
- run: echo Resolution label is not set
- run: gh issue comment $ISSUE --body "This issue is **NOT** closed with a proper **Reason/** label. Make sure to add proper reason label before closing. Please add or leave a comment with the proper reason label now.
- **Reason/EngineeringMistake** - The issue occurred due to a mistake made in the past.
- **Reason/Regression** - The issue has introduced a regression.
- **Reason/MultipleComponentInteraction** - Issue occured due to interactions in multiple components.
- **Reason/Complex** - Issue occurred due to complex scenario.
- **Reason/Invalid** - Issue is invalid.
- **Reason/Other** - None of the above cases."
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE: ${{ github.event.issue.html_url }}
================================================
FILE: .github/workflows/bbe-on-demand.yml
================================================
name: Run BBE On Demand
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to run BBE tests against'
required: true
default: 'master'
javaVersion:
description: 'Java version to run BBE tests on'
required: true
default: '25'
type: choice
options:
- '21'
- '25'
jobs:
bbe-tests:
name: Run BBE on Java ${{ github.event.inputs.javaVersion }}
if: github.repository_owner == 'ballerina-platform'
runs-on: ubuntu-latest
concurrency:
group: ${{ github.ref }}-manual-bbe-tests
cancel-in-progress: true
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.branch }}
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
- name: Build Ballerina pack
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: ${{ secrets.BALLERINA_BOT_WORKFLOW }}
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x :ballerina:testExamples -x :project-api-tests:test
- name: Set up JDK ${{ github.event.inputs.javaVersion }} for BBE tests
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ github.event.inputs.javaVersion }}
- name: Run BBE tests
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: ${{ secrets.BALLERINA_BOT_WORKFLOW }}
run: |
java -version
./gradlew :ballerina:testExamples --stacktrace --scan --console=plain --no-daemon --continue -x :project-api-tests:test
================================================
FILE: .github/workflows/daily-build-2201.12.x.yml
================================================
name: Daily build (2201.12.x)
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # 07:30 in LK time (GMT+5:30)
jobs:
ubuntu-build:
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
ref: 2201.12.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Get daily docker version
id: version
run: echo "::set-output name=version::$(date +'%Y-%m-%d')"
- name: Get project version
id: project-version
run: |
SHORT_VERSION=$((grep -w "version" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
DIST_VERSION=$((grep -w "version" | cut -d= -f2 | xargs) < gradle.properties)
LANG_VERSION=$((grep -w "ballerinaLangVersion" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION-$CODE_NAME
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
echo "::set-output name=langversion::$LANG_VERSION"
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x project-api-tests:test
- name: Create linux-deb
id: run_installers_deb
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-deb successfully"
- name: Create linux-rpm
id: run_installers_rpm
run: |
cd installers/linux-rpm
./build-ballerina-linux-rpm-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-rpm successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb.sha256 installers/linux-deb/target/ballerina-*-linux-x64.deb
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}-linux-x64.rpm.sha256 installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.version }}.zip
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.sversion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Ballerina ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina ZIP
path: ballerina/build/distributions/ballerina-*-swan-lake.zip
- name: Archive Ballerina Short Name ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name ZIP
path: ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Linux deb
uses: actions/upload-artifact@v4
with:
name: Linux Installer deb
path: installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Archive Linux rpm
uses: actions/upload-artifact@v4
with:
name: Linux Installer rpm
path: installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
- name: Archive MacOS Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ZIP
path: ballerina/build/distributions/ballerina-*-macos.zip
- name: Archive MacOS-ARM Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM Installer ZIP
path: ballerina/build/distributions/ballerina-*-macos-arm.zip
- name: Archive Windows Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Windows Installer ZIP
path: ballerina/build/distributions/ballerina-*-windows.zip
- name: Archive Linux deb Hashes
uses: actions/upload-artifact@v4
with:
name: Linux deb Hashes
path: ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb.sha256
- name: Archive Linux rpm Hashes
uses: actions/upload-artifact@v4
with:
name: Linux rpm Hashes
path: ballerina-${{ steps.project-version.outputs.version }}-linux-x64.rpm.sha256
- name: Archive Ballerina Zip Hashes
uses: actions/upload-artifact@v4
with:
name: Ballerina Zip Hashes
path: ballerina-${{ steps.project-version.outputs.version }}.zip.sha256
- name: Archive Ballerina Short Name Hashes
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name Hashes
path: ballerina-${{ steps.project-version.outputs.sversion }}.zip.sha256
- name: Install Ballerina DEB
run: sudo dpkg -i installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ steps.project-version.outputs.langversion }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
- name: Checkout docker repo
uses: actions/checkout@v3
with:
repository: ballerina-platform/module-ballerina-docker
path: module-ballerina-docker
- name: Copy zip artifact
run: cp ballerina/build/distributions/ballerina-22*.zip module-ballerina-docker/base/docker/
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build the docker image
id: docker_build
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
load: true
push: false
tags: ballerina/ballerina:nightly-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina:nightly-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
push: true
tags: ballerina/ballerina:nightly-2201.12.x
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Copy deb artifact
run: cp installers/linux-deb/target/ballerina-*-linux-x64.deb module-ballerina-docker/base/devcontainer/
- name: Build the dev container docker image
id: docker_build_devcontainer
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/devcontainer/
load: true
push: false
tags: ballerina/ballerina-devcontainer:nightly-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb
- name: Run Trivy vulnerability scanner for dev container docker image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina-devcontainer:nightly-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
- name: Build and push dev container image
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/devcontainer/
push: true
tags: ballerina/ballerina-devcontainer:nightly-2201.12.x
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb
- name: Notify failure
if: ${{ failure() }}
run: |
curl -X POST \
'https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches' \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"notify-build-failure\",
\"client_payload\": {
\"repoName\": \"ballerina-distribution\",
\"branch\": \"2201.12.x\"
}
}"
outputs:
project-version: ${{ steps.project-version.outputs.version }}
lang-version: ${{ steps.project-version.outputs.langversion }}
project-api-tests:
runs-on: ubuntu-latest
if: false
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.12.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
prodCentralToken: ${{ secrets.BALLERINA_CENTRAL_ACCESS_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew :project-api-tests:test --stacktrace --scan --console=plain --no-daemon --continue
windows-build:
runs-on: windows-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.12.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew.bat clean build --stacktrace --scan --console=plain --no-daemon -x test
ubuntu-rpm-installer-test:
needs: ubuntu-build
runs-on: ubuntu-latest
container: centos:latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.12.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Setup Files
run: |
cd /etc/yum.repos.d/
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
- name: Download Ballerina rpm Installer
uses: actions/download-artifact@v4
with:
name: Linux Installer rpm
- name: Install Ballerina RPM
run: |
rpm -ivh ballerina-*-linux-x64.rpm
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
sed -i -e "s/swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
sed -i -e "s/swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
macos-installer-build:
needs: ubuntu-build
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.12.x
- name: Download MacOS Intaller Zip
uses: actions/download-artifact@v4
with:
name: MacOS Installer ZIP
- name: Create macos-pkg
id: run_installers_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../
echo "Created macos-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-x64.pkg.sha256 installers/mac/target/pkg/ballerina-*-macos-x64.pkg
- name: Archive MacOS pkg Hashes
uses: actions/upload-artifact@v4
with:
name: MacOS pkg Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-x64.pkg.sha256
- name: Archive MacOS pkg
uses: actions/upload-artifact@v4
with:
name: MacOS Installer pkg
path: installers/mac/target/pkg/ballerina-*-macos-x64.pkg
- name: Install Ballerina PKG
run: sudo installer -pkg installers/mac/target/pkg/ballerina-*-macos-x64.pkg -target /
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
macos-arm-installer-build:
needs: ubuntu-build
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Download MacOS-ARM Intaller Zip
uses: actions/download-artifact@v4
with:
name: MacOS-ARM Installer ZIP
- name: Create macos-arm-pkg
id: run_installers_arm_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../ -a arm
echo "Created macos-arm-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-arm-x64.pkg.sha256 installers/mac/target/pkg/ballerina-*-macos-arm-x64.pkg
- name: Archive MacOS-ARM pkg Hashes
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM pkg Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-arm-x64.pkg.sha256
- name: Archive MacOS-ARM pkg
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ARM pkg
path: installers/mac/target/pkg/ballerina-*-macos-arm-x64.pkg
windows-installer-build:
needs: ubuntu-build
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.12.x
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '2.1.x'
- name: Install GUID Generator
run: dotnet tool install -g dotnet-guid --version 0.5.2
- name: Set up Wix toolkit
run: echo "${WIX}bin" >> $GITHUB_PATH
shell: bash
- name: Download Windows Intaller Zip
uses: actions/download-artifact@v4
with:
name: Windows Installer ZIP
- name: Create windows-msi
id: run_installers_msi
run: |
move installers\windows .\..\..\
cd ..\..\windows
.\build-ballerina-windows-x64.bat --version ${{ needs.ubuntu-build.outputs.project-version }} --path .\..\ballerina-distribution\ballerina-distribution
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi.sha256 D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Archive Windows msi Hashes
uses: actions/upload-artifact@v4
with:
name: Windows msi Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi.sha256
- name: Archive Windows msi
uses: actions/upload-artifact@v4
with:
name: Windows Installer msi
path: D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Install Ballerina msi
run: msiexec /i D:\a\windows\target\msi\ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi /quiet /qr
shell: cmd
- name: Update Installer Test Configs
run: |
set DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
set SWAN_LAKE_LATEST_VERSION=swan-lake-%DISPLAY_TEXT%
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=%DISPLAY_TEXT%/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=%SWAN_LAKE_LATEST_VERSION%/" ballerina-test-automation/gradle.properties
shell: cmd
- name: Run Installer Tests
working-directory: .\ballerina-test-automation\installer-test
run: |
$env:Path += ";C:\Program Files\Ballerina\bin"
.\..\gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
trigger-notifications:
needs: [ubuntu-build, macos-installer-build, windows-installer-build]
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.12.x
- name: Invoke Connector Ballerina Version Bump
run: |
ballerinaVersion=$((grep -w 'ballerinaLangVersion' | cut -d= -f2) < gradle.properties)
echo "Triggering connectors dependency bumps..." && \
curl -X POST \
https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: token ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"connector-update\",
\"client_payload\": {
\"ballerinaVersion\": \"$ballerinaVersion\"
}
}"
================================================
FILE: .github/workflows/daily-build-2201.13.x.yml
================================================
name: Daily build (2201.13.x)
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # 07:30 in LK time (GMT+5:30)
jobs:
ubuntu-build:
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
ref: 2201.13.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Get daily docker version
id: version
run: echo "::set-output name=version::$(date +'%Y-%m-%d')"
- name: Get project version
id: project-version
run: |
SHORT_VERSION=$((grep -w "version" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
DIST_VERSION=$((grep -w "version" | cut -d= -f2 | xargs) < gradle.properties)
LANG_VERSION=$((grep -w "ballerinaLangVersion" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION-$CODE_NAME
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
echo "::set-output name=langversion::$LANG_VERSION"
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x project-api-tests:test
- name: Create linux-deb
id: run_installers_deb
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-deb successfully"
- name: Create linux-rpm
id: run_installers_rpm
run: |
cd installers/linux-rpm
./build-ballerina-linux-rpm-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-rpm successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb.sha256 installers/linux-deb/target/ballerina-*-linux-x64.deb
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}-linux-x64.rpm.sha256 installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.version }}.zip
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.sversion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Ballerina ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina ZIP
path: ballerina/build/distributions/ballerina-*-swan-lake.zip
- name: Archive Ballerina Short Name ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name ZIP
path: ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Linux deb
uses: actions/upload-artifact@v4
with:
name: Linux Installer deb
path: installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Archive Linux rpm
uses: actions/upload-artifact@v4
with:
name: Linux Installer rpm
path: installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
- name: Archive MacOS Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ZIP
path: ballerina/build/distributions/ballerina-*-macos.zip
- name: Archive MacOS-ARM Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM Installer ZIP
path: ballerina/build/distributions/ballerina-*-macos-arm.zip
- name: Archive Windows Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Windows Installer ZIP
path: ballerina/build/distributions/ballerina-*-windows.zip
- name: Archive Linux deb Hashes
uses: actions/upload-artifact@v4
with:
name: Linux deb Hashes
path: ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb.sha256
- name: Archive Linux rpm Hashes
uses: actions/upload-artifact@v4
with:
name: Linux rpm Hashes
path: ballerina-${{ steps.project-version.outputs.version }}-linux-x64.rpm.sha256
- name: Archive Ballerina Zip Hashes
uses: actions/upload-artifact@v4
with:
name: Ballerina Zip Hashes
path: ballerina-${{ steps.project-version.outputs.version }}.zip.sha256
- name: Archive Ballerina Short Name Hashes
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name Hashes
path: ballerina-${{ steps.project-version.outputs.sversion }}.zip.sha256
- name: Install Ballerina DEB
run: sudo dpkg -i installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ steps.project-version.outputs.langversion }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
- name: Checkout docker repo
uses: actions/checkout@v3
with:
repository: ballerina-platform/module-ballerina-docker
path: module-ballerina-docker
- name: Copy zip artifact
run: cp ballerina/build/distributions/ballerina-22*.zip module-ballerina-docker/base/docker/
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build the docker image
id: docker_build
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
load: true
push: false
tags: ballerina/ballerina:nightly-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina:nightly-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
push: true
tags: ballerina/ballerina:nightly-2201.13.x
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Copy deb artifact
run: cp installers/linux-deb/target/ballerina-*-linux-x64.deb module-ballerina-docker/base/devcontainer/
- name: Build the dev container docker image
id: docker_build_devcontainer
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/devcontainer/
load: true
push: false
tags: ballerina/ballerina-devcontainer:nightly-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb
- name: Run Trivy vulnerability scanner for dev container docker image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina-devcontainer:nightly-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
- name: Build and push dev container image
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/devcontainer/
push: true
tags: ballerina/ballerina-devcontainer:nightly-2201.13.x
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb
- name: Notify failure
if: ${{ failure() }}
run: |
curl -X POST \
'https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches' \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"notify-build-failure\",
\"client_payload\": {
\"repoName\": \"ballerina-distribution\",
\"branch\": \"2201.13.x\"
}
}"
outputs:
project-version: ${{ steps.project-version.outputs.version }}
lang-version: ${{ steps.project-version.outputs.langversion }}
project-api-tests:
runs-on: ubuntu-latest
if: false
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.13.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
prodCentralToken: ${{ secrets.BALLERINA_CENTRAL_ACCESS_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew :project-api-tests:test --stacktrace --scan --console=plain --no-daemon --continue
windows-build:
runs-on: windows-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.13.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew.bat clean build --stacktrace --scan --console=plain --no-daemon -x test
ubuntu-rpm-installer-test:
needs: ubuntu-build
runs-on: ubuntu-latest
container: centos:latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.13.x
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Setup Files
run: |
cd /etc/yum.repos.d/
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
- name: Download Ballerina rpm Installer
uses: actions/download-artifact@v4
with:
name: Linux Installer rpm
- name: Install Ballerina RPM
run: |
rpm -ivh ballerina-*-linux-x64.rpm
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
sed -i -e "s/swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
sed -i -e "s/swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
macos-installer-build:
needs: ubuntu-build
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.13.x
- name: Download MacOS Intaller Zip
uses: actions/download-artifact@v4
with:
name: MacOS Installer ZIP
- name: Create macos-pkg
id: run_installers_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../
echo "Created macos-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-x64.pkg.sha256 installers/mac/target/pkg/ballerina-*-macos-x64.pkg
- name: Archive MacOS pkg Hashes
uses: actions/upload-artifact@v4
with:
name: MacOS pkg Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-x64.pkg.sha256
- name: Archive MacOS pkg
uses: actions/upload-artifact@v4
with:
name: MacOS Installer pkg
path: installers/mac/target/pkg/ballerina-*-macos-x64.pkg
- name: Install Ballerina PKG
run: sudo installer -pkg installers/mac/target/pkg/ballerina-*-macos-x64.pkg -target /
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
macos-arm-installer-build:
needs: ubuntu-build
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Download MacOS-ARM Intaller Zip
uses: actions/download-artifact@v4
with:
name: MacOS-ARM Installer ZIP
- name: Create macos-arm-pkg
id: run_installers_arm_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../ -a arm
echo "Created macos-arm-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-arm-x64.pkg.sha256 installers/mac/target/pkg/ballerina-*-macos-arm-x64.pkg
- name: Archive MacOS-ARM pkg Hashes
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM pkg Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-arm-x64.pkg.sha256
- name: Archive MacOS-ARM pkg
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ARM pkg
path: installers/mac/target/pkg/ballerina-*-macos-arm-x64.pkg
windows-installer-build:
needs: ubuntu-build
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.13.x
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '2.1.x'
- name: Install GUID Generator
run: dotnet tool install -g dotnet-guid --version 0.5.2
- name: Set up Wix toolkit
run: echo "${WIX}bin" >> $GITHUB_PATH
shell: bash
- name: Download Windows Intaller Zip
uses: actions/download-artifact@v4
with:
name: Windows Installer ZIP
- name: Create windows-msi
id: run_installers_msi
run: |
move installers\windows .\..\..\
cd ..\..\windows
.\build-ballerina-windows-x64.bat --version ${{ needs.ubuntu-build.outputs.project-version }} --path .\..\ballerina-distribution\ballerina-distribution
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi.sha256 D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Archive Windows msi Hashes
uses: actions/upload-artifact@v4
with:
name: Windows msi Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi.sha256
- name: Archive Windows msi
uses: actions/upload-artifact@v4
with:
name: Windows Installer msi
path: D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Install Ballerina msi
run: msiexec /i D:\a\windows\target\msi\ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi /quiet /qr
shell: cmd
- name: Update Installer Test Configs
run: |
set DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
set SWAN_LAKE_LATEST_VERSION=swan-lake-%DISPLAY_TEXT%
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=%DISPLAY_TEXT%/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=%SWAN_LAKE_LATEST_VERSION%/" ballerina-test-automation/gradle.properties
shell: cmd
- name: Run Installer Tests
working-directory: .\ballerina-test-automation\installer-test
run: |
$env:Path += ";C:\Program Files\Ballerina\bin"
.\..\gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
trigger-notifications:
needs: [ubuntu-build, macos-installer-build, windows-installer-build]
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: 2201.13.x
- name: Invoke Connector Ballerina Version Bump
run: |
ballerinaVersion=$((grep -w 'ballerinaLangVersion' | cut -d= -f2) < gradle.properties)
echo "Triggering connectors dependency bumps..." && \
curl -X POST \
https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: token ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"connector-update\",
\"client_payload\": {
\"ballerinaVersion\": \"$ballerinaVersion\"
}
}"
================================================
FILE: .github/workflows/daily-build-editor.yml
================================================
name: Daily build editor
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # 07:30 in LK time (GMT+5:30)
jobs:
ubuntu-build:
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '10.22.1'
- name: Checkout Dev Tools Repository
uses: actions/checkout@v3
with:
repository: ballerina-platform/ballerina-dev-tools
path: ballerina-dev-tools
- name: Build with Gradle
id: build-dev-tools
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: |
cd ballerina-dev-tools
echo "::set-output name=version::$(grep "^version=" gradle.properties | cut -d'=' -f2)"
echo "::set-output name=langVersion::$(grep "^ballerinaLangVersion=" gradle.properties | cut -d'=' -f2)"
./gradlew clean build --stacktrace --scan -x test --console=plain --no-daemon --continue publishToMavenLocal
cd ..
- name: Checkout Distribution Repository
uses: actions/checkout@v3
with:
repository: ballerina-platform/ballerina-distribution
path: ballerina-distribution
- name: Get daily docker version
id: version
run: echo "::set-output name=version::$(date +'%Y-%m-%d')"
- name: Get project version
id: project-version
run: |
cd ballerina-distribution
SHORT_VERSION=$((grep -w "version" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
DIST_VERSION=$((grep -w "version" | cut -d= -f2 | xargs) < gradle.properties)
LANG_VERSION=$((grep -w "ballerinaLangVersion" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION-$CODE_NAME
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
echo "::set-output name=langversion::$LANG_VERSION"
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: |
cd ballerina-distribution
sed -i "s/^devToolsVersion=.*/devToolsVersion=${{ steps.build-dev-tools.outputs.version }}/" gradle.properties
sed -i "s/^ballerinaLangVersion=.*/ballerinaLangVersion=${{ steps.build-dev-tools.outputs.langVersion }}/" gradle.properties
./gradlew clean build --stacktrace --scan -x test --console=plain --no-daemon --continue -x project-api-tests:test
- name: Create linux-deb
id: run_installers_deb
run: |
cd ballerina-distribution/installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-deb successfully"
- name: Create linux-rpm
id: run_installers_rpm
run: |
cd ballerina-distribution/installers/linux-rpm
./build-ballerina-linux-rpm-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-rpm successfully"
- name: Archive Ballerina ZIP
uses: actions/upload-artifact@v4
id: artifact-upload
with:
name: Ballerina ZIP
path: ballerina-distribution/ballerina/build/distributions/ballerina-*-swan-lake.zip
- name: Archive Ballerina Short Name ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name ZIP
path: ballerina-distribution/ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Linux deb
uses: actions/upload-artifact@v4
with:
name: Linux Installer deb
path: ballerina-distribution/installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Archive Linux rpm
uses: actions/upload-artifact@v4
with:
name: Linux Installer rpm
path: ballerina-distribution/installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
- name: Archive MacOS Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ZIP
path: ballerina-distribution/ballerina/build/distributions/ballerina-*-macos.zip
- name: Archive MacOS-ARM Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM Installer ZIP
path: ballerina-distribution/ballerina/build/distributions/ballerina-*-macos-arm.zip
- name: Archive Windows Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Windows Installer ZIP
path: ballerina-distribution/ballerina/build/distributions/ballerina-*-windows.zip
- name: Send release notification
shell: bash
run: |
body=$(cat << EOF
{
"cards": [
{
"header": {
"title": "Daily Build",
},
"sections": [
{
"widgets": [
{
"keyValue": {
"topLabel": "Ballerina Distribution",
"content": "v${{ steps.project-version.outputs.langversion }}",
"onClick": {
"openLink": {
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
},
"iconUrl": "https://lh6.googleusercontent.com/proxy/R9Rx8vYNd-_HZn58ckf5PNX7RMlC6P-B75fB7UQ_GFH5R0UwtfJ1gVNARBvH1us8LBuK4NVFsvMGnwZkm-H2_9ACwH_j0lQmExR1SRMNGlFcbrm_1O7foFpqqOiVzA",
"button": {
"textButton": {
"text": "Download ZIP",
"onClick": {
"openLink": {
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload.outputs.artifact-id }}"
}
}
}
}
}
}
]
}
]
}
]
}
EOF
)
curl \
-X POST \
-H 'Content-Type: application/json' \
"https://chat.googleapis.com/v1/spaces/AAAApvQDm3o/messages?key=${{ secrets.EDITOR_CHAT_BOT_KEY }}&token=${{ secrets.EDITOR_CHAT_BOT_TOKEN }}" \
-d "$body"
================================================
FILE: .github/workflows/daily-build.yml
================================================
name: Daily build
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # 07:30 in LK time (GMT+5:30)
jobs:
ubuntu-build:
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Get daily docker version
id: version
run: echo "::set-output name=version::$(date +'%Y-%m-%d')"
- name: Get project version
id: project-version
run: |
SHORT_VERSION=$((grep -w "version" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
DIST_VERSION=$((grep -w "version" | cut -d= -f2 | xargs) < gradle.properties)
LANG_VERSION=$((grep -w "ballerinaLangVersion" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION-$CODE_NAME
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
echo "::set-output name=langversion::$LANG_VERSION"
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x project-api-tests:test
- name: Create linux-deb
id: run_installers_deb
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-deb successfully"
- name: Create linux-rpm
id: run_installers_rpm
run: |
cd installers/linux-rpm
./build-ballerina-linux-rpm-x64.sh -v ${{ steps.project-version.outputs.version }} -p ./../../ballerina/build/distributions
echo "Created linux-rpm successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb.sha256 installers/linux-deb/target/ballerina-*-linux-x64.deb
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}-linux-x64.rpm.sha256 installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.version }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.version }}.zip
openssl dgst -sha256 -out ballerina-${{ steps.project-version.outputs.sversion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Ballerina ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina ZIP
path: ballerina/build/distributions/ballerina-*-swan-lake.zip
- name: Archive Ballerina Short Name ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name ZIP
path: ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
# Delete existing nightly release if it exists
- name: Delete existing nightly release
continue-on-error: true
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const release = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: 'nightly'
});
await github.rest.repos.deleteRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id
});
await github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/nightly'
});
# Create nightly release and upload artifacts
- name: Create nightly release
uses: softprops/action-gh-release@v1
with:
tag_name: nightly
name: Nightly Build - ${{ steps.version.outputs.version }}
body: |
Automated nightly build for ${{ steps.version.outputs.version }}
Ballerina Version: ${{ steps.project-version.outputs.version }}
prerelease: true
files: |
ballerina/build/distributions/ballerina-*-swan-lake.zip
installers/linux-deb/target/ballerina-*-linux-x64.deb
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Archive Linux deb
uses: actions/upload-artifact@v4
with:
name: Linux Installer deb
path: installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Archive Linux rpm
uses: actions/upload-artifact@v4
with:
name: Linux Installer rpm
path: installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
- name: Archive Linux Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Linux Installer ZIP
path: ballerina/build/distributions/ballerina-*-linux.zip
- name: Archive Linux-ARM Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Linux-ARM Installer ZIP
path: ballerina/build/distributions/ballerina-*-linux-arm.zip
- name: Archive MacOS Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ZIP
path: ballerina/build/distributions/ballerina-*-macos.zip
- name: Archive MacOS-ARM Installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM Installer ZIP
path: ballerina/build/distributions/ballerina-*-macos-arm.zip
- name: Archive Windows Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Windows Installer ZIP
path: ballerina/build/distributions/ballerina-*-windows.zip
- name: Archive Linux deb Hashes
uses: actions/upload-artifact@v4
with:
name: Linux deb Hashes
path: ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb.sha256
- name: Archive Linux rpm Hashes
uses: actions/upload-artifact@v4
with:
name: Linux rpm Hashes
path: ballerina-${{ steps.project-version.outputs.version }}-linux-x64.rpm.sha256
- name: Archive Ballerina Zip Hashes
uses: actions/upload-artifact@v4
with:
name: Ballerina Zip Hashes
path: ballerina-${{ steps.project-version.outputs.version }}.zip.sha256
- name: Archive Ballerina Short Name Hashes
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name Hashes
path: ballerina-${{ steps.project-version.outputs.sversion }}.zip.sha256
- name: Install Ballerina DEB
run: sudo dpkg -i installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ steps.project-version.outputs.langversion }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
- name: Checkout docker repo
uses: actions/checkout@v2
with:
repository: ballerina-platform/module-ballerina-docker
path: module-ballerina-docker
- name: Copy zip artifact
run: cp ballerina/build/distributions/ballerina-22*.zip module-ballerina-docker/base/docker/
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build the docker image
id: docker_build
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
load: true
push: false
tags: ballerina/ballerina:nightly-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina:nightly-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
push: true
tags: ballerina/ballerina:nightly
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Copy deb artifact
run: cp installers/linux-deb/target/ballerina-*-linux-x64.deb module-ballerina-docker/base/devcontainer/
- name: Build the dev container docker image
id: docker_build_devcontainer
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/devcontainer/
load: true
push: false
tags: ballerina/ballerina-devcontainer:nightly-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb
- name: Clean up created artifacts
run: |
rm -rf installers/linux-deb/target/ballerina-*-linux-x64.deb
rm -rf installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
rm -rf ballerina/build/distributions/ballerina-*-macos.zip
rm -rf ballerina/build/distributions/ballerina-*-macos-arm.zip
rm -rf ballerina/build/distributions/ballerina-*-windows.zip
- name: Run Trivy vulnerability scanner for dev container docker image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina-devcontainer:nightly-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
- name: Build and push dev container image
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/devcontainer/
push: true
tags: ballerina/ballerina-devcontainer:nightly
build-args: |
BALLERINA_DIST=ballerina-${{ steps.project-version.outputs.version }}-linux-x64.deb
- name: Notify failure
if: ${{ failure() }}
run: |
curl -X POST \
'https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches' \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"notify-build-failure\",
\"client_payload\": {
\"repoName\": \"ballerina-distribution\",
\"branch\": \"master\"
}
}"
outputs:
project-version: ${{ steps.project-version.outputs.version }}
lang-version: ${{ steps.project-version.outputs.langversion }}
project-api-tests:
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
prodCentralToken: ${{ secrets.BALLERINA_CENTRAL_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew :project-api-tests:test --stacktrace --scan --console=plain --no-daemon --continue
windows-build:
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build with Gradle
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: ./gradlew.bat clean build --stacktrace --scan --console=plain --no-daemon -x test
ubuntu-rpm-installer-test:
needs: ubuntu-build
runs-on: ubuntu-latest
container: centos:latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Setup Files
run: |
cd /etc/yum.repos.d/
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
- name: Download Ballerina rpm Installer
uses: actions/download-artifact@v4
with:
name: Linux Installer rpm
- name: Install Ballerina RPM
run: |
rpm -ivh ballerina-*-linux-x64.rpm
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
sed -i -e "s/swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
sed -i -e "s/swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
ubuntu-arm-installer-build:
needs: ubuntu-build
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Download Linux-ARM Installer Zip
uses: actions/download-artifact@v4
with:
name: Linux-ARM Installer ZIP
- name: Create linux-arm deb
working-directory: installers/linux-deb
run: |
./build-ballerina-linux-deb-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../ -a arm
echo "Created linux-arm-deb successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-linux-arm-x64.deb.sha256 installers/linux-deb/target/ballerina-*-linux-arm-x64.deb
- name: Archive Linux deb
uses: actions/upload-artifact@v4
with:
name: Linux-ARM Installer deb
path: installers/linux-deb/target/ballerina-*-linux-arm-x64.deb
- name: Archive Linux deb Hashes
uses: actions/upload-artifact@v4
with:
name: Linux-ARM deb Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-linux-arm-x64.deb.sha256
macos-installer-build:
needs: ubuntu-build
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Download MacOS Installer Zip
uses: actions/download-artifact@v4
with:
name: MacOS Installer ZIP
- name: Create macos-pkg
id: run_installers_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../
echo "Created macos-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-x64.pkg.sha256 installers/mac/target/pkg/ballerina-*-macos-x64.pkg
- name: Archive MacOS pkg Hashes
uses: actions/upload-artifact@v4
with:
name: MacOS pkg Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-x64.pkg.sha256
- name: Archive MacOS pkg
uses: actions/upload-artifact@v4
with:
name: MacOS Installer pkg
path: installers/mac/target/pkg/ballerina-*-macos-x64.pkg
- name: Install Ballerina PKG
run: sudo installer -pkg installers/mac/target/pkg/ballerina-*-macos-x64.pkg -target /
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
macos-arm-installer-build:
needs: ubuntu-build
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Download MacOS-ARM Intaller Zip
uses: actions/download-artifact@v4
with:
name: MacOS-ARM Installer ZIP
- name: Create macos-arm-pkg
id: run_installers_arm_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.ubuntu-build.outputs.project-version }} -p ./../../ -a arm
echo "Created macos-arm-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-arm-x64.pkg.sha256 installers/mac/target/pkg/ballerina-*-macos-arm-x64.pkg
- name: Archive MacOS-ARM pkg Hashes
uses: actions/upload-artifact@v4
with:
name: MacOS-ARM pkg Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-macos-arm-x64.pkg.sha256
- name: Archive MacOS-ARM pkg
uses: actions/upload-artifact@v4
with:
name: MacOS Installer ARM pkg
path: installers/mac/target/pkg/ballerina-*-macos-arm-x64.pkg
windows-installer-build:
needs: ubuntu-build
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '2.1.x'
- name: Install GUID Generator
run: dotnet tool install -g dotnet-guid --version 0.5.2
- name: Set up Wix toolkit
run: echo "${WIX}bin" >> $GITHUB_PATH
shell: bash
- name: Download Windows Installer Zip
uses: actions/download-artifact@v4
with:
name: Windows Installer ZIP
- name: Create windows-msi
id: run_installers_msi
run: |
move installers\windows .\..\..\
cd ..\..\windows
.\build-ballerina-windows-x64.bat --version ${{ needs.ubuntu-build.outputs.project-version }} --path .\..\ballerina-distribution\ballerina-distribution
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi.sha256 D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Archive Windows msi Hashes
uses: actions/upload-artifact@v4
with:
name: Windows msi Hashes
path: ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi.sha256
- name: Archive Windows msi
uses: actions/upload-artifact@v4
with:
name: Windows Installer msi
path: D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Install Ballerina msi
run: msiexec /i D:\a\windows\target\msi\ballerina-${{ needs.ubuntu-build.outputs.project-version }}-windows-x64.msi /quiet /qr
shell: cmd
- name: Update Installer Test Configs
run: |
set DISPLAY_TEXT=${{ needs.ubuntu-build.outputs.lang-version }}
set SWAN_LAKE_LATEST_VERSION=swan-lake-%DISPLAY_TEXT%
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=%DISPLAY_TEXT%/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=%SWAN_LAKE_LATEST_VERSION%/" ballerina-test-automation/gradle.properties
shell: cmd
- name: Run Installer Tests
working-directory: .\ballerina-test-automation\installer-test
run: |
$env:Path += ";C:\Program Files\Ballerina\bin"
.\..\gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
env:
TEST_MODE_ACTIVE: true
trigger-notifications:
needs: [ubuntu-build, macos-installer-build, windows-installer-build]
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Invoke Connector Ballerina Version Bump
run: |
ballerinaVersion=$((grep -w 'ballerinaLangVersion' | cut -d= -f2) < gradle.properties)
echo "Triggering connectors dependency bumps..." && \
curl -X POST \
https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: token ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"connector-update\",
\"client_payload\": {
\"ballerinaVersion\": \"$ballerinaVersion\"
}
}"
================================================
FILE: .github/workflows/fossa_scan.yml
================================================
name: Fossa Scan
on:
workflow_dispatch:
schedule:
- cron: '30 18 * * *' # 00:00 in LK time (GMT+5:30)
jobs:
fossa-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: fossas/fossa-action@main
env:
packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }}
packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
api-key: ${{secrets.FOSSA_APIKEY}}
================================================
FILE: .github/workflows/language_server_simulator_fhir.yml
================================================
name: Language Server Simulator on FHIR
on:
schedule:
- cron: '0 */12 * * *'
workflow_dispatch:
jobs:
run_simulator:
name: Run LS Simulator
runs-on: ubuntu-latest
timeout-minutes: 240
strategy:
fail-fast: false
matrix:
branch: [ "master" ]
skipGenerators: [ "", "IMPORT_STATEMENT" ]
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: ${{ matrix.branch }}
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Initialize sub-modules
run: git submodule update --init
- name: Build with Gradle
timeout-minutes: 180
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
run: |
export DISPLAY=':99.0'
/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
./gradlew clean :language-server-simulator:runLSSimulatorOnFHIR -Dls.simulation.skipGenerators=${{ matrix.skipGenerators }}
- name: Check Simulation Failure
run: if test -f dump.hprof; then exit 1; else exit 0; fi
- name: Analyze Heap Dump If Exists
if: failure()
run: |
if test -f dump.hprof; then echo "Heap sump exists. Analyzing..."; else exit 0; fi
wget https://ftp.jaist.ac.jp/pub/eclipse/mat/1.12.0/rcp/MemoryAnalyzer-1.12.0.20210602-linux.gtk.x86_64.zip
unzip MemoryAnalyzer-1.12.0.20210602-linux.gtk.x86_64.zip
./mat/ParseHeapDump.sh ./dump.hprof org.eclipse.mat.api:suspects
- name: Upload Heap Dumps
uses: actions/upload-artifact@v4
if: always()
with:
name: heap_dump-${{ matrix.branch }}.hprof
path: '*.hprof'
- name: Upload Leaks Suspects
uses: actions/upload-artifact@v4
if: failure()
with:
name: Leak_Suspects-${{ matrix.branch }}
path: 'dump_Leak_Suspects.zip'
- name: Notify failure
if: failure()
run: |
curl -X POST \
'https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches' \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"notify-simulator-failure\",
\"client_payload\": {
\"branch\": \"${{ matrix.branch }}\",
\"runId\":\"${{ github.run_id }}\"
}
}"
================================================
FILE: .github/workflows/language_server_simulator_nballerina.yml
================================================
name: Language Server Simulator on nBallerina
on:
schedule:
- cron: '0 */12 * * *'
workflow_dispatch:
jobs:
run_simulator:
name: Run LS Simulator
runs-on: ubuntu-latest
timeout-minutes: 240
strategy:
fail-fast: false
matrix:
branch: [ "master" ]
skipGenerators: [ "", "IMPORT_STATEMENT" ]
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
ref: ${{ matrix.branch }}
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Initialize sub-modules
run: git submodule update --init
- name: Build with Gradle
timeout-minutes: 180
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
run: |
export DISPLAY=':99.0'
/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
./gradlew clean :language-server-simulator:runLSSimulatorOnnBallerina -Dls.simulation.skipGenerators=${{ matrix.skipGenerators }}
- name: Check Simulation Failure
run: if test -f dump.hprof; then exit 1; else exit 0; fi
- name: Analyze Heap Dump If Exists
if: failure()
run: |
if test -f dump.hprof; then echo "Heap sump exists. Analyzing..."; else exit 0; fi
wget https://ftp.jaist.ac.jp/pub/eclipse/mat/1.12.0/rcp/MemoryAnalyzer-1.12.0.20210602-linux.gtk.x86_64.zip
unzip MemoryAnalyzer-1.12.0.20210602-linux.gtk.x86_64.zip
./mat/ParseHeapDump.sh ./dump.hprof org.eclipse.mat.api:suspects
- name: Upload Heap Dumps
uses: actions/upload-artifact@v4
if: always()
with:
name: heap_dump-${{ matrix.branch }}.hprof
path: '*.hprof'
- name: Upload Leaks Suspects
uses: actions/upload-artifact@v4
if: failure()
with:
name: Leak_Suspects-${{ matrix.branch }}
path: 'dump_Leak_Suspects.zip'
- name: Notify failure
if: failure()
run: |
curl -X POST \
'https://api.github.com/repos/ballerina-platform/ballerina-release/dispatches' \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--data "{
\"event_type\": \"notify-simulator-failure\",
\"client_payload\": {
\"branch\": \"${{ matrix.branch }}\",
\"runId\":\"${{ github.run_id }}\"
}
}"
================================================
FILE: .github/workflows/main.yml
================================================
name: Build
on:
push:
branches:
- master
- stage
- ballerina-1.1.x
workflow_dispatch:
inputs:
skipTests:
description: 'Skip Tests during build'
required: false
default: 'false'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Get project version
id: project-version
run: |
SHORT_VERSION=$((grep -w "version" | cut -d= -f2 | cut -d- -f1) < gradle.properties)
DIST_VERSION=$((grep -w "version" | cut -d= -f2) < gradle.properties)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION-$CODE_NAME
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
- name: Build Ballerina Distribution
if: ${{ github.event.inputs.skipTests == '' || github.event.inputs.skipTests == 'false' }}
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x project-api-tests:test
- name: Build Ballerina Distribution Skip Tests
if: ${{ github.event.inputs.skipTests == 'true' }}
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x test
- name: Archive Ballerina ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina ZIP
path: ballerina/build/distributions/ballerina-*-swan-lake.zip
- name: Archive Ballerina Short Name ZIP
uses: actions/upload-artifact@v4
with:
name: Ballerina Short Name ZIP
path: ballerina/build/distributions/ballerina-${{ steps.project-version.outputs.sversion }}.zip
- name: Archive Linux installer ZIP
uses: actions/upload-artifact@v4
with:
name: Linux installer ZIP
path: ballerina/build/distributions/ballerina-*-linux.zip
- name: Archive MacOS installer ZIP
uses: actions/upload-artifact@v4
with:
name: MacOS installer ZIP
path: ballerina/build/distributions/ballerina-*-macos.zip
- name: Archive Windows Installer ZIP
uses: actions/upload-artifact@v4
with:
name: Windows Installer ZIP
path: ballerina/build/distributions/ballerina-*-windows.zip
================================================
FILE: .github/workflows/publish-release-artifacts-1.2.x.yml
================================================
name: Publish Release Artifacts (1.2.x)
on:
workflow_dispatch:
inputs:
release_version:
description: 'Release Version e.g., 1.2.31, 1.2.31-rc1'
default: '1.2.31'
required: true
jobs:
publish-artifacts:
name: Publish Release Artifacts
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 8
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '8'
- name: Set version env variable
id: set-version
run: |
RELEASE_VERSION=${{ github.event.inputs.release_version }}
VERSION=$(cut -d- -f1 <<< $RELEASE_VERSION)
TAGGED_VERSION=$RELEASE_VERSION
DISPLAY_VERSION=$VERSION
echo VERSION=$VERSION >> $GITHUB_ENV
echo DISPLAY_VERSION=$DISPLAY_VERSION >> $GITHUB_ENV
echo GIT_TAG=$TAGGED_VERSION >> $GITHUB_ENV
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=taggedVersion::$TAGGED_VERSION"
- name: Download artifacts
run: |
baseUrl="https://github.com/ballerina-platform/ballerina-distribution/releases/download/v$GIT_TAG"
rm -rf $VERSION
mkdir $VERSION
cd $VERSION
wget "$baseUrl/ballerina-$VERSION.zip"
wget "$baseUrl/jballerina-$VERSION.zip"
wget "$baseUrl/ballerina-windows-installer-x64-$VERSION.msi"
wget "$baseUrl/ballerina-linux-installer-x64-$VERSION.deb"
wget "$baseUrl/ballerina-linux-installer-x64-$VERSION.rpm"
wget "$baseUrl/ballerina-macos-installer-x64-$VERSION.pkg"
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }}
packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
- name: Generate metadata json
working-directory: ${{ steps.set-version.outputs.version }}
run: |
date=`date +%Y-%m-%d`
winInstSize=`ls -lh ballerina-windows-installer-x64-$VERSION.msi | cut -d " " -f5 | sed 's/M/mb/g'`
linuxInstSize=`ls -lh ballerina-linux-installer-x64-$VERSION.deb | cut -d " " -f5 | sed 's/M/mb/g'`
rpmInstSize=`ls -lh ballerina-linux-installer-x64-$VERSION.rpm | cut -d " " -f5 | sed 's/M/mb/g'`
macInstSize=`ls -lh ballerina-macos-installer-x64-$VERSION.pkg | cut -d " " -f5 | sed 's/M/mb/g'`
fileName=metadata.json
echo { > $fileName
echo " \"version\":\"$VERSION\"," >> $fileName
echo " \"release-date\":\"$date\"," >> $fileName
echo " \"windows-installer\":\"ballerina-windows-installer-x64-$VERSION.msi\"," >> $fileName
echo " \"windows-installer-size\":\"$winInstSize\"," >> $fileName
echo " \"linux-installer\":\"ballerina-linux-installer-x64-$VERSION.deb\"," >> $fileName
echo " \"linux-installer-size\":\"$linuxInstSize\"," >> $fileName
echo " \"macos-installer\":\"ballerina-macos-installer-x64-$VERSION.pkg\"," >> $fileName
echo " \"macos-installer-size\":\"$macInstSize\"," >> $fileName
echo " \"rpm-installer\":\"ballerina-linux-installer-x64-$VERSION.rpm\"," >> $fileName
echo " \"rpm-installer-size\":\"$rpmInstSize\"," >> $fileName
echo " \"other-artefacts\":[ " >> $fileName
echo " \"ballerina-$VERSION.zip\"," >> $fileName
echo " \"ballerina-$VERSION.vsix\"" >> $fileName
echo " ]", >> $fileName
echo " \"api-docs\":\"ballerina-api-docs-$VERSION.zip\"," >> $fileName
echo " \"release-notes\":\"ballerina-release-notes-$VERSION.md\"" >> $fileName
echo } >> $fileName
- name: Archive metadata json
uses: actions/upload-artifact@v4
with:
name: Metadata JSON
path: ${{ steps.set-version.outputs.version }}/metadata.json
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Process docker
id: process-docker
run: |
git clone --single-branch --branch ballerina-1.2.x https://github.com/ballerina-platform/module-ballerina-docker
DOCKER_REPO='module-ballerina-docker'
echo "::set-output name=dockerRepo::$DOCKER_REPO"
- name: Enable experimental functions
run: |
echo $'{\n "experimental": true\n}' | sudo tee /etc/docker/daemon.json
sudo service docker restart
docker version
- name: Build and push docker image
run: |
DOCKER_REPO=${{ steps.process-docker.outputs.dockerRepo }}
cp $VERSION/ballerina-$VERSION.zip $DOCKER_REPO/base/docker/
docker build --no-cache=true --squash --build-arg BALLERINA_DIST=ballerina-$VERSION.zip -t ballerina/ballerina:$VERSION $DOCKER_REPO/base/docker/
rm $DOCKER_REPO/base/docker/ballerina-$VERSION.zip
docker push ballerina/ballerina:$VERSION
docker rmi ballerina/ballerina:$VERSION
docker image prune -f
- name: Publish Artifacts
run: |
sudo apt-get install python3-setuptools
python3 -m pip install --user awscli
aws configure set aws_access_key_id $s3_acc_id
aws configure set aws_secret_access_key $s3_acc_key
aws s3 cp $VERSION s3://dist-dev.ballerina.io/downloads/$VERSION --recursive
env:
s3_acc_id: ${{ secrets.S3_ID }}
s3_acc_key: ${{ secrets.S3_KEY }}
================================================
FILE: .github/workflows/publish-release-artifacts.yml
================================================
name: Publish Release Artifacts
on:
workflow_dispatch:
inputs:
release_version:
description: 'Release Version e.g., 2201.1.1, 2201.1.1-rc1'
default: '2201.1.1'
required: true
jobs:
publish-artifacts:
name: Publish Release Artifacts
runs-on: ubuntu-latest
if: github.repository_owner == 'ballerina-platform'
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Set version env variable
id: set-version
env:
RELEASE_VERSION: ${{ github.event.inputs.release_version }}
run: |
if [[ "$RELEASE_VERSION" == *-SNAPSHOT ]]; then
VERSION="${RELEASE_VERSION%-SNAPSHOT}"
else
VERSION="$RELEASE_VERSION"
fi
DIST_VERSION="${VERSION%%-*}"
CODE_NAME="swan-lake"
TAGGED_VERSION=$RELEASE_VERSION
LONG_VERSION=$DIST_VERSION-$CODE_NAME
UPDATE_NUMBER=$(cut -d'.' -f2 <<< $DIST_VERSION)
DISPLAY_VERSION="$VERSION (Swan Lake Update $UPDATE_NUMBER)"
echo VERSION=$VERSION >> $GITHUB_ENV
echo DIST_VERSION=$DIST_VERSION >> $GITHUB_ENV
echo LONG_VERSION=$LONG_VERSION >> $GITHUB_ENV
echo DISPLAY_VERSION=$DISPLAY_VERSION >> $GITHUB_ENV
echo GIT_TAG=$TAGGED_VERSION >> $GITHUB_ENV
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "distVersion=$DIST_VERSION" >> "$GITHUB_OUTPUT"
echo "taggedVersion=$TAGGED_VERSION" >> "$GITHUB_OUTPUT"
echo "::set-output name=updateNumber::$UPDATE_NUMBER"
echo "::set-output name=longVersion::$LONG_VERSION"
- name: Download artifacts
run: |
baseUrl="https://github.com/ballerina-platform/ballerina-distribution/releases/download/v$GIT_TAG"
updateNumber=${{ steps.set-version.outputs.updateNumber }}
distVersion=${{ steps.set-version.outputs.distVersion }}
rm -rf $VERSION
mkdir $VERSION
cd $VERSION
wget "$baseUrl/ballerina-$LONG_VERSION.zip"
wget "$baseUrl/ballerina-$distVersion.zip"
wget "$baseUrl/ballerina-$LONG_VERSION-windows-x64.msi"
wget "$baseUrl/ballerina-$LONG_VERSION-linux-x64.deb"
wget "$baseUrl/ballerina-$LONG_VERSION-linux-x64.rpm"
wget "$baseUrl/ballerina-$LONG_VERSION-macos-x64.pkg"
if [ "$updateNumber" -ge 5 ]; then
wget "$baseUrl/ballerina-$LONG_VERSION-macos-arm-x64.pkg"
fi
if [ "$updateNumber" -ge 11 ]; then
wget "$baseUrl/ballerina-$LONG_VERSION-linux-arm-x64.deb"
fi
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }}
packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
- name: Generate metadata json
working-directory: ${{ steps.set-version.outputs.version }}
run: |
updateNumber=${{ steps.set-version.outputs.updateNumber }}
date=`date +%Y-%m-%d`
winInstSize=`ls -lh ballerina-$LONG_VERSION-windows-x64.msi | cut -d " " -f5 | sed 's/M/mb/g'`
linuxInstSize=`ls -lh ballerina-$LONG_VERSION-linux-x64.deb | cut -d " " -f5 | sed 's/M/mb/g'`
rpmInstSize=`ls -lh ballerina-$LONG_VERSION-linux-x64.rpm | cut -d " " -f5 | sed 's/M/mb/g'`
macInstSize=`ls -lh ballerina-$LONG_VERSION-macos-x64.pkg | cut -d " " -f5 | sed 's/M/mb/g'`
fileName=metadata.json
echo "{" >> $fileName
echo " \"version\":\"$VERSION\"," >> $fileName
echo " \"short-version\":\"$VERSION\"," >> $fileName
echo " \"display-version\":\"$DISPLAY_VERSION\"," >> $fileName
echo " \"release-date\":\"$date\"," >> $fileName
echo " \"windows-installer\":\"ballerina-$LONG_VERSION-windows-x64.msi\"," >> $fileName
echo " \"windows-installer-size\":\"$winInstSize\"," >> $fileName
echo " \"linux-installer\":\"ballerina-$LONG_VERSION-linux-x64.deb\"," >> $fileName
echo " \"linux-installer-size\":\"$linuxInstSize\"," >> $fileName
if [ "$updateNumber" -ge 11 ]; then
linuxArmInstSize=`ls -lh ballerina-$LONG_VERSION-linux-arm-x64.deb | cut -d " " -f5 | sed 's/M/mb/g'`
echo " \"linux-arm-installer\":\"ballerina-$LONG_VERSION-linux-arm-x64.deb\"," >> $fileName
echo " \"linux-arm-installer-size\":\"$linuxArmInstSize\"," >> $fileName
fi
echo " \"macos-installer\":\"ballerina-$LONG_VERSION-macos-x64.pkg\"," >> $fileName
echo " \"macos-installer-size\":\"$macInstSize\"," >> $fileName
if [ "$updateNumber" -ge 5 ]; then
macArmInstSize=`ls -lh ballerina-$LONG_VERSION-macos-arm-x64.pkg | cut -d " " -f5 | sed 's/M/mb/g'`
echo " \"macos-arm-installer\":\"ballerina-$LONG_VERSION-macos-arm-x64.pkg\"," >> $fileName
echo " \"macos-arm-installer-size\":\"$macArmInstSize\"," >> $fileName
fi
echo " \"rpm-installer\":\"ballerina-$LONG_VERSION-linux-x64.rpm\"," >> $fileName
echo " \"rpm-installer-size\":\"$rpmInstSize\"," >> $fileName
echo " \"other-artefacts\":[ " >> $fileName
echo " \"ballerina-$LONG_VERSION.zip\"" >> $fileName
echo " ]", >> $fileName
echo " \"api-docs\":\"ballerina-api-docs-$VERSION.zip\"," >> $fileName
echo " \"release-notes\":\"ballerina-release-notes-$VERSION.md\"" >> $fileName
echo "}" >> $fileName
- name: Archive metadata json
uses: actions/upload-artifact@v4
with:
name: Metadata JSON
path: ${{ steps.set-version.outputs.version }}/metadata.json
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Clone Docker Repo
id: process-docker
run: |
git clone --single-branch --branch master https://github.com/ballerina-platform/module-ballerina-docker
DOCKER_REPO='module-ballerina-docker'
echo "::set-output name=dockerRepo::$DOCKER_REPO"
- name: Copy artifacts to Docker repo
run: |
DOCKER_REPO=${{ steps.process-docker.outputs.dockerRepo }}
cp $VERSION/ballerina-${{ steps.set-version.outputs.distVersion }}.zip $DOCKER_REPO/base/docker/
cp $VERSION/ballerina-$LONG_VERSION-linux-x64.deb $DOCKER_REPO/base/devcontainer/
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push the Ballerina Docker image
id: docker_build
uses: docker/build-push-action@v6
with:
context: ${{ steps.process-docker.outputs.dockerRepo }}/base/docker/
push: true
tags: ballerina/ballerina:${{ steps.set-version.outputs.taggedVersion }}
platforms: linux/amd64,linux/arm64
build-args: |
BALLERINA_DIST=ballerina-${{ steps.set-version.outputs.distVersion }}.zip
- name: Build and push dev container image
id: docker_build_devcontainer
uses: docker/build-push-action@v6
with:
context: ${{ steps.process-docker.outputs.dockerRepo }}/base/devcontainer/
push: true
tags: ballerina/ballerina-devcontainer:${{ steps.set-version.outputs.taggedVersion }}
build-args: |
BALLERINA_DIST=ballerina-${{ steps.set-version.outputs.longVersion }}-linux-x64.deb
- name: Publish Artifacts
run: |
sudo apt-get install python3-setuptools
python3 -m pip install --user awscli
aws configure set aws_access_key_id $s3_acc_id
aws configure set aws_secret_access_key $s3_acc_key
aws s3 cp $VERSION s3://dist-dev.ballerina.io/downloads/$VERSION --recursive
env:
s3_acc_id: ${{ secrets.S3_ID }}
s3_acc_key: ${{ secrets.S3_KEY }}
================================================
FILE: .github/workflows/publish-release.yml
================================================
name: Publish release
on:
workflow_dispatch:
inputs:
isPreRelease:
description: "Tag created is a pre-release tag"
required: true
default: "false"
preReleaseSuffix:
description: "The text that will be suffixed to the Git tag. e.g., rc1"
required: false
default: ""
permissions:
id-token: write
contents: write
jobs:
publish-release:
name: Publish Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: "21.0.3"
- name: Set version env variable
id: version-set
run: |
SHORT_VERSION=$(grep -w "version" gradle.properties | cut -d= -f2 | sed 's/-SNAPSHOT//' | xargs)
DIST_VERSION=$(grep -w "version" gradle.properties | cut -d= -f2 | sed 's/-SNAPSHOT//' | xargs)
LANG_VERSION=$(grep -w "ballerinaLangVersion" gradle.properties | cut -d= -f2 | sed 's/-SNAPSHOT//' | xargs)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION
TAGGED_VERSION=$RELEASE_VERSION
LONG_VERSION=$DIST_VERSION-$CODE_NAME
if [ -n "${{ github.event.inputs.preReleaseSuffix }}" ]; then
TAGGED_VERSION=$RELEASE_VERSION-${{ github.event.inputs.preReleaseSuffix }}
fi
echo VERSION=$RELEASE_VERSION >> $GITHUB_ENV
echo GIT_TAG=$TAGGED_VERSION >> $GITHUB_ENV
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
echo "::set-output name=taggedVersion::$TAGGED_VERSION"
echo "::set-output name=longVersion::$LONG_VERSION"
echo "::set-output name=langVersion::$LANG_VERSION"
- name: Pre release depenency version update
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
run: |
echo "Version: ${VERSION}"
echo "Tagged Version: ${GIT_TAG}"
git config user.name ${{ secrets.BALLERINA_BOT_USERNAME }}
git config user.email ${{ secrets.BALLERINA_BOT_EMAIL }}
git checkout -b release-${GIT_TAG}
- name: Generate UUID
run: |
UUID=$(uuidgen)
perl -pi -e "s/^\s*installerVersion=.*/installerVersion=$UUID/" gradle.properties
git config user.name ${{ secrets.BALLERINA_BOT_USERNAME }}
git config user.email ${{ secrets.BALLERINA_BOT_EMAIL }}
git add gradle.properties
git commit -m "Update UUID for installer"
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Publish artifact
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }}
packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
prodCentralToken: ${{ secrets.BALLERINA_CENTRAL_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
run: |
./gradlew build -x project-api-tests:test -Pversion=${VERSION}
./gradlew release -x project-api-tests:test -Prelease.useAutomaticVersion=true
- name: Checkout docker repo
uses: actions/checkout@v3
with:
repository: ballerina-platform/module-ballerina-docker
path: module-ballerina-docker
- name: Copy zip artifact
run: cp ballerina/build/distributions/ballerina-22*.zip module-ballerina-docker/base/docker/
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build the docker image
id: docker_build
uses: docker/build-push-action@v6
with:
context: module-ballerina-docker/base/docker/
load: true
push: false
tags: ballerina/ballerina:release-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.version-set.outputs.sversion }}.zip
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: "ballerina/ballerina:release-test"
skip-dirs: "ballerina/runtime/examples"
format: "table"
exit-code: "1"
timeout: "10m0s"
- name: cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: "14"
- name: Install GitHub CLI
run: |
npm install -g github-cli
gh --version
- name: Get Markdown file
id: file-url
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh repo view ballerina-platform/ballerina-dev-website --json url --jq '.clone_url'
gh api repos/ballerina-platform/ballerina-dev-website/contents/downloads/verify-ballerina-artifacts.md -H 'Accept: application/vnd.github.v3.raw' > release_notes.md
sed -i '1,10d' release_notes.md
- name: Retrieve Branch
id: retrieve-branch
run: |
branchName=$(echo ${{ github.ref }} | cut -d'/' -f3)
echo "::set-output name=branchName::$branchName"
- name: Update Markdown file
run: |
if ${{ github.event.inputs.isPreRelease }} == 'true'; then
echo "" > release_notes.md;
else sed -i 's/{{ version }}/${{ steps.version-set.outputs.taggedVersion }}/g' release_notes.md; sed -i 's/{{ branch }}/${{ steps.retrieve-branch.outputs.branchName }}/g' release_notes.md; fi
- name: Read release notes from file
id: release_notes
uses: actions/github-script@v4
with:
github-token: ${{ secrets.BALLERINA_BOT_TOKEN }}
script: |
const fs = require('fs');
const releaseNotes = fs.readFileSync('release_notes.md', 'utf8');
core.setOutput('notes', releaseNotes);
- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
tag_name: "v${{ steps.version-set.outputs.taggedVersion }}"
release_name: ${{ steps.version-set.outputs.taggedVersion }}
body: ${{ steps.release_notes.outputs.notes }}
draft: false
prerelease: ${{ github.event.inputs.isPreRelease }}
- name: Create linux-deb Installer
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.version-set.outputs.longVersion }} -p ./../../ballerina/build/distributions
echo "Created linux-deb successfully"
- name: Sign the linux-deb installer
run: |
cosign sign-blob installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sig --yes
- name: Verify the linux-deb installer
run: |
cosign verify-blob installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Create linux-rpm Installer
id: run_installers_rpm
run: |
cd installers/linux-rpm
./build-ballerina-linux-rpm-x64.sh -v ${{ steps.version-set.outputs.longVersion }} -p ./../../ballerina/build/distributions
echo "Created linux-rpm successfully"
- name: Sign the linux-rpm installer
run: |
cosign sign-blob installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sig --yes
- name: Verify the linux-rpm installer
run: |
cosign verify-blob installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sha256 installers/linux-deb/target/ballerina-*-linux-x64.deb
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sha256 installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.longVersion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}.zip
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.sversion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.sversion }}.zip
- name: Sign the zip artifacts
run: |
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}.zip --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}.sig --yes
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.sversion }}.zip --output-certificate ballerina-${{ steps.version-set.outputs.sversion }}.pem --output-signature ballerina-${{ steps.version-set.outputs.sversion }}.sig --yes
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux.zip --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux.sig --yes
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.zip --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.sig --yes
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos.zip --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-macos.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-macos.sig --yes
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.zip --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.sig --yes
cosign sign-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-windows.zip --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-windows.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-windows.sig --yes
- name: Verify the zip artifacts
run: |
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}.zip --certificate ballerina-${{ steps.version-set.outputs.longVersion }}.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.sversion }}.zip --certificate ballerina-${{ steps.version-set.outputs.sversion }}.pem --signature ballerina-${{ steps.version-set.outputs.sversion }}.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux.zip --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.zip --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos.zip --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-macos.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-macos.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.zip --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
cosign verify-blob ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-windows.zip --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-windows.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-windows.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Upload zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}.zip
asset_content_type: application/octet-stream
- name: Upload zip artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}.pem
asset_content_type: application/octet-stream
- name: Upload zip artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}.sig
asset_content_type: application/octet-stream
- name: Upload zip without tool artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.sversion }}.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.sversion }}.zip
asset_content_type: application/octet-stream
- name: Upload zip without tool artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.sversion }}.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.sversion }}.pem
asset_content_type: application/octet-stream
- name: Upload zip without tool artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.sversion }}.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.sversion }}.sig
asset_content_type: application/octet-stream
- name: Upload Linux deb Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb
asset_path: installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb
asset_content_type: application/octet-stream
- name: Upload Linux deb Installer's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.pem
asset_content_type: application/octet-stream
- name: Upload Linux deb Installer's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sig
asset_content_type: application/octet-stream
- name: Upload Linux rpm Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm
asset_path: installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm
asset_content_type: application/octet-stream
- name: Upload Linux rpm Installer's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.pem
asset_content_type: application/octet-stream
- name: Upload Linux rpm Installer's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sig
asset_content_type: application/octet-stream
- name: Upload Linux zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux.zip
asset_content_type: application/octet-stream
- name: Upload Linux zip artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux.pem
asset_content_type: application/octet-stream
- name: Upload Linux zip artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux.sig
asset_content_type: application/octet-stream
- name: Upload Linux-ARM zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.zip
asset_content_type: application/octet-stream
- name: Upload Linux-ARM zip artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.pem
asset_content_type: application/octet-stream
- name: Upload Linux-ARM zip artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.sig
asset_content_type: application/octet-stream
- name: Upload MacOS zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos.zip
asset_content_type: application/octet-stream
- name: Upload MacOS zip artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-macos.pem
asset_content_type: application/octet-stream
- name: Upload MacOS zip artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-macos.sig
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.zip
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM zip artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.pem
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM zip artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.sig
asset_content_type: application/octet-stream
- name: Upload Windows zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-windows.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-windows.zip
asset_content_type: application/octet-stream
- name: Upload Windows zip artifact's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-windows.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-windows.pem
asset_content_type: application/octet-stream
- name: Upload Windows zip artifact's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-windows.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-windows.sig
asset_content_type: application/octet-stream
- name: Upload Linux deb Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sha256
asset_path: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sha256
asset_content_type: application/octet-stream
- name: Upload Linux rpm Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sha256
asset_path: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sha256
asset_content_type: application/octet-stream
- name: Upload Ballerina zip Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}.zip.sha256
asset_path: ballerina-${{ steps.version-set.outputs.longVersion }}.zip.sha256
asset_content_type: application/octet-stream
- name: Upload ballerina Short Name zip Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.sversion }}.zip.sha256
asset_path: ballerina-${{ steps.version-set.outputs.sversion }}.zip.sha256
asset_content_type: application/octet-stream
- name: Install Ballerina DEB
run: sudo dpkg -i installers/linux-deb/target/ballerina-*-linux-x64.deb
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ steps.version-set.outputs.langVersion }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
# - name: Run Installer Tests
# working-directory: ./ballerina-test-automation/installer-test
# run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
# env:
# TEST_MODE_ACTIVE: true
- name: Create linux-arm-deb Installer
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.version-set.outputs.longVersion }} -p ./../../ballerina/build/distributions -a arm
echo "Created linux-arm-deb successfully"
- name: Sign the linux-arm-deb installer
run: |
cosign sign-blob installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb --output-certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.pem --output-signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.sig --yes
- name: Verify the linux-arm-deb installer
run: |
cosign verify-blob installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb --certificate ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.pem --signature ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Upload Linux-ARM deb Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb
asset_path: installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb
asset_content_type: application/octet-stream
- name: Upload Linux-ARM deb Installer's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.pem
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.pem
asset_content_type: application/octet-stream
- name: Upload Linux-ARM deb Installer's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.sig
asset_path: ./ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb.sig
asset_content_type: application/octet-stream
- name: Post release PR
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
run: |
curl -fsSL https://github.com/github/hub/raw/master/script/get | bash -s 2.14.1
bin/hub pull-request -m "[Automated] Sync master after "$VERSION" release"
outputs:
project-version: ${{ steps.version-set.outputs.longVersion }}
upload-asset-url: ${{ steps.create_release.outputs.upload_url }}
release-version: ${{ steps.version-set.outputs.taggedVersion }}
lang-version: ${{ steps.version-set.outputs.langVersion }}
macos-installer-build:
name: MacOS Installer Build
needs: publish-release
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: "temurin"
java-version: "21.0.3"
- name: Download MacOS Intaller Zip
run: |
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ needs.publish-release.outputs.release-version }}/ballerina-${{ needs.publish-release.outputs.project-version }}-macos.zip
- name: cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Create macos-pkg Installer
id: run_installers_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.publish-release.outputs.project-version }} -p ./../../
echo "Created macos-pkg successfully"
- name: Sign the MacOS installer
run: |
cosign sign-blob installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg --output-certificate ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.pem --output-signature ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sig --yes
- name: Verify the MacOS installer
run: |
cosign verify-blob installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg --certificate ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.pem --signature ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sha256 installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg
- name: Upload MacOS pkg Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sha256
asset_path: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sha256
asset_content_type: application/octet-stream
- name: Upload MacOS pkg Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg
asset_path: installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg
asset_content_type: application/octet-stream
- name: Upload MacOS installer's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.pem
asset_path: ./ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.pem
asset_content_type: application/octet-stream
- name: Upload MacOS installer's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sig
asset_path: ./ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sig
asset_content_type: application/octet-stream
- name: Install Ballerina PKG
run: sudo installer -pkg installers/mac/target/pkg/ballerina-*-macos-x64.pkg -target /
- name: Update Installer Test Configs
run: |
DISPLAY_TEXT=${{ needs.publish-release.outputs.lang-version }}
SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
# - name: Run Installer Tests
# working-directory: ./ballerina-test-automation/installer-test
# run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
# env:
# TEST_MODE_ACTIVE: true
- name: Download MacOS-ARM Installer Zip
run: |
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ needs.publish-release.outputs.release-version }}/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm.zip
- name: Create macos-arm-pkg Installer
id: run_installers_arm_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.publish-release.outputs.project-version }} -p ./../../ -a arm
echo "Created macos-arm-pkg successfully"
- name: Sign the MacOS-ARM installer
run: |
cosign sign-blob installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg --output-certificate ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.pem --output-signature ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sig --yes
- name: Verify the MacOS-ARM installer
run: |
cosign verify-blob installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg --certificate ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.pem --signature ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sha256 installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg
- name: Upload MacOS-ARM pkg Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sha256
asset_path: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sha256
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM pkg Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg
asset_path: installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM installer's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.pem
asset_path: ./ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.pem
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM installer's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sig
asset_path: ./ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sig
asset_content_type: application/octet-stream
windows-installer-build:
name: Windows Installer Build
needs: publish-release
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: "temurin"
java-version: "21.0.3"
- uses: actions/setup-dotnet@v1
with:
dotnet-version: "2.1.x"
- name: Install GUID Generator
run: dotnet tool install -g dotnet-guid --version 0.5.2
- name: Set up Wix toolkit
run: echo "${WIX}bin" >> $GITHUB_PATH
shell: bash
- name: Set cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Download Windows Installer Zip
run: |
echo default login ${{ secrets.BALLERINA_BOT_USERNAME }} password ${{ secrets.BALLERINA_BOT_TOKEN }} >> _netrc
curl --netrc-file _netrc -L -o ballerina-${{ needs.publish-release.outputs.project-version }}-windows.zip https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ needs.publish-release.outputs.release-version }}/ballerina-${{ needs.publish-release.outputs.project-version }}-windows.zip
- name: Create windows-msi Installer
id: run_installers_msi
run: |
move installers\windows .\..\..\
cd ..\..\windows
.\build-ballerina-windows-x64.bat --version ${{ needs.publish-release.outputs.project-version }} --path .\..\ballerina-distribution\ballerina-distribution
- name: Sign the Windows installer
run: |
cosign sign-blob D:\a\windows\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi --output-certificate ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.pem --output-signature ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sig --yes
- name: Verify the Windows installer
run: |
cosign verify-blob D:\a\windows\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi --certificate ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.pem --signature ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/publish-release.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sha256 D:\a\windows\target\msi\ballerina-*-windows-x64.msi
- name: Upload Windows msi Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sha256
asset_path: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sha256
asset_content_type: application/octet-stream
- name: Upload Windows msi Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi
asset_path: D:\a\windows\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi
asset_content_type: application/octet-stream
- name: Upload Windows installer's Certificate
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.pem
asset_path: ./ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.pem
asset_content_type: application/octet-stream
- name: Upload Windows installer's Signature
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sig
asset_path: ./ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sig
asset_content_type: application/octet-stream
- name: Install Ballerina msi
run: msiexec /i D:\a\windows\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi /quiet /qr
shell: cmd
- name: Update Installer Test Configs
run: |
set DISPLAY_TEXT=${{ needs.publish-release.outputs.lang-version }}
set SWAN_LAKE_LATEST_VERSION=swan-lake-%DISPLAY_TEXT%
perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=%DISPLAY_TEXT%/" ballerina-test-automation/gradle.properties
perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=%SWAN_LAKE_LATEST_VERSION%/" ballerina-test-automation/gradle.properties
shell: cmd
# - name: Run Installer Tests
# working-directory: .\ballerina-test-automation\installer-test
# run: |
# $env:Path += ";C:\Program Files\Ballerina\bin"
# .\..\gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
================================================
FILE: .github/workflows/publish_release_bi.yml
================================================
name: Publish Release Kola Distribution
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # 07:30 in LK time (GMT+5:30)
permissions:
id-token: write
contents: write
jobs:
publish-release:
name: Publish Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '10.22.1'
- name: Clone ballerina-dev-tools
run: git clone https://github.com/ballerina-platform/ballerina-dev-tools.git
- name: Build ballerina-dev-tools
id: build-dev-tools
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
TEST_MODE_ACTIVE: true
run: |
cd ballerina-dev-tools
VERSION=$((grep -w 'version' | cut -d= -f2 | cut -d- -f1) < gradle.properties)
sed -i "s/^version=.*/version=$VERSION/" gradle.properties
echo "::set-output name=version::$(grep "^version=" gradle.properties | cut -d'=' -f2)"
echo "::set-output name=langVersion::$(grep "^ballerinaLangVersion=" gradle.properties | cut -d'=' -f2)"
./gradlew clean build --stacktrace --scan -x test --console=plain --no-daemon --continue publishToMavenLocal
cd ..
rm -rf ballerina-dev-tools
- name: Set version env variable
id: version-set
run: |
sed -i "s/^devToolsVersion=.*/devToolsVersion=${{ steps.build-dev-tools.outputs.version }}/" gradle.properties
sed -i "s/^ballerinaLangVersion=.*/ballerinaLangVersion=${{ steps.build-dev-tools.outputs.langVersion }}/" gradle.properties
SHORT_VERSION=$((grep -w 'version' | cut -d= -f2 | cut -d- -f1) < gradle.properties)
DIST_VERSION=$((grep -w 'version' | cut -d= -f2) < gradle.properties | rev | cut --complement -d- -f1 | rev)
LANG_VERSION=$((grep -w "ballerinaLangVersion" | cut -d= -f2 | cut -d- -f1 | xargs) < gradle.properties)
CODE_NAME=$((grep -w 'codeName' | cut -d= -f2) < gradle.properties)
RELEASE_VERSION=$DIST_VERSION
TAGGED_VERSION=$RELEASE_VERSION-bi-pack-$(TZ="Asia/Kolkata" date +'%Y%m%d-%H%M00')
LONG_VERSION=$DIST_VERSION-$CODE_NAME
echo VERSION=$RELEASE_VERSION >> $GITHUB_ENV
echo GIT_TAG=$TAGGED_VERSION >> $GITHUB_ENV
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=sversion::$SHORT_VERSION"
echo "::set-output name=taggedVersion::$TAGGED_VERSION"
echo "::set-output name=longVersion::$LONG_VERSION"
echo "::set-output name=langVersion::$LANG_VERSION"
- name: Pre release dependency version update
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
run: |
echo "Version: ${VERSION}"
echo "Tagged Version: ${GIT_TAG}"
git config user.name ${{ secrets.BALLERINA_BOT_USERNAME }}
git config user.email ${{ secrets.BALLERINA_BOT_EMAIL }}
git checkout -b release-${GIT_TAG}
- name: Generate UUID
run: |
UUID=$(uuidgen)
perl -pi -e "s/^\s*installerVersion=.*/installerVersion=$UUID/" gradle.properties
git config user.name ${{ secrets.BALLERINA_BOT_USERNAME }}
git config user.email ${{ secrets.BALLERINA_BOT_EMAIL }}
git add gradle.properties
git commit -m "Update UUID for installer"
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Publish artifact
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
packageUser: ${{ secrets.BALLERINA_BOT_USERNAME }}
packagePAT: ${{ secrets.BALLERINA_BOT_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
prodCentralToken: ${{ secrets.BALLERINA_CENTRAL_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
run: |
./gradlew build -x test -x project-api-tests:test -Pversion=${VERSION}
./gradlew release -Prelease.useAutomaticVersion=true -x test
- name: Checkout docker repo
uses: actions/checkout@v3
with:
repository: ballerina-platform/module-ballerina-docker
path: module-ballerina-docker
- name: Copy zip artifact
run: cp ballerina/build/distributions/ballerina-22*.zip module-ballerina-docker/base/docker/
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Build the docker image
id: docker_build
uses: docker/build-push-action@v2
with:
context: module-ballerina-docker/base/docker/
load: true
push: false
tags: ballerina/ballerina:release-test
build-args: |
BALLERINA_DIST=ballerina-${{ steps.version-set.outputs.sversion }}.zip
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ballerina/ballerina:release-test'
skip-dirs: 'ballerina/runtime/examples'
format: 'table'
exit-code: '1'
timeout: "10m0s"
severity: 'CRITICAL,HIGH'
- name: cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install GitHub CLI
run: |
npm install -g github-cli
gh --version
- name: Get Markdown file
id: file-url
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh repo view ballerina-platform/ballerina-dev-website --json url --jq '.clone_url'
gh api repos/ballerina-platform/ballerina-dev-website/contents/downloads/verify-ballerina-artifacts.md -H 'Accept: application/vnd.github.v3.raw' > release_notes.md
sed -i '1,10d' release_notes.md
- name: Retrieve Branch
id: retrieve-branch
run: |
branchName=$(echo ${{ github.ref }} | cut -d'/' -f3)
echo "::set-output name=branchName::$branchName"
- name: Update Markdown file
run: echo "" > release_notes.md;
- name: Read release notes from file
id: release_notes
uses: actions/github-script@v4
with:
github-token: ${{ secrets.BALLERINA_BOT_TOKEN }}
script: |
const fs = require('fs');
const releaseNotes = fs.readFileSync('release_notes.md', 'utf8');
core.setOutput('notes', releaseNotes);
- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
tag_name: "v${{ steps.version-set.outputs.taggedVersion }}"
release_name: ${{ steps.version-set.outputs.taggedVersion }}
body: ${{ steps.release_notes.outputs.notes }}
draft: false
prerelease: true
- name: Create linux-deb Installer
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.version-set.outputs.longVersion }} -p ./../../ballerina/build/distributions
echo "Created linux-deb successfully"
- name: Create linux-rpm Installer
id: run_installers_rpm
run: |
cd installers/linux-rpm
./build-ballerina-linux-rpm-x64.sh -v ${{ steps.version-set.outputs.longVersion }} -p ./../../ballerina/build/distributions
echo "Created linux-rpm successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sha256 installers/linux-deb/target/ballerina-*-linux-x64.deb
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sha256 installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-*-linux-x64.rpm
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.longVersion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}.zip
openssl dgst -sha256 -out ballerina-${{ steps.version-set.outputs.sversion }}.zip.sha256 ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.sversion }}.zip
- name: Upload zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}.zip
asset_content_type: application/octet-stream
- name: Upload zip without tool artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.sversion }}.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.sversion }}.zip
asset_content_type: application/octet-stream
- name: Upload Linux deb Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb
asset_path: installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb
asset_content_type: application/octet-stream
- name: Upload Linux rpm Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm
asset_path: installers/linux-rpm/rpmbuild/RPMS/x86_64/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm
asset_content_type: application/octet-stream
- name: Upload Linux zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux.zip
asset_content_type: application/octet-stream
- name: Upload Linux-ARM zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm.zip
asset_content_type: application/octet-stream
- name: Upload MacOS zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos.zip
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-macos-arm.zip
asset_content_type: application/octet-stream
- name: Upload Windows zip artifacts
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-windows.zip
asset_path: ballerina/build/distributions/ballerina-${{ steps.version-set.outputs.longVersion }}-windows.zip
asset_content_type: application/octet-stream
- name: Upload Linux deb Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sha256
asset_path: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.deb.sha256
asset_content_type: application/octet-stream
- name: Upload Linux rpm Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sha256
asset_path: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-x64.rpm.sha256
asset_content_type: application/octet-stream
- name: Upload Ballerina zip Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}.zip.sha256
asset_path: ballerina-${{ steps.version-set.outputs.longVersion }}.zip.sha256
asset_content_type: application/octet-stream
- name: Upload ballerina Short Name zip Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.sversion }}.zip.sha256
asset_path: ballerina-${{ steps.version-set.outputs.sversion }}.zip.sha256
asset_content_type: application/octet-stream
# - name: Install Ballerina DEB
# run: sudo dpkg -i installers/linux-deb/target/ballerina-*-linux-x64.deb
# - name: Update Installer Test Configs
# run: |
# DISPLAY_TEXT=${{ steps.version-set.outputs.langVersion }}
# SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
# perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
# perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
# - name: Run Installer Tests
# working-directory: ./ballerina-test-automation/installer-test
# run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
# env:
# TEST_MODE_ACTIVE: true
- name: Create linux-arm-deb Installer
run: |
cd installers/linux-deb
./build-ballerina-linux-deb-x64.sh -v ${{ steps.version-set.outputs.longVersion }} -p ./../../ballerina/build/distributions -a arm
echo "Created linux-arm-deb successfully"
- name: Upload Linux-ARM deb Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_name: ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb
asset_path: installers/linux-deb/target/ballerina-${{ steps.version-set.outputs.longVersion }}-linux-arm-x64.deb
asset_content_type: application/octet-stream
outputs:
project-version: ${{ steps.version-set.outputs.longVersion }}
upload-asset-url: ${{ steps.create_release.outputs.upload_url }}
release-version: ${{ steps.version-set.outputs.taggedVersion }}
lang-version: ${{ steps.version-set.outputs.langVersion }}
macos-installer-build:
name: MacOS Installer Build
needs: publish-release
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Download MacOS Intaller Zip
run: |
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ needs.publish-release.outputs.release-version }}/ballerina-${{ needs.publish-release.outputs.project-version }}-macos.zip
- name: cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Create macos-pkg Installer
id: run_installers_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.publish-release.outputs.project-version }} -p ./../../
echo "Created macos-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sha256 installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg
- name: Upload MacOS pkg Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sha256
asset_path: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg.sha256
asset_content_type: application/octet-stream
- name: Upload MacOS pkg Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg
asset_path: installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-x64.pkg
asset_content_type: application/octet-stream
# - name: Install Ballerina PKG
# run: sudo installer -pkg installers/mac/target/pkg/ballerina-*-macos-x64.pkg -target /
# - name: Update Installer Test Configs
# run: |
# DISPLAY_TEXT=${{ needs.publish-release.outputs.lang-version }}
# SWAN_LAKE_LATEST_VERSION="swan-lake-"+$DISPLAY_TEXT
# perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=$DISPLAY_TEXT/" ballerina-test-automation/gradle.properties
# perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=$SWAN_LAKE_LATEST_VERSION/" ballerina-test-automation/gradle.properties
# - name: Run Installer Tests
# working-directory: ./ballerina-test-automation/installer-test
# run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
# env:
# TEST_MODE_ACTIVE: true
- name: Download MacOS-ARM Installer Zip
run: |
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ needs.publish-release.outputs.release-version }}/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm.zip
- name: Create macos-arm-pkg Installer
id: run_installers_arm_pkg
run: |
cd installers/mac
./build-ballerina-macos-x64.sh -v ${{ needs.publish-release.outputs.project-version }} -p ./../../ -a arm
echo "Created macos-arm-pkg successfully"
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sha256 installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg
- name: Upload MacOS-ARM pkg Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sha256
asset_path: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg.sha256
asset_content_type: application/octet-stream
- name: Upload MacOS-ARM pkg Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg
asset_path: installers/mac/target/pkg/ballerina-${{ needs.publish-release.outputs.project-version }}-macos-arm-x64.pkg
asset_content_type: application/octet-stream
windows-installer-build:
name: Windows Installer Build
needs: publish-release
runs-on: windows-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up JDK 21
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: '21.0.3'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '2.1.x'
- name: Install GUID Generator
run: dotnet tool install -g dotnet-guid --version 0.5.2
- name: Set up Wix toolkit
run: echo "${WIX}bin" >> $GITHUB_PATH
shell: bash
- name: Set cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Download Windows Installer Zip
run: |
echo default login ${{ secrets.BALLERINA_BOT_USERNAME }} password ${{ secrets.BALLERINA_BOT_TOKEN }} >> _netrc
curl --netrc-file _netrc -L -o ballerina-${{ needs.publish-release.outputs.project-version }}-windows.zip https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ needs.publish-release.outputs.release-version }}/ballerina-${{ needs.publish-release.outputs.project-version }}-windows.zip
- name: Create windows-msi Installer
id: run_installers_msi
run: |
move installers\windows .\..\..\
cd ..\..\windows
.\build-ballerina-windows-x64.bat --version ${{ needs.publish-release.outputs.project-version }} --path .\..\ballerina-distribution\ballerina-distribution
- name: Generate Hashes
run: |
openssl dgst -sha256 -out ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sha256 D:\a\windows\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi
- name: Upload Windows msi Hashes
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sha256
asset_path: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi.sha256
asset_content_type: application/octet-stream
- name: Upload Windows msi Installer
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.BALLERINA_BOT_TOKEN }}
with:
upload_url: ${{ needs.publish-release.outputs.upload-asset-url }}
asset_name: ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi
asset_path: D:\a\windows\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi
asset_content_type: application/octet-stream
# - name: Install Ballerina msi
# run: msiexec /i w\target\msi\ballerina-${{ needs.publish-release.outputs.project-version }}-windows-x64.msi /quiet /qr
# shell: cmd
# - name: Update Installer Test Configs
# run: |
# set DISPLAY_TEXT=${{ needs.publish-release.outputs.lang-version }}
# set SWAN_LAKE_LATEST_VERSION=swan-lake-%DISPLAY_TEXT%
# perl -pi -e "s/^\s*swan-lake-latest-version-display-text=.*/swan-lake-latest-version-display-text=%DISPLAY_TEXT%/" ballerina-test-automation/gradle.properties
# perl -pi -e "s/^\s*swan-lake-latest-version=.*/swan-lake-latest-version=%SWAN_LAKE_LATEST_VERSION%/" ballerina-test-automation/gradle.properties
# shell: cmd
# - name: Run Installer Tests
# working-directory: .\ballerina-test-automation\installer-test
# run: |
# $env:Path += ";C:\Program Files\Ballerina\bin"
# .\..\gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
send-notification:
name: Send Notification
runs-on: ubuntu-latest
needs: [publish-release, macos-installer-build, windows-installer-build]
steps:
- name: Send notification to Google Chat
run: |
body=$(cat << 'EOF'
{
"cardsV2": [
{
"cardId": "published-ballerina-distribution",
"card": {
"header": {
"title": "Ballerina Distribution",
"subtitle": "v${{ needs.publish-release.outputs.release-version }}",
"imageUrl": "https://lh6.googleusercontent.com/proxy/R9Rx8vYNd-_HZn58ckf5PNX7RMlC6P-B75fB7UQ_GFH5R0UwtfJ1gVNARBvH1us8LBuK4NVFsvMGnwZkm-H2_9ACwH_j0lQmExR1SRMNGlFcbrm_1O7foFpqqOiVzA",
"imageType": "CIRCLE"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "Run \"Update Kola\" to update the distribution."
}
},
{
"buttonList": {
"buttons": [
{
"text": "Download",
"onClick": {
"openLink": {
"url": "https://github.com/${{ github.repository }}/releases/tag/v${{ needs.publish-release.outputs.release-version }}"
}
}
}
]
}
}
]
}
]
}
}
]
}
EOF
)
curl -X POST -H 'Content-Type: application/json' "https://chat.googleapis.com/v1/spaces/AAAApvQDm3o/messages?key=${{ secrets.EDITOR_CHAT_BOT_KEY }}&token=${{ secrets.EDITOR_CHAT_BOT_TOKEN }}" -d "$body"
send-failure-notification:
name: Send Failure Notification
runs-on: ubuntu-latest
if: ${{ failure() }}
needs: [publish-release, macos-installer-build, windows-installer-build]
steps:
- name: Send failure notification to Google Chat
run: |
body=$(cat << 'EOF'
{
"cardsV2": [
{
"cardId": "ballerina-dist-build-error",
"card": {
"header": {
"title": "Ballerina Distribution",
"subtitle": "Release Failure",
"imageUrl": "https://lh6.googleusercontent.com/proxy/R9Rx8vYNd-_HZn58ckf5PNX7RMlC6P-B75fB7UQ_GFH5R0UwtfJ1gVNARBvH1us8LBuK4NVFsvMGnwZkm-H2_9ACwH_j0lQmExR1SRMNGlFcbrm_1O7foFpqqOiVzA",
"imageType": "CIRCLE"
},
"sections": [
{
"widgets": [
{
"decoratedText": {
"text": "Error: The build has failed",
"startIcon": {
"materialIcon": {
"name": "error",
"fill": true,
"weight": 500,
"grade": 100
}
}
}
},
{
"textParagraph": {
"text": "Please view the workflow for further details."
}
},
{
"buttonList": {
"buttons": [
{
"text": "View Workflow",
"onClick": {
"openLink": {
"url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}
}
}
]
}
}
]
}
]
}
}
]
}
EOF
)
curl -X POST -H 'Content-Type: application/json' "https://chat.googleapis.com/v1/spaces/AAAApvQDm3o/messages?key=${{ secrets.EDITOR_CHAT_BOT_KEY }}&token=${{ secrets.EDITOR_CHAT_BOT_TOKEN }}" -d "$body"
================================================
FILE: .github/workflows/pull-request.yml
================================================
name: Pull Request
on:
pull_request:
branches:
- master
- ballerina-1.1.x
- ballerina-1.2.x
- 2201.[0-9]+.x
jobs:
ubuntu-integration-tests:
name: Integration Tests on Ubuntu
runs-on: ubuntu-latest
concurrency:
group: ${{ github.ref }}-integration-tests
cancel-in-progress: true
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build Ballerina Distribution
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
run: ./gradlew clean build --stacktrace --scan --console=plain --no-daemon --continue -x :ballerina:testExamples -x :project-api-tests:test
ubuntu-bbe-tests:
name: BBE Tests on Ubuntu
runs-on: ubuntu-latest
concurrency:
group: ${{ github.ref }}-bbe-tests
cancel-in-progress: true
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build Ballerina Distribution
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
run: ./gradlew clean :ballerina:testExamples --stacktrace --scan --console=plain --no-daemon --continue -x :project-api-tests:test
windows-build-without-tests:
name: Windows Build
runs-on: windows-latest
concurrency:
group: ${{ github.ref }}-windows
cancel-in-progress: true
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21.0.3'
- name: Build Ballerina Distribution
env:
packageUser: ${{ github.actor }}
packagePAT: ${{ secrets.GITHUB_TOKEN }}
JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8
devCentralToken: ${{ secrets.BALLERINA_CENTRAL_DEV_ACCESS_TOKEN }}
githubAccessToken: ${{ secrets.GITHUB_TOKEN }}
ballerinaBotWorkflow: $ {{ secrets.BALLERINA_BOT_WORKFLOW }}
run: ./gradlew.bat clean build --stacktrace --scan --console=plain --no-daemon --continue -x test
================================================
FILE: .github/workflows/sign-installers.yml
================================================
name: Sign release artifacts
on:
workflow_dispatch:
inputs:
versionName:
description: 'Specify the Version name eg: 2201.6.0'
required: true
default: ''
permissions:
id-token: write
contents: write
jobs:
sign-release:
name: Sign release artifacts
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: cosign-installer
uses: sigstore/cosign-installer@v3.5.0
- name: Install Node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install GitHub CLI
run: |
npm install -g github-cli
- name: Retrieve MacOS Installer
run:
|
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ github.event.inputs.versionName }}/ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg
- name: Sign the MacOS Installer
run: |
cosign sign-blob ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg --output-certificate ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg.pem --output-signature ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg.sig --yes
- name: Verify the MacOS Installer
run: |
cosign verify-blob ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg --certificate ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg.pem --signature ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/sign-installers.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Retrieve MacOS-ARM Installer
run:
|
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ github.event.inputs.versionName }}/ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg
- name: Sign the MacOS-ARM Installer
run: |
cosign sign-blob ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg --output-certificate ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg.pem --output-signature ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg.sig --yes
- name: Verify the MacOS-ARM Installer
run: |
cosign verify-blob ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg --certificate ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg.pem --signature ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/sign-installers.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Retrieve Windows Installer
run:
|
wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ github.event.inputs.versionName }}/ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi
- name: Sign the Windows Installer
run: |
cosign sign-blob ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi --output-certificate ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi.pem --output-signature ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi.sig --yes
- name: Verify the Windows Installer
run: |
cosign verify-blob ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi --certificate ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi.pem --signature ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi.sig --certificate-identity=https://github.com/ballerina-platform/ballerina-distribution/.github/workflows/sign-installers.yml@${{ github.ref }} --certificate-oidc-issuer=https://token.actions.githubusercontent.com
- name: Upload Installers' Verification Files
env:
GH_TOKEN : ${{ secrets.BALLERINA_BOT_TOKEN }}
run: |
gh release upload v${{ github.event.inputs.versionName }} ./ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg.pem --clobber
gh release upload v${{ github.event.inputs.versionName }} ./ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-x64.pkg.sig --clobber
gh release upload v${{ github.event.inputs.versionName }} ./ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg.pem --clobber
gh release upload v${{ github.event.inputs.versionName }} ./ballerina-${{ github.event.inputs.versionName }}-swan-lake-macos-arm-x64.pkg.sig --clobber
gh release upload v${{ github.event.inputs.versionName }} ./ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi.pem --clobber
gh release upload v${{ github.event.inputs.versionName }} ./ballerina-${{ github.event.inputs.versionName }}-swan-lake-windows-x64.msi.sig --clobber
================================================
FILE: .github/workflows/test-installers.yml
================================================
name: Test Installers
on:
workflow_dispatch:
inputs:
releaseVersion:
description: 'Version of the release. e.g., swan-lake-beta1'
required: true
preReleaseSuffix:
description: 'The text that will be suffixed to the Git tag. e.g., rc1'
required: false
default: ''
jobs:
macos-installer-test:
runs-on: macos-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set Version
id: set-version
run: |
RELEASE_VERSION=${{ github.event.inputs.releaseVersion }}
TAGGED_VERSION=$RELEASE_VERSION
if [ -n "${{ github.event.inputs.preReleaseSuffix }}" ]; then
TAGGED_VERSION=$RELEASE_VERSION-${{ github.event.inputs.preReleaseSuffix }}
fi
echo VERSION=$RELEASE_VERSION >> $GITHUB_ENV
echo GIT_TAG=$TAGGED_VERSION >> $GITHUB_ENV
echo "::set-output name=version::$RELEASE_VERSION"
echo "::set-output name=taggedVersion::$TAGGED_VERSION"
- name: Download Ballerina pkg Installer
run: wget https://github.com/ballerina-platform/ballerina-distribution/releases/download/v${{ steps.set-version.outputs.taggedVersion }}/ballerina-macos-x64-${{ steps.set-version.outputs.version }}.pkg
- name: Install Ballerina pkg
run: sudo installer -pkg ballerina-macos-x64-${{ steps.set-version.outputs.version }}.pkg -target /
- name: Run Installer Tests
working-directory: ./ballerina-test-automation/installer-test
run: ./../gradlew build --stacktrace -scan --console=plain --no-daemon -DballerinaInstalled=true
================================================
FILE: .github/workflows/trigger-bbe-generation.yml
================================================
name: Generate Ballerina By Examples
on:
push:
branches:
- '2201.0.x'
paths:
- 'examples/**'
jobs:
trigger-bbe-gen:
name: Trigger Ballerina By Examples genration
if: github.repository_owner == 'ballerina-platform'
runs-on: ubuntu-latest
steps:
- run: |
curl --request \
POST 'https://api.github.com/repos/ballerina-platform/ballerina-dev-website/dispatches' \
--header 'Accept: application/vnd.github.v3+json' \
--header 'Authorization: Bearer ${{ secrets.BALLERINA_BOT_TOKEN }}' \
--header 'Content-Type: application/json' \
--data-raw '{
"event_type": "distribution-update"
}'
================================================
FILE: .gitignore
================================================
*.class
*.log
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
.idea
*.iml
*.ipr
*.iws
# generated files
target
/gen
/compiler/ballerina-lang/src/main/resources/grammar/BallerinaLexer.tokens
/tool-plugins/intellij/src/main/antlr/org/ballerinalang/plugins/idea/grammar/BallerinaLexer.tokens
velocity.log
# direct downloads
ballerina/downloads/ballerina-jre-artifacts-zip/
# gradle
.gradle
build/
gradle-app.setting
!gradle-wrapper.jar
.gradletasknamecache
# mac
.DS_Store
.classpath
.project
.settings
.vscode
# Temporarily ignore
CVE-2025-27553
CVE-2025-30474
# Config.toml files that may be used with examples
examples/**/Config.toml
================================================
FILE: .trivyignore
================================================
# False positive
CVE-2020-7768
# Need to be fixed
CVE-2025-48924
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
================================================
FILE: README.md
================================================
# Ballerina Distribution
[](https://github.com/ballerina-platform/ballerina-distribution/actions?query=workflow%3A%22Build%22)
[](https://github.com/ballerina-platform/ballerina-distribution/actions?query=workflow%3A%22Daily+build%22)
The Ballerina distribution repository builds the final Ballerina distributions. It combines the Ballerina runtime with standard libraries and language extensions.
## Table of contents
- [Getting started](#getting-started)
- [Download and install](#download-and-install)
- [Contributing to Ballerina](#contributing-to-ballerina)
- [License](#license)
- [Useful links](#useful-links)
## Getting started
You can use one of the following options to try out Ballerina.
* [Getting Started](https://ballerina.io/learn/getting-started/)
* [Quick Tour](https://ballerina.io/learn/quick-tour/)
* [Ballerina by Example](https://ballerina.io/learn/by-example/)
## Download and install
### Download the binary
You can download the Ballerina distribution at http://ballerina.io/downloads.
### Install from source
Alternatively, you can install Ballerina from the source using the following instructions.
#### Prerequisites
* JDK21 ([Adopt OpenJDK21](https://adoptopenjdk.net/) or any other OpenJDK distribution)
#### Building the source
1. Clone this repository using the following command.
```bash
git clone https://github.com/ballerina-platform/ballerina-distribution
```
2. This repository is depending on Github packages. You need to have a personal access token with read package permissions. Then you need to set following environment variables.
Linux/Unix
```bash
export packageUser=
export packagePAT=
```
Windows
```batch
set packageUser=
set packagePAT=
```
3. This repository contains central integration tests. You need to have dev access token of the bctestorg organization to run these tests. Then you need to set following environment variables.
Linux/Unix
```bash
export BALLERINA_DEV_CENTRAL=true
export BALLERINA_CENTRAL_ACCESS_TOKEN=
```
Windows
```batch
set BALLERINA_DEV_CENTRAL=true
set BALLERINA_CENTRAL_ACCESS_TOKEN=
```
Else you can disable central integration tests and build the repository.
4. Run the Gradle build command ``./gradlew build`` from the repository root directory.
5. Out of the ZIP distributions in the `ballerina/build/distributions/` directory, extract the `ballerina--SNAPSHOT.zip` file (e.g., `ballerina-swan-lake-beta3-SNAPSHOT.zip`). The other distributions are used for installer generation.
## Contributing to Ballerina
As an open source project, Ballerina welcomes contributions from the community. To start contributing, read these [contribution guidelines](https://github.com/ballerina-platform/ballerina-lang/blob/master/CONTRIBUTING.md) for information on how you should go about contributing to our project.
Check the issue tracker for open issues that interest you. We look forward to receiving your contributions.
## License
Ballerina code is distributed under [Apache license 2.0](https://github.com/ballerina-platform/ballerina-lang/blob/master/LICENSE).
## Useful links
* The ballerina-dev@googlegroups.com mailing list is for discussing code changes to the Ballerina project.
* Chat live with us via our [Discord server](https://discord.gg/ballerinalang).
* Technical questions should be posted on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag.
================================================
FILE: ballerina/COPYRIGHT
================================================
Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
This product includes software developed at WSO2 (http://wso2.com/).
=========================================================================
== ANTLR Notice ==
=========================================================================
[The BSD License]
Copyright (c) 2012 Terence Parr and Sam Harwell
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
- Neither the name of the author nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=========================================================================
== Saxon-HE Notice ==
=========================================================================
Most of the open source code in the Saxon product is governed by the Mozilla Public
License version 2.0, which is reproduced below.
https://www.mozilla.org/en-US/MPL/2.0/
=========================================================================
(This notice is included in the Saxon distribution because Saxon includes a QuickSort
module that was originally developed by Wolfgang Hoschek at CERN, and which was licensed
for use under the conditions specified here.)
Copyright © 1999 CERN - European Organization for Nuclear Research.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose
is hereby granted without fee, provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear in supporting documentation.
CERN makes no representations about the suitability of this software for any purpose.
It is provided "as is" without expressed or implied warranty.
=========================================================================
(This notice is included in the Saxon distribution because Saxon's XPath parser
was originally derived from an XPath parser written by James Clark and made available
under this license. The Saxon XPath parser has since diverged very substantially, but
there are traces of the original code still present.)
Copyright (c) 1998, 1999 James Clark
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL JAMES CLARK BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of James Clark shall
not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization
from James Clark.
=========================================================================
(This notice is included in the Saxon distribution because Saxon
uses code for conversion of XML Schema Regular expressions to
Java/.NET regular expressions that was originally written by James
Clark and made available under this license. The Saxon version of
the code has been enhanced in various ways but is still recognizably
based on the original.)
Copyright (c) 2001-2003 Thai Open Source Software Center Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
Neither the name of the Thai Open Source Software Center Ltd nor
the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=========================================================================
(This notice is included in the Saxon distribution because Saxon
uses code performing Unicode Normalization that was originally written by Mark
Davis and made available under this license. The Saxon version of the
code has been enhanced in various minor ways but is still recognizably
based on the original. For details of modifications, see the comments in
the source code.)
COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2007 Unicode, Inc. All rights reserved. Distributed under the Terms of Use
in http://www.unicode.org/copyright.html.
Permission is hereby granted, free of charge, to any person obtaining a copy of the Unicode
data files and any associated documentation (the "Data Files") or Unicode software and any
associated documentation (the "Software") to deal in the Data Files or Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute,
and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or
Software are furnished to do so, provided that (a) the above copyright notice(s) and this
permission notice appear with all copies of the Data Files or Software, (b) both the above
copyright notice(s) and this permission notice appear in associated documentation, and
(c) there is clear notice in each modified Data File or in the Software as well as in the
documentation associated with the Data File(s) or Software that the data or software has
been modified.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not be used
in advertising or otherwise to promote the sale, use or other dealings in these
Data Files or Software without prior written authorization of the copyright holder.
=========================================================================
== SLF4J Notice ==
=========================================================================
Copyright (c) 2004-2017 QOS.ch
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: ballerina/LICENSE
================================================
This product is licensed by WSO2 LLC. under Apache License 2.0. The license
can be downloaded from the following locations:
http://www.apache.org/licenses/LICENSE-2.0.html
http://www.apache.org/licenses/LICENSE-2.0.txt
This product also contains software under different licenses. This table below
all the contained libraries (jar files) and the license under which they are
provided to you.
At the bottom of this file is a table that shows what each license indicated
below is and where the actual text of the license can be found.
Name Type License
_________________________________________________________________________________________________________________________________________________
ballerina-command-1.5.0.jar jar apache2
sequence-model-generator-ls-extension-2.0.0.jar jar apache2
org.eclipse.lsp4j.jsonrpc-0.15.0.jar bundle apache2
json-to-record-converter-2201.12.0.jar jar apache2
ballerinalang-data-mapper-2201.12.0.jar jar apache2
openapi-ls-extension-2.3.0.jar jar apache2
graphql-model-generator-ls-extension-2.0.0.jar jar apache2
language-server-core-2201.12.0.jar jar apache2
performance-analyzer-services-2201.12.0.jar jar apache2
flow-model-generator-ls-extension-2.0.0.jar jar apache2
xml-to-record-converter-2201.12.0.jar jar apache2
language-server-cli-2201.12.0.jar jar apache2
service-model-generator-ls-extension-2.0.0.jar jar apache2
trigger-service-2201.12.0.jar jar apache2
language-server-stdio-launcher-2201.12.0.jar jar apache2
test-manager-service-ls-extension-2.0.0.jar jar apache2
commons-io-2.15.1.jar bundle apache2
architecture-model-generator-ls-extension-2.0.0.jar jar apache2
org.eclipse.lsp4j-0.15.0.jar bundle apache2
partial-parser-2201.12.0.jar jar apache2
guava-32.0.1-jre.jar bundle apache2
cloud-tooling-3.3.0.jar jar apache2
bal-shell-service-2201.12.0.jar jar apache2
debug-adapter-core-2201.12.0.jar jar apache2
java-semver-0.9.0.jar jar mit
org.eclipse.lsp4j.jsonrpc.debug-0.15.0.jar bundle epl2
debug-adapter-cli-2201.12.0.jar jar apache2
org.eclipse.lsp4j.debug-0.12.0.jar bundle epl2
central-client-2201.12.0.jar jar apache2
okhttp-3.14.0.jar jar apache2
object-2201.12.0.jar jar apache2
graphql-model-generator-core-2.0.0.jar jar apache2
syntax-api-calls-gen-2201.12.0.jar jar apache2
jackson-dataformat-yaml-2.15.3.jar bundle apache2
asm-analysis-9.7.jar bundle bsd3
table-2201.12.0.jar jar apache2
ballerina-to-openapi-2.3.0.jar jar apache2
jaeger-core-0.31.0.jar jar apache2
transactions-jta-5.0.8.jar jar apache2
value-2201.12.0.jar jar apache2
commons-beanutils-1.9.4.jar bundle apache2
ballerina-tools-api-2201.12.0.jar jar apache2
jballerina.java-2201.12.0.jar jar apache2
commons-codec-1.14.jar bundle apache2
axiom-impl-1.4.0.jar bundle apache2
formatter-cli-2201.12.0.jar jar apache2
ballerina-linter-2201.12.0.jar jar apache2
asm-commons-9.7.jar bundle bsd3
shell-core-2201.12.0.jar jar apache2
string-2201.12.0.jar jar apache2
transaction-2201.12.0.jar jar apache2
ballerina-bindgen-2201.12.0.jar jar apache2
jackson-core-2.15.3.jar bundle apache2
asm-9.7.jar bundle bsd3
jackson-annotations-2.15.3.jar bundle apache2
graphql-code-generator-0.13.0.jar jar mit
formatter-core-2201.12.0.jar jar apache2
ballerina-metrics-extension-2201.12.0.jar jar apache2
ballerina-parser-2201.12.0.jar jar apache2
stream-2201.12.0.jar jar apache2
netty-resolver-4.1.118.Final.jar bundle apache2
kotlin-stdlib-common-1.6.0.jar jar apache2
function-2201.12.0.jar jar apache2
maven-resolver-2201.12.0.jar jar apache2
java-diff-utils-4.5.jar bundle apache2
toml-parser-2201.12.0.jar jar mit
architecture-model-generator-plugin-2.0.0.jar jar apache2
annotations-2201.12.0.jar jar apache2
asyncapi-cli-0.11.0.jar jar apache2
testerina-runtime-2201.12.0.jar jar apache2
javax.mail-1.6.2.jar bundle cddl1
future-2201.12.0.jar jar apache2
org.wso2.carbon.metrics.core-2.3.7.jar bundle apache2
asm-tree-9.7.jar bundle bsd3
org.jacoco.report-0.8.12.jar bundle epl2
graphql-cli-0.13.0
commons-compress-1.26.2.jar bundle apache2
bool-2201.12.0
jline-3.25.0.jar bundle upl1.0
model-generator-commons-2.0.0
netty-transport-4.1.118.Final.jar bundle apache2
org.jacoco.core-0.8.12.jar bundle epl2
org.wso2.transport.local-file-system-6.0.55.jar bundle apache2
shell-cli-2201.12.0
jackson-databind-2.15.3.jar bundle apache2
integer-2201.12.0
javax.transaction-api-1.3.jar bundle cddl1
openapi-cli-2.3.0
snakeyaml-2.0.jar bundle apache2
stax2-api-4.2.1.jar bundle bsd
package-semantic-analyzer-1.0.0.jar jar apache2
ballerina-io-internal-2201.12.0
asm-util-9.7.jar bundle bsd3
openapi-build-extension-2.3.0
semver-checker-cli-2201.12.0
commons-text-1.10.0.jar bundle apache2
gson-2.10.1.jar bundle apache2
jaeger-thrift-0.31.0.jar jar apache2
openapi-bal-task-plugin-2.3.0
netty-common-4.1.118.Final.jar bundle apache2
regexp-2201.12.0
ballerina-cli-2201.12.0
testerina-core-2201.12.0
typedesc-2201.12.0
transactions-api-5.0.8.jar jar apache2
diagram-util-2201.12.0
persist-cli-1.6.0
query-2201.12.0
netty-buffer-4.1.118.Final.jar bundle apache2
sqlite-jdbc-3.47.0.0.jar bundle apache2
kotlin-stdlib-1.6.0.jar jar apache2
floatingpoint-2201.12.0
architecture-model-generator-core-2.0.0
org.wso2.securevault-1.0.0-wso2v2.jar bundle apache2
netty-transport-native-kqueue-4.1.118.Final.jar bundle apache2
graphql-schema-file-generator-0.13.0
okio-jvm-3.4.0.jar bundle apache2
geronimo-stax-api_1.0_spec-1.0.1.jar bundle apache2
language-server-commons-2201.12.0
woodstox-core-6.5.0.jar bundle apache2
org.wso2.carbon.core-5.1.0.jar bundle apache2
sequence-model-generator-core-2.0.0
transactions-5.0.8.jar jar apache2
jackson-datatype-jsr310-2.15.3.jar bundle apache2
error-2201.12.0
atomikos-util-5.0.8.jar jar apache2
configurable-schema-generator-2201.12.0
jacocoagent.jar jar apache2
decimal-2201.12.0
array-2201.12.0
netty-transport-native-epoll-4.1.118.Final.jar bundle apache2
compiler-0.8.9.jar jar apache2
org.wso2.carbon.messaging-2.3.7.jar bundle apache2
staxon-core-1.2.0.wso2v2.jar bundle apache2
docerina-2201.12.0
identifier-util-2201.12.0
openapi-validator-2.3.0
openapi-core-2.3.0
map-2201.12.0
axiom-api-1.4.0.jar bundle apache2
ballerina-profiler-1.0.jar jar apache2
xml-2201.12.0
picocli-4.0.1.jar bundle apache2
ballerina-rt-2201.12.0
progressbar-0.7.4.jar jar mit
ballerina-lang-2201.12.0
flow-model-generator-core-2.0.0
commons-collections-3.2.2.jar bundle apache2
protoc-cli-0.5.0
org.wso2.transport.http.netty-6.3.11.jar bundle apache2
semver-checker-core-2201.12.0
ballerinai-observe-0.0.0.jar jar apache2
ballerinai-transaction-0.0.0.jar jar apache2
ballerina-openapi-2.3.0.jar jar apache2
ballerina-toml-0.8.0.jar jar apache2
ballerina-toml.parser-0.8.0.jar jar apache2
ballerina-toml.lexer-0.8.0.jar jar apache2
ballerina-toml.writer-0.8.0.jar jar apache2
ballerina-data.yaml-0.8.0.jar jar apache2
ballerina-lang.__internal-0.0.0.jar jar apache2
ballerina-time-2.7.0.jar jar apache2
ballerina-ftp-2.13.0.jar jar apache2
ballerina-lang.future-0.0.0.jar jar apache2
ballerina-io-1.8.0.jar jar apache2
ballerina-lang.boolean-0.0.0.jar jar apache2
ballerina-lang.decimal-0.0.0.jar jar apache2
ballerina-avro-1.2.0.jar jar apache2
ballerina-lang.function-0.0.0.jar jar apache2
ballerina-auth-2.14.0.jar jar apache2
ballerina-tcp-1.13.0.jar jar apache2
ballerina-lang.array-0.0.0.jar jar apache2
ballerina-lang.string-0.0.0.jar jar apache2
ballerina-data.jsondata-1.1.0.jar jar apache2
ballerina-lang.object-0.0.0.jar jar apache2
ballerina-lang.transaction-0.0.0.jar jar apache2
ballerina-random-1.7.0.jar jar apache2
ballerina-lang.typedesc-0.0.0.jar jar apache2
ballerina-websocket-2.14.0.jar jar apache2
ballerina-jballerina.java-0.0.0.jar jar apache2
ballerina-observe-1.5.0.jar jar apache2
ballerina-observe.mockextension-1.5.0.jar jar apache2
ballerina-uuid-1.10.0.jar jar apache2
ballerina-email-2.12.0.jar jar apache2
ballerina-task-2.7.0.jar jar apache2
ballerina-edi-1.5.0.jar jar apache2
ballerina-lang.query-0.0.0.jar jar apache2
ballerina-cloud-3.3.0.jar jar apache2
ballerina-url-2.6.0.jar jar apache2
ballerina-lang.stream-0.0.0.jar jar apache2
ballerina-cache-3.10.0.jar jar apache2
ballerina-sql-1.16.0.jar jar apache2
ballerina-lang.int-0.0.0.jar jar apache2
ballerina-xslt-2.9.0.jar jar apache2
ballerina-lang.error-0.0.0.jar jar apache2
ballerina-lang.float-0.0.0.jar jar apache2
ballerina-lang.value-0.0.0.jar jar apache2
ballerina-data.csv-0.8.0.jar jar apache2
ballerina-persist-1.6.0.jar jar apache2
ballerina-math.vector-1.2.0.jar jar apache2
ballerina-lang.runtime-0.0.0.jar jar apache2
ballerina-websubhub-1.15.0.jar jar apache2
ballerina-data.xmldata-1.3.0.jar jar apache2
ballerina-protobuf.types.any-1.8.0.jar jar apache2
ballerina-protobuf-1.8.0.jar jar apache2
ballerina-protobuf.types.duration-1.8.0.jar jar apache2
ballerina-protobuf.types.timestamp-1.8.0.jar jar apache2
ballerina-protobuf.types.wrappers-1.8.0.jar jar apache2
ballerina-protobuf.types.empty-1.8.0.jar jar apache2
ballerina-protobuf.types.struct-1.8.0.jar jar apache2
ballerina-log-2.12.0.jar jar apache2
ballerina-jwt-2.15.0.jar jar apache2
ballerina-oauth2-2.14.0.jar jar apache2
ballerina-mime-2.12.0.jar jar apache2
ballerina-crypto-2.9.0.jar jar apache2
ballerina-test-0.0.0.jar jar apache2
ballerina-lang.table-0.0.0.jar jar apache2
ballerina-grpc-1.14.0.jar jar apache2
ballerina-grpc.types.wrappers-1.14.0.jar jar apache2
ballerina-grpc.types.any-1.14.0.jar jar apache2
ballerina-grpc.types.struct-1.14.0.jar jar apache2
ballerina-grpc.types.timestamp-1.14.0.jar jar apache2
resources.jar jar apache2
ballerina-grpc.types.duration-1.14.0.jar jar apache2
ballerina-mqtt-1.4.0.jar jar apache2
ballerina-websub-2.14.0.jar jar apache2
ballerina-constraint-1.7.0.jar jar apache2
ballerina-jballerina.java.arrays-1.6.0.jar jar apache2
ballerina-xmldata-2.9.0.jar jar apache2
ballerina-os-1.10.0.jar jar apache2
ballerina-ldap-1.3.0.jar jar apache2
ballerina-udp-1.13.0.jar jar apache2
ballerina-graphql.dataloader-1.16.0.jar jar apache2
ballerina-graphql.parser-1.16.0.jar jar apache2
ballerina-graphql.subgraph-1.16.0.jar jar apache2
ballerina-graphql-1.16.0.jar jar apache2
ballerina-lang.xml-0.0.0.jar jar apache2
ballerina-lang.map-0.0.0.jar jar apache2
ballerina-soap.wssec-2.3.0.jar jar apache2
ballerina-soap-2.3.0.jar jar apache2
ballerina-soap.soap11-2.3.0.jar jar apache2
ballerina-soap.soap12-2.3.0.jar jar apache2
ballerina-http.httpscerr-2.14.0.jar jar apache2
ballerina-http-2.14.0.jar jar apache2
ballerina-lang.annotations-0.0.0.jar jar apache2
ballerina-lang.regexp-0.0.0.jar jar apache2
ballerina-file-1.12.0.jar jar apache2
ballerina-yaml.lexer-0.8.0.jar jar apache2
ballerina-yaml.composer-0.8.0.jar jar apache2
ballerina-yaml.common-0.8.0.jar jar apache2
ballerina-yaml.serializer-0.8.0.jar jar apache2
ballerina-yaml.emitter-0.8.0.jar jar apache2
ballerina-yaml.schema-0.8.0.jar jar apache2
ballerina-yaml-0.8.0.jar jar apache2
ballerina-yaml.parser-0.8.0.jar jar apache2
observe-internal-native-1.5.0.jar jar apache2
transaction-native-1.12.0.jar jar apache2
data.yaml-compiler-plugin-0.8.0.jar jar apache2
data.yaml-native-0.8.0.jar jar apache2
constraint-native-1.7.0.jar jar apache2
time-native-2.7.0.jar jar apache2
ftp-compiler-plugin-2.13.0.jar jar apache2
commons-vfs2-2.8.0.jar bundle apache2
jsch-0.1.55.jar jar bsd3
ftp-native-2.13.0.jar jar apache2
commons-net-3.9.0.jar bundle apache2
io-compiler-plugin-1.8.0.jar jar apache2
io-native-1.8.0.jar jar apache2
jackson-core-2.18.0.jar bundle apache2
jackson-databind-2.18.0.jar bundle apache2
jackson-annotations-2.18.0.jar bundle apache2
avro-1.11.4.jar bundle apache2
avro-native-1.2.0.jar jar apache2
auth-native-2.14.0.jar jar apache2
tcp-compiler-plugin-1.13.0.jar jar apache2
tcp-native-1.13.0.jar jar apache2
netty-transport-native-unix-common-4.1.118.Final.jar bundle apache2
lz4-1.3.0.jar bundle apache2
jboss-marshalling-2.0.5.Final.jar jar apache2
netty-codec-4.1.118.Final.jar bundle apache2
protobuf-java-3.25.5.jar bundle upl1.0
netty-handler-4.1.118.Final.jar bundle apache2
netty-codec-socks-4.1.118.Final.jar bundle apache2
data.jsondata-compiler-plugin-1.1.0.jar jar apache2
json-path-2.9.0.jar bundle apache2
data.jsondata-native-1.1.0.jar jar apache2
json-smart-2.4.11.jar bundle apache2
accessors-smart-2.4.7.jar bundle apache2
websocket-compiler-plugin-2.14.0.jar jar apache2
http-native-2.14.0.jar jar apache2
netty-codec-http-4.1.118.Final.jar bundle apache2
mime-native-2.12.0.jar jar apache2
netty-handler-proxy-4.1.118.Final.jar bundle apache2
websocket-native-2.14.0.jar jar apache2
opentelemetry-sdk-trace-1.0.0.jar jar apache2
observe-native-1.5.0.jar jar apache2
opentelemetry-semconv-1.0.0-alpha.jar jar apache2
opentelemetry-sdk-testing-1.0.0.jar jar apache2
opentelemetry-sdk-common-1.0.0.jar jar apache2
email-compiler-plugin-2.12.0.jar jar apache2
mimepull-1.9.11.jar bundle edl1
activation-1.1.1.jar jar cddl1
greenmail-1.5.11.jar bundle apache2
email-native-2.12.0.jar jar apache2
quartz-2.3.2.jar bundle apache2
task-native-2.7.0.jar jar apache2
cloud-compiler-plugin-3.3.0.jar jar apache2
url-native-2.6.0.jar jar apache2
cache-compiler-plugin-3.10.0.jar jar apache2
cache-native-3.10.0.jar jar apache2
sql-compiler-plugin-1.16.0.jar jar apache2
sql-native-1.16.0.jar jar apache2
HikariCP-3.3.1.jar bundle apache2
jakarta.activation-1.2.2.jar bundle edl1
Saxon-HE-11.4.jar jar mpl10
xmlresolver-4.5.2.jar jar apache2
xslt-native-2.9.0.jar jar apache2
data.csv-compiler-plugin-0.8.0.jar jar apache2
data.csv-native-0.8.0.jar jar apache2
persist-compiler-plugin-1.6.0.jar jar apache2
persist-native-1.6.0.jar jar apache2
websubhub-compiler-plugin-1.15.0.jar jar apache2
websubhub-native-1.15.0.jar jar apache2
data.xmldata-compiler-plugin-1.3.0.jar jar apache2
data.xmldata-native-1.3.0.jar jar apache2
protobuf-native-1.8.0.jar jar apache2
log-compiler-plugin-2.12.0.jar jar apache2
log-native-2.12.0.jar jar apache2
jwt-native-2.15.0.jar jar apache2
oauth2-native-2.14.0.jar jar apache2
jakarta.activation-api-2.0.1.jar bundle edl1
bcpg-jdk18on-1.78.jar bundle apache2 + bouncy
bcutil-jdk18on-1.78.jar bundle bouncy
bcpkix-jdk18on-1.78.jar bundle bouncy
bcprov-jdk18on-1.78.jar bundle bouncy
crypto-native-2.9.0.jar jar apache2
testerina-compiler-plugin-0.1.0.jar jar apache2
grpc-compiler-plugin-1.14.0.jar jar apache2
netty-codec-http2-4.1.118.Final.jar bundle apache2
netty-tcnative-boringssl-static-2.0.70.Final-osx-aarch_64.jar bundle apache2
netty-tcnative-classes-2.0.70.Final.jar bundle apache2
grpc-native-1.14.0.jar jar apache2
commons-pool-1.5.6.wso2v1.jar bundle apache2
netty-tcnative-boringssl-static-2.0.70.Final-linux-aarch_64.jar bundle apache2
ballerina-parser-2201.12.0.jar jar apache2
netty-tcnative-boringssl-static-2.0.70.Final-osx-x86_64.jar bundle apache2
netty-tcnative-boringssl-static-2.0.70.Final-linux-x86_64.jar bundle apache2
proto-google-common-protos-1.17.0.jar jar apache2
netty-tcnative-boringssl-static-2.0.70.Final.jar bundle apache2
formatter-core-2201.12.0.jar jar apache2
netty-tcnative-boringssl-static-2.0.70.Final-windows-x86_64.jar bundle apache2
mqtt-compiler-plugin-1.4.0.jar jar epl2
org.eclipse.paho.mqttv5.client-1.2.5.jar bundle epl2
mqtt-native-1.4.0.jar jar apache2
websub-compiler-plugin-2.14.0.jar jar apache2
websub-native-2.14.0.jar jar apache2
constraint-compiler-plugin-1.7.0.jar jar apache2
xmldata-compiler-plugin-2.9.0.jar jar apache2
xmldata-native-2.9.0.jar jar apache2
os-compiler-plugin-1.10.0.jar jar apache2
os-test-utils-1.10.0.jar jar apache2
os-native-1.10.0.jar jar apache2
ldap-native-1.3.0.jar jar mit
unboundid-ldapsdk-7.0.0.jar bundle apache2 + gpl2 + lgpl2
udp-compiler-plugin-1.13.0.jar jar apache2
udp-native-1.13.0.jar jar apache2
graphql-commons-1.16.0.jar jar apache2
graphql-compiler-plugin-1.16.0.jar jar apache2
graphql-native-1.16.0.jar jar apache2
wss4j-ws-security-dom-3.0.1.jar bundle apache2
wss4j-ws-security-common-3.0.1.jar bundle apache2
xmlsec-3.0.3.jar bundle apache2
soap-native-2.3.0.jar jar mit
ballerina-to-openapi-2.3.0.jar jar apache2
http-compiler-plugin-2.14.0.jar jar apache2
file-compiler-plugin-1.12.0.jar jar apache2
file-native-1.12.0.jar jar apache2
The license types used by the above libraries and their information is given below:
apache2 Apache License Version 2.0
http://www.apache.org/licenses/LICENSE-2.0.html
mit MIT License
http://www.opensource.org/licenses/mit-license.php
epl2 Eclipse Public License Version 2.0
https://www.eclipse.org/legal/epl-2.0/
bsd3 Berkeley License - 3
http://opensource.org/licenses/BSD-3-Clause
cddl1 Common Development and Distribution License
http://www.opensource.org/licenses/cddl1.php
upl1.0 Universal Permissive License, Version 1.0
http://opensource.org/licenses/UPL
bsd Berkeley License
http://www.opensource.org/licenses/bsd-license.php
edl1 Eclipse Distribution License Version 1.0
http://www.eclipse.org/org/documents/edl-v10.php
mpl10 Mozilla Public License Version 1.0
http://www.mozilla.org/MPL/
bouncy Bouncy Castle License
http://www.bouncycastle.org/licence.html
lgpl2 Lesser General Public License Version 2.1
http://www.gnu.org/licenses/lgpl-2.1.php
gpl2 General Public License Version 2.0
https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
================================================
FILE: ballerina/README
================================================
Hello, I'm Ballerina.
=====================
Ballerina is a general purpose, concurrent, and strongly typed programming language with both textual
and graphical syntaxes. It is designed to make it easier to write programs that integrate with data sources,
services, and network-connected APIs of all kinds. It is optimized primarily for such programs - while it can
be use to program anything, it is not recommended to use Ballerina if a significant portion of the program is
not related to integrating with data sources, services, or network-connected APIs.
Ballerina has been inspired by Java, Go, and other languages, but it has a concurrency model built around a
sequence diagram metaphor.
Getting started
==================
You can download the Ballerina distribution, try samples, and read the documentation at http://ballerinalang.org.
Building from the source
==================================
If you want to build Ballerina from the source code:
1. Get a clone or download the source from this repository:
https://github.com/ballerina-lang/ballerina
2. Run the following Maven command from the root directory:
mvn clean install
3. Extract the Ballerina distribution created at `ballerina/distribution/zip/ballerina/target/ballerina--SNAPSHOT.zip`
to your local directory.
================================================
FILE: ballerina/build.gradle
================================================
/*
~ * 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.
~ */
import groovy.json.JsonSlurper
import org.apache.tools.ant.filters.ReplaceTokens
import org.apache.tools.ant.taskdefs.condition.Os
import static groovy.io.FileType.FILES
description = 'Ballerina - Tools'
ext {
jreLocation = "downloads/ballerina-jre-artifacts-zip/"
distributionName = "ballerina"
shortVersion = "${version}".replaceAll("-SNAPSHOT", "")
}
configurations {
jBallerinaDistribution
ballerinaDistribution
ballerinaLinuxDistribution
ballerinaLinuxArmDistribution
ballerinaMacDistribution
ballerinaMacArmDistribution
ballerinaWindowsDistribution
repoBuilder
}
dependencies {
repoBuilder project(':dist-repo-builder')
implementation project(':cache-generator')
}
def jBallerinaDistributionZip = file("$project.buildDir/distributions/ballerina-${shortVersion}.zip")
def ballerinaDistributionZip = file("$project.buildDir/distributions/ballerina-${ballerinaLangVersion}.zip")
def ballerinaLinuxDistributionZip = file("$project.buildDir/distributions/ballerina-linux-${ballerinaLangVersion}.zip")
def ballerinaLinuxArmDistributionZip = file("$project.buildDir/distributions/ballerina-linux-arm-${ballerinaLangVersion}.zip")
def ballerinaMacDistributionZip = file("$project.buildDir/distributions/ballerina-macos-${ballerinaLangVersion}.zip")
def ballerinaMacArmDistributionZip = file("$project.buildDir/distributions/ballerina-macos-arm-${ballerinaLangVersion}.zip")
def ballerinaWindowsDistributionZip = file("$project.buildDir/distributions/ballerina-windows-${ballerinaLangVersion}.zip")
task unpackBallerinaJre(type: Download) {
group = "unpack_dependencies"
def jreBaseURL = "https://github.com/ballerina-platform/ballerina-custom-jre/releases/download/${ballerinaJreVersion}"
src([
"${jreBaseURL}/ballerina-jre-linux-64-${ballerinaJreVersion}.zip",
"${jreBaseURL}/ballerina-jre-linux-arm-64-${ballerinaJreVersion}.zip",
"${jreBaseURL}/ballerina-jre-macos-64-${ballerinaJreVersion}.zip",
"${jreBaseURL}/ballerina-jre-macos-arm-64-${ballerinaJreVersion}.zip",
"${jreBaseURL}/ballerina-jre-win-64-${ballerinaJreVersion}.zip"
])
onlyIfModified true
dest "${jreLocation}"
}
task unpackJballerinaTools(type: Copy) {
group = "unpack_dependencies"
configurations.jbalTools.resolvedConfiguration.resolvedArtifacts.each { artifact ->
from zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
task unpackDevTools(type: Copy) {
group = "unpack_dependencies"
configurations.devTools.resolvedConfiguration.resolvedArtifacts.each { artifact ->
from zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
task downloadDocUi {
def response = new JsonSlurper().parseText(new URL(docUiApi).text)
def zipFileUrl = response.fileURL
def zipInputStream = new URL(zipFileUrl).openStream()
def outputFile = new File("${buildDir}/target/extracted-distributions/doc-ui-zip/ballerina-doc-ui.zip")
doLast {
outputFile.parentFile.mkdirs()
outputFile.withOutputStream { outputStream ->
outputStream << zipInputStream
}
}
}
task unpackAwsLambdaBala(type: Copy) {
group = "unpack_dependencies"
configurations.awsLambdaBala.resolvedConfiguration.resolvedArtifacts.each { artifact ->
from zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
task unpackStdLibs() {
doLast {
configurations.ballerinaStdLibs.resolvedConfiguration.resolvedArtifacts.each { artifact ->
copy {
from project.zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
}
}
task unpackBalTools() {
doLast {
configurations.ballerinaTools.resolvedConfiguration.resolvedArtifacts.each { artifact ->
copy {
from project.zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
}
}
task unpackC2cLibs() {
doLast {
configurations.ballerinaC2cLibs.resolvedConfiguration.resolvedArtifacts.each { artifact ->
copy {
from project.zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
}
}
task unpackC2cTooling() {
doLast {
configurations.ballerinaC2cTooling.resolvedConfiguration.resolvedArtifacts.each { artifact ->
copy {
from project.zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
}
}
task unpackOpenapiModule() {
doLast {
configurations.openapiModule.resolvedConfiguration.resolvedArtifacts.each { artifact ->
copy {
from project.zipTree(artifact.getFile())
into new File("${buildDir}/target/extracted-distributions", artifact.name + "-zip")
}
}
}
}
task downloadBalCommand(type: Download) {
group = "unpack_dependencies"
def commandBaseURL = "https://github.com/ballerina-platform/ballerina-update-tool/releases/download/v${ballerinaCommandVersion}"
src([
"${commandBaseURL}/ballerina-command-${ballerinaCommandVersion}.zip"
])
onlyIfModified true
dest "${buildDir}/target/"
}
task unpackBalCommand(type: Copy) {
group = "unpack_dependencies"
from zipTree { "${buildDir}/target/ballerina-command-${ballerinaCommandVersion}.zip" }
into new File("${buildDir}/target/")
}
task extractJreForLinux(type: Copy) {
group = "extract_jre"
from zipTree { "${jreLocation}/ballerina-jre-linux-64-${ballerinaJreVersion}.zip" }
into("${buildDir}/target/extracted-jre-linux")
}
task extractJreForLinuxArm(type: Copy) {
group = "extract_jre"
from zipTree { "${jreLocation}/ballerina-jre-linux-arm-64-${ballerinaJreVersion}.zip" }
into("${buildDir}/target/extracted-jre-linux-arm")
}
task extractJreForMac(type: Copy) {
group = "extract_jre"
from zipTree { "${jreLocation}/ballerina-jre-macos-64-${ballerinaJreVersion}.zip" }
into("${buildDir}/target/extracted-jre-macos")
}
task extractJreForMacArm(type: Copy) {
group = "extract_jre"
from zipTree { "${jreLocation}/ballerina-jre-macos-arm-64-${ballerinaJreVersion}.zip" }
into("${buildDir}/target/extracted-jre-macos-arm")
}
task extractJreForWindows(type: Copy) {
group = "extract_jre"
from zipTree { "${jreLocation}/ballerina-jre-win-64-${ballerinaJreVersion}.zip" }
into("${buildDir}/target/extracted-jre-windows")
}
task deleteTemporaryFiles(type: Delete) {
delete 'build/target'
}
task copyOtherRepos(type: Copy) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
def ballerinaDist = "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
into ballerinaDist
/* Standard Libraries */
configurations.ballerinaStdLibs.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
from("${artifactExtractedPath}/libs") {
into "bre/lib/"
}
from("${artifactExtractedPath}/bala") {
into "repo/bala"
}
}
/* Bal tools (e.g. openapi, graphql etc.) */
configurations.ballerinaTools.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
from("${artifactExtractedPath}/bala") {
into "repo/bala"
}
}
/* C2C Libraries */
configurations.ballerinaC2cLibs.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
from("${artifactExtractedPath}/libs") {
into "bre/lib/"
}
from("${artifactExtractedPath}/bala") {
into "repo/bala"
}
}
/* C2C Tooling */
configurations.ballerinaC2cTooling.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
from("${artifactExtractedPath}") {
into "lib/tools/lang-server/lib/"
}
}
/* OpenAPI Module */
configurations.openapiModule.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
from("${artifactExtractedPath}/bala") {
into "repo/bala"
}
from("${artifactExtractedPath}/cache") {
into "repo/cache"
}
}
/* Language Server Extension Artifacts */
configurations.devTools.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/${artifact.name}-zip"
from("${artifactExtractedPath}/ls-extensions/bre-libs") {
into "bre/lib/"
}
from("${artifactExtractedPath}/ls-extensions/ls-libs") {
into "lib/tools/lang-server/lib/"
}
}
}
task buildDistRepo(type: JavaExec) {
classpath = project.configurations.repoBuilder
main = 'io.ballerina.dist.DistRepoBuilder'
args "$buildDir/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
}
task copyDevToolsCoverageReport(type: Copy) {
from "$project.buildDir/target/extracted-distributions/ballerina-dev-tools-zip/tests/testerina-report-tools-${devToolsVersion}.zip"
into "$project.buildDir/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/lib/tools/coverage"
rename("testerina-report-tools-${devToolsVersion}.zip", "report.zip")
}
task copyDevToolsDocUi(type: Copy) {
from zipTree("$project.buildDir/target/extracted-distributions/doc-ui-zip/ballerina-doc-ui.zip")
into "$project.buildDir/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/lib/tools/doc-ui"
}
task filterApiDocs(type: Delete) {
delete "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/docs/ballerina/lang.__internal"
delete "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/docs/ballerina/lang.annotations"
}
task combineDocs(type: Exec) {
workingDir "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/docs"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine 'cmd', '/c', "$project.buildDir/target/extracted-distributions/jballerina-tools-zip/" +
"jballerina-tools-${ballerinaLangVersion}/bin/bal.bat", "doc", "--combine"
} else {
commandLine "$project.buildDir/target/extracted-distributions/jballerina-tools-zip/" +
"jballerina-tools-${ballerinaLangVersion}/bin/bal", "doc", "--combine"
}
}
task packageDist(type: Zip) {
group = "package_distribution"
description = 'Ballerina Tools Distribution Assembly'
ext {
baseName = "${distributionName}"
parentDir = "${baseName}-${shortVersion}"
}
archiveFileName = "${baseName}-${shortVersion}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "/LICENSE"
}
into("${parentDir}") {
from "LICENSE"
}
into("${parentDir}/examples") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Files */
into("${parentDir}/lib/") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: version])
}
/* Dependencies */
into("${parentDir}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina Tools Distribution Packaged'
}
outputs.file jBallerinaDistributionZip
}
task packageDistZip(type: Zip) {
group = "package_distribution"
description = 'Ballerina Distribution Assembly'
ext {
baseName = "${distributionName}-${version}"
parentDir = "${baseName}-${codeName}"
}
archiveFileName = "${distributionName}-${version}-${codeName}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}") {
from({ new File(temporaryDir, 'dependencies').mkdirs(); temporaryDir })
}
into("${parentDir}/distributions/ballerina-${shortVersion}/examples/") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/distributions/ballerina-${shortVersion}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Tools artifacts */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "/bin/version.txt"
exclude "/LICENSE"
}
into("${parentDir}") {
from "../resources/tools"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
/* Files */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "LICENSE"
}
into("${parentDir}/distributions/ballerina-${shortVersion}/bin") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/ballerina-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/installer-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [uuid: installerVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal"
fileMode = 0775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal.bat"
fileMode = 0775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/lib") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/lib/ballerina-command-${ballerinaCommandVersion}.jar"
fileMode = 0775
}
/* Dependencies */
into("${parentDir}/distributions/ballerina-${shortVersion}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina Distribution Packaged'
}
outputs.file ballerinaDistributionZip
}
task packageDistLinux(type: Zip) {
group = "package_distribution"
description = 'Ballerina Linux Distribution Assembly'
ext {
baseName = "${distributionName}-${version}"
parentDir = "${baseName}-${codeName}-linux"
}
archiveFileName = "${parentDir}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}/dependencies") {
from "build/target/extracted-jre-linux"
fileMode = 0755
}
into("${parentDir}/distributions/ballerina-${shortVersion}/examples/") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/distributions/ballerina-${shortVersion}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Tools artifacts */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "/bin/bal.bat"
exclude "/bin/version.txt"
exclude "/LICENSE"
}
into("${parentDir}") {
from "../resources/tools"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "**/scripts/**"
}
/* Files */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "LICENSE"
}
into("${parentDir}/distributions/ballerina-${shortVersion}/bin") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: version])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/ballerina-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/installer-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [uuid: installerVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal"
fileMode = 775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/lib") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/lib/ballerina-command-${ballerinaCommandVersion}.jar"
fileMode = 0775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/bal_completion.bash"
fileMode = 775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/_bal"
fileMode = 775
}
/* Dependencies */
into("${parentDir}/distributions/ballerina-${shortVersion}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina Linux Distribution Packaged'
}
outputs.file ballerinaLinuxDistributionZip
}
task packageDistLinuxArm(type: Zip) {
group = "package_distribution"
description = 'Ballerina Linux-ARM Distribution Assembly'
ext {
baseName = "${distributionName}-${version}"
parentDir = "${baseName}-${codeName}-linux-arm"
}
archiveFileName = "${parentDir}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}/dependencies") {
from "build/target/extracted-jre-linux-arm"
fileMode = 0755
}
into("${parentDir}/distributions/ballerina-${shortVersion}/examples/") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/distributions/ballerina-${shortVersion}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Tools artifacts */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "/bin/bal.bat"
exclude "/bin/version.txt"
exclude "/LICENSE"
}
into("${parentDir}") {
from "../resources/tools"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "**/scripts/**"
}
/* Files */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "LICENSE"
}
into("${parentDir}/distributions/ballerina-${shortVersion}/bin") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: version])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/ballerina-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/installer-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [uuid: installerVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal"
fileMode = 775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/lib") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/lib/ballerina-command-${ballerinaCommandVersion}.jar"
fileMode = 0775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/bal_completion.bash"
fileMode = 775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/_bal"
fileMode = 775
}
/* Dependencies */
into("${parentDir}/distributions/ballerina-${shortVersion}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina Linux-ARM Distribution Packaged'
}
outputs.file ballerinaLinuxArmDistributionZip
}
task packageDistMac(type: Zip) {
group = "package_distribution"
description = 'Ballerina MacOS Distribution Assembly'
ext {
baseName = "${distributionName}-${version}"
parentDir = "${baseName}-${codeName}-macos"
}
archiveFileName = "${parentDir}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}/dependencies") {
from "build/target/extracted-jre-macos"
fileMode = 0755
}
into("${parentDir}/distributions/ballerina-${shortVersion}/examples/") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/distributions/ballerina-${shortVersion}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Tools artifacts */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "/bin/bal.bat"
exclude "/bin/version.txt"
exclude "/LICENSE"
}
/* Files */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "LICENSE"
}
into("${parentDir}/distributions/ballerina-${shortVersion}/bin") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: version])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/ballerina-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/installer-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [uuid: installerVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal"
fileMode = 775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/lib") {
from "build/target/ballerina-command-${ballerinaCommandVersion}" +
"/lib/ballerina-command-${ballerinaCommandVersion}.jar"
fileMode = 0775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/bal_completion.bash"
fileMode = 775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/_bal"
fileMode = 775
}
/* Dependencies */
into("${parentDir}/distributions/ballerina-${shortVersion}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina MacOS Distribution Packaged'
}
outputs.file ballerinaMacDistributionZip
}
task packageDistMacArm(type: Zip) {
group = "package_distribution"
description = 'Ballerina MacOS-ARM Distribution Assembly'
ext {
baseName = "${distributionName}-${version}"
parentDir = "${baseName}-${codeName}-macos-arm"
}
archiveFileName = "${parentDir}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}/dependencies") {
from "build/target/extracted-jre-macos-arm"
fileMode = 0755
}
into("${parentDir}/distributions/ballerina-${shortVersion}/examples/") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/distributions/ballerina-${shortVersion}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Tools artifacts */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "/bin/bal.bat"
exclude "/bin/version.txt"
exclude "/LICENSE"
}
/* Files */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "LICENSE"
}
into("${parentDir}/distributions/ballerina-${shortVersion}/bin") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: version])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/ballerina-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/installer-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [uuid: installerVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal"
fileMode = 775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/lib") {
from "build/target/ballerina-command-${ballerinaCommandVersion}" +
"/lib/ballerina-command-${ballerinaCommandVersion}.jar"
fileMode = 0775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/bal_completion.bash"
fileMode = 775
}
into("${parentDir}/scripts") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/scripts/_bal"
fileMode = 775
}
/* Dependencies */
into("${parentDir}/distributions/ballerina-${shortVersion}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina MacOS-ARM Distribution Packaged'
}
outputs.file ballerinaMacArmDistributionZip
}
task packageDistWindows(type: Zip) {
group = "package_distribution"
description = 'Ballerina Windows Distribution Assembly'
ext {
baseName = "${distributionName}-${version}"
parentDir = "${baseName}-${codeName}-windows"
}
archiveFileName = "${parentDir}.zip"
entryCompression = ZipEntryCompression.DEFLATED
into("${parentDir}/dependencies") {
from "build/target/extracted-jre-windows"
fileMode = 0755
}
into("${parentDir}/distributions/ballerina-${shortVersion}/examples/") {
from "${project.rootDir}/examples/"
fileMode = 0755
}
// Code2Cloud Extension Examples
into("${parentDir}/distributions/ballerina-${shortVersion}/examples") {
from "build/target/extracted-distributions/c2c-examples-zip"
exclude "index.js"
}
/* Tools artifacts */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
exclude "distributions/ballerina-version"
exclude "distributions/installer-version"
exclude "/bin/bal"
exclude "/bin/version.txt"
exclude "/LICENSE"
}
/* Files */
into("${parentDir}/distributions/ballerina-${shortVersion}") {
from "LICENSE"
}
into("${parentDir}/distributions/ballerina-${shortVersion}/bin") {
from "lib/version.txt"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: version])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/ballerina-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [version: shortVersion])
}
into("${parentDir}/distributions") {
from "../resources/tools/distributions/installer-version"
fileMode = 0644
filter(ReplaceTokens, tokens: [uuid: installerVersion])
}
into("${parentDir}/bin") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/bin/bal.bat"
fileMode = 775
filter(ReplaceTokens, tokens: [ballerinaCommandVersion: ballerinaCommandVersion])
}
into("${parentDir}/lib") {
from "build/target/ballerina-command-${ballerinaCommandVersion}/lib/ballerina-command-${ballerinaCommandVersion}.jar"
fileMode = 0775
}
/* Dependencies */
into("${parentDir}/distributions/ballerina-${shortVersion}/bre/lib") {
from configurations.exten
}
doLast {
println 'Ballerina Windows Distribution Packaged'
}
outputs.file ballerinaWindowsDistributionZip
}
task unzipDistForTests(type: Copy) {
from zipTree("${project.rootDir}/ballerina/build/distributions/ballerina-${version}-${codeName}.zip")
into file("${project.rootDir}/ballerina/build/target/extracted-distributions/")
}
artifacts {
jBallerinaDistribution file: jBallerinaDistributionZip, builtBy: packageDist
ballerinaDistribution file: ballerinaDistributionZip, builtBy: packageDistZip
ballerinaLinuxDistribution file: ballerinaLinuxDistributionZip, builtBy: packageDistLinux
ballerinaLinuxArmDistribution file: ballerinaLinuxArmDistributionZip, builtBy: packageDistLinuxArm
ballerinaMacDistribution file: ballerinaMacDistributionZip, builtBy: packageDistMac
ballerinaMacArmDistribution file: ballerinaMacArmDistributionZip, builtBy: packageDistMacArm
ballerinaWindowsDistribution file: ballerinaWindowsDistributionZip, builtBy: packageDistWindows
}
task testExamples() {
def distPath = "${project.rootDir}/ballerina/build/target/extracted-distributions/ballerina-${version}-${codeName}"
def bbeList = []
PatternSet patternSet = new PatternSet()
patternSet.exclude("**/.ballerina/**")
patternSet.exclude("**/Ballerina.toml")
patternSet.exclude("**/Ballerina.lock")
patternSet.exclude("**/ballerina-internal.log")
inputs.files(files("${distPath}/examples").asFileTree.matching(patternSet))
doFirst {
copy {
into "$distPath/lib"
from configurations.externalTestJars
}
def src = "${project.rootDir}/examples/"
def dis = "${distPath}"
copy {
from(src)
into dis
}
def inputFile = new File("${distPath}/index.json")
def categories = new JsonSlurper().parseText(inputFile.text)
categories.each { category ->
def examples = category.samples
examples.each { example ->
def folder = new File("${distPath}/${example.url}")
if (folder.exists()) {
bbeList.push(example)
}
}
}
}
ext.prepareProject = { bbe ->
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
//TODO: Need to verify with windows
exec {
workingDir "${distPath}/${bbe}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat init test"
}
} else {
exec {
workingDir "${distPath}/${bbe}"
commandLine 'sh', '-c', "${distPath}/bin/bal init test"
}
}
if (bbe.startsWith('mysql')) {
def tomlFile = new File("${distPath}/${bbe}/Ballerina.toml")
tomlFile.append("\n\n[[platform.java11.dependency]]")
tomlFile.append("\npath = \"$distPath/lib/mysql-connector-java-8.0.21.jar\"\n")
}
if (bbe.startsWith('grpc')) {
new File("${distPath}/${bbe}").eachFileRecurse(FILES) {
if(it.name.endsWith('.proto')) {
def grpcCommand = "grpc --input ${it.name} --output ."
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
//TODO: Need to verify with windows
exec {
workingDir "${distPath}/${bbe}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat ${grpcCommand}"
}
} else {
exec {
workingDir "${distPath}/${bbe}"
commandLine 'sh', '-c', "${distPath}/bin/bal ${grpcCommand}"
}
}
}
}
}
}
ext.buildBBE = { bbe ->
def exitVal
def additionalParams = bbe.url == "natural-expressions" ? "--experimental" : ""
println "Building example '${bbe.url}'"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
//TODO: Need to verify with windows
exitVal = exec {
ignoreExitValue true
workingDir "${distPath}/${bbe.url}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat", 'build', "${additionalParams}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat", 'test', "${additionalParams}"
}
} else {
exitVal = exec {
ignoreExitValue true
workingDir "${distPath}/${bbe.url}"
commandLine 'sh', '-c', "${distPath}/bin/bal build ${additionalParams}"
commandLine 'sh', '-c', "${distPath}/bin/bal test ${additionalParams}"
}
}
if (exitVal.getExitValue() == 1) {
return true
}
return false
}
ext.outputVerification = { bbe ->
def directoryPath = "${distPath}/${bbe.url}"
File fileReference
def curlCommands = []
def ports = []
def fileOut = []
def balName = bbe.url.replace('-', '_') + ".bal"
FileTree tree = fileTree(directoryPath)
def curlCommandsFilePath = "${directoryPath}/curlcommandout.txt"
def portFilePath = "${directoryPath}/ports.txt"
def outputFilePath = "${directoryPath}/output.txt"
def additionalBuildParams = ""
tree.each { content ->
if (content.name.contains("client.out")) {
fileReference = file(content)
fileReference.readLines().each { lines ->
fileOut.add(lines.trim())
if (lines.startsWith("curl")) {
lines = lines.trim() + " >> output.txt"
curlCommands.add(lines)
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
curlCommands.add("echo. >> output.txt")
} else {
curlCommands.add("echo >> output.txt")
}
}
}
} else if (content.name.contains("server.out") || content.name.contains("service.out")) {
fileReference = file(content)
String portPrefixHttp = "[ballerina/http] started HTTP/WS listener 0.0.0.0:"
String portPrefixHttps = "[ballerina/http] started HTTPS/WSS listener 0.0.0.0:"
String portPrefixWs = "[ballerina/websocket] started WS listener 0.0.0.0:"
String portPrefixWss = "[ballerina/websocket] started WSS listener 0.0.0.0:"
fileReference.readLines().each { lines ->
if (lines.contains(portPrefixHttp)) {
lines = (lines - portPrefixHttp).trim()
ports.add(lines)
} else if (lines.contains(portPrefixHttps)) {
lines = (lines - portPrefixHttps).trim()
ports.add(lines)
} else if (lines.contains(portPrefixWs)) {
lines = (lines - portPrefixWs).trim()
ports.add(lines)
} else if (lines.contains(portPrefixWss)) {
lines = (lines - portPrefixWss).trim()
ports.add(lines)
}
}
}
}
def failFlag = false
if (!ports.isEmpty() && !curlCommands.isEmpty()) {
fileReference = new File(portFilePath)
ports.each {
fileReference << "${it}\n"
}
fileReference = new File(curlCommandsFilePath)
curlCommands.each {
fileReference << "${it}\n"
}
println "Verify output '${bbe.url}'"
def scriptPath = "${project.rootDir}/ballerina/lib"
def exitVal
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
exitVal = exec {
def windowsPath = scriptPath.replace('/', '\\')
workingDir "$windowsPath"
commandLine "$windowsPath\\run.bat", "${directoryPath}", "${distPath}/bin/bal.bat", "${balName}", "${curlCommandsFilePath}", "${portFilePath}", "${additionalBuildParams}"
ignoreExitValue true
}
} else {
exitVal = exec {
workingDir "$scriptPath"
commandLine "bash", "run.sh", "${directoryPath}", "${distPath}/bin/./bal", "${balName}", "${curlCommandsFilePath}", "${portFilePath}", "${additionalBuildParams}"
ignoreExitValue true
}
}
// Return immediately if command failed
if (exitVal.getExitValue() == 1) {
println "Failed due to build failure."
return true
}
File bbeOutput = new File(outputFilePath)
if (bbeOutput.exists()) {
bbeOutput.readLines().each {
if (!fileOut.contains(it.trim()) && it) {
println "Failed due to output did not match. Line ${it}"
failFlag = true
}
}
}
delete portFilePath
delete curlCommandsFilePath
delete outputFilePath
} else if (ports.isEmpty() && curlCommands.isEmpty()) {
balName = bbe.url.replace('-', '_')
def str = ""
fileOut = []
def bbeOutput = []
def cmdOutAry = []
def errOut = new ByteArrayOutputStream()
new ByteArrayOutputStream().withStream { os ->
println "Output Verification '${bbe.url}'"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
exec {
workingDir "${directoryPath}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat", 'run', "${balName}.bal ", ${additionalBuildParams}
ignoreExitValue true
standardOutput = os
errorOutput = errOut
}
} else {
exec {
workingDir "${directoryPath}"
commandLine 'sh', '-c', "${distPath}/bin/bal run ${balName}.bal ${additionalBuildParams}"
ignoreExitValue true
standardOutput = os
errorOutput = errOut
}
}
cmdOutAry.add(os.toString())
cmdOutAry.add(errOut.toString())
}
File file = file("${directoryPath}/${balName}.out")
file.readLines().each {
str = it.trim()
if (str?.trim()) {
if (str[0] != '#') {
fileOut.add(str)
}
}
}
def lines = "${cmdOutAry[0]}".split('\n')
lines = lines + "${cmdOutAry[1]}".split('\n')
lines.each {
str = it.trim()
if (str != "Compiling source" && str != "${balName}.bal" && str != "Running executable" && str != ""
&& str != "Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8" && !str.startsWith('HINT')) {
bbeOutput.add(str)
}
}
for (int i = 0; i < bbeOutput.size(); i++) {
def index = fileOut.findIndexOf { it == bbeOutput[i] }
if (index == -1) {
println "Failed due to output did not match. Line ${i}: ${bbeOutput[i]}"
failFlag = true
}
}
}
return failFlag
}
doLast {
def failedBuildExamples = []
def failOutputVerificationExamples = []
bbeList.each { bbe ->
if (bbe.verifyOutput) {
boolean outputVerificationFailFlag = outputVerification(bbe)
if (outputVerificationFailFlag) {
failOutputVerificationExamples.add(bbe.url)
}
}
if (bbe.verifyBuild) {
File tomlFile = new File("${distPath}/${bbe.url}/Ballerina.toml")
if (!tomlFile.exists()) {
prepareProject(bbe.url)
}
boolean buildFailFlag = buildBBE(bbe)
if (buildFailFlag) {
failedBuildExamples.add(bbe.url)
}
delete tomlFile
}
}
boolean buildFailure = false
if (!failedBuildExamples.isEmpty()) {
println "\n\tThe following BBEs failed while building:\n"
failedBuildExamples.each {
println "\t ${it}"
}
sleep(10 * 1000)
buildFailure = true
}
if (!failOutputVerificationExamples.isEmpty()) {
println "\n\tThe following BBEs failed while verifying the output:\n"
failOutputVerificationExamples.each {
println "\t ${it}"
}
sleep(10 * 1000)
buildFailure = true
}
if (buildFailure) {
sleep(10 * 1000)
throw new GradleException('There are test failures')
}
}
}
def addH2Dependency = { filePath ->
// Add the H2 database dependency to Ballerina.toml file
def tomlFile = new File(filePath)
tomlFile.append("\n\n[[platform.java21.dependency]]")
tomlFile.append("\nartifactId = \"h2\"")
tomlFile.append("\nversion = \"2.2.224\"")
tomlFile.append("\ngroupId = \"com.h2database\"")
}
def buildAndTestStandardLibs = { distPath, stdlibTest, isStageTest, testMinorVersionDifference ->
def exitVal
def additionalBuildParams = ""
if (stdlibTest == "config") {
additionalBuildParams = "--user.name=ballerina-user"
}
if (stdlibTest == "websub-generic" || stdlibTest == "websub-advance") {
additionalBuildParams = "--test.hub.url=https://localhost:23191/websub/hub"
}
println "Testing standard library ${stdlibTest}"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
if (!isStageTest) {
def tomlFile = new File("${distPath}/${stdlibTest}/Ballerina.toml")
if (!tomlFile.exists()) {
exec {
workingDir "${distPath}/${stdlibTest}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat init test"
}
}
if (stdlibTest == 'transaction') {
addH2Dependency("${distPath}/${stdlibTest}/Ballerina.toml")
}
} else {
exec {
workingDir "${distPath}/${stdlibTest}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat clean"
}
}
if (testMinorVersionDifference) {
exec {
workingDir "${distPath}/${stdlibTest}"
commandLine 'cmd', '/c', "rm Dependencies.toml"
}
}
exitVal = exec {
ignoreExitValue true
workingDir "${distPath}/${stdlibTest}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat test ${additionalBuildParams}"
}
} else {
if (!isStageTest) {
def tomlFile = new File("${distPath}/${stdlibTest}/Ballerina.toml")
if (!tomlFile.exists()) {
exec {
workingDir "${distPath}/${stdlibTest}"
commandLine 'sh', '-c', "${distPath}/bin/bal init test"
}
}
if (stdlibTest == 'transaction') {
addH2Dependency("${distPath}/${stdlibTest}/Ballerina.toml")
}
} else {
exec {
workingDir "${distPath}/${stdlibTest}"
commandLine 'sh', '-c', "${distPath}/bin/bal clean"
}
}
if (testMinorVersionDifference) {
exec {
workingDir "${distPath}/${stdlibTest}"
commandLine 'sh', '-c', "rm Dependencies.toml"
}
}
exitVal = exec {
environment "BALLERINA_STAGE_CENTRAL", "$isStageTest"
ignoreExitValue true
workingDir "${distPath}/${stdlibTest}"
commandLine 'sh', '-c', "${distPath}/bin/bal test ${additionalBuildParams}"
}
}
if (exitVal.getExitValue() == 1) {
return true
}
return false
}
task testStdlibs() {
def distPath = "${project.rootDir}/ballerina/build/target/extracted-distributions/ballerina-${version}-${codeName}"
def stdlibTestsList = []
PatternSet patternSet = new PatternSet()
patternSet.exclude("**/.ballerina/**")
patternSet.exclude("**/Ballerina.toml")
patternSet.exclude("**/Ballerina.lock")
patternSet.exclude("**/ballerina-internal.log")
inputs.files(files("${distPath}/stdlib-integration-tests").asFileTree.matching(patternSet))
doFirst {
def src = "${project.rootDir}/stdlib-integration-tests/"
def dis = "${distPath}"
copy {
from(src)
into dis
}
def inputFile = new File("${distPath}/index.json")
def stdlibTests = new JsonSlurper().parseText(inputFile.text)
stdlibTests.each { stdlibTest ->
def folder = new File("${distPath}/${stdlibTest.path}")
if (folder.exists()) {
stdlibTestsList.push(stdlibTest)
}
}
}
doLast {
def failedStdLibs = []
stdlibTestsList.each { stdlibTest ->
if (stdlibTest.enableTest) {
boolean stdLibTestFailFlag = buildAndTestStandardLibs(distPath, stdlibTest.path, false, false)
if (stdLibTestFailFlag) {
failedStdLibs.add(stdlibTest.path);
}
}
}
if (!failedStdLibs.isEmpty()) {
sleep(10 * 1000)
println "\n\tThe following Standard Library Tests Failed:\n"
failedStdLibs.each {
println "\t ${it}"
}
throw new GradleException('There are test failures')
}
}
}
// Task used to run automated testing before stdlib release. Not executed during normal build
task testStdlibsWithStaging() {
def distPath = "${project.rootDir}/ballerina/build/target/extracted-distributions/ballerina-${version}-${codeName}"
def stdlibTestsList = []
def testMinorVersionDifference = project.hasProperty('testMinorVersionDifference') ? true : false
doFirst {
def indexJsonFile = new File("${distPath}/index.json")
def stdlibTests = new JsonSlurper().parseText(indexJsonFile.text)
stdlibTests.each { stdlibTest ->
def folder = new File("${distPath}/${stdlibTest.path}")
if (folder.exists()) {
stdlibTestsList.push(stdlibTest)
}
}
}
doLast {
def failedStdLibs = []
stdlibTestsList.each { stdlibTest ->
if (stdlibTest.enableTest) {
boolean stdLibTestFailFlag = buildAndTestStandardLibs(distPath, stdlibTest.path, true, testMinorVersionDifference)
if (stdLibTestFailFlag) {
failedStdLibs.add(stdlibTest.path);
}
}
}
if (!failedStdLibs.isEmpty()) {
sleep(10 * 1000)
println "\n\tThe following Standard Library Tests Failed with Staging Central:\n"
failedStdLibs.each {
println "\t ${it}"
}
throw new GradleException('There are test failures')
}
}
}
task testDevToolsIntegration() {
def distPath = "${project.rootDir}/ballerina/build/target/extracted-distributions/ballerina-${version}-${codeName}"
def devtoolsProjectList = []
PatternSet patternSet = new PatternSet()
patternSet.exclude("**/.ballerina/**")
patternSet.exclude("**/Ballerina.toml")
patternSet.exclude("**/Ballerina.lock")
patternSet.exclude("**/ballerina-internal.log")
inputs.files(files("${distPath}/devtools-integration-tests").asFileTree.matching(patternSet))
doFirst {
// Copy projects to distPath
def devToolsProjectSrc = "${project.rootDir}/devtools-integration-tests/"
copy {
from(devToolsProjectSrc)
into distPath
}
// Read the projects from the index.json
def inputFile = new File("${distPath}/index.json")
def devtoolsProjects = new JsonSlurper().parseText(inputFile.text)
// For each project in the devtools integration tests folder, we add it to the project list
devtoolsProjects.each { devtoolsProject ->
def folder = new File("${distPath}/${devtoolsProject.path}")
if (folder.exists()) {
devtoolsProjectList.push("$devtoolsProject.path")
}
}
}
doLast {
// For each devtools project in the list
devtoolsProjectList.each { String devtoolsProject ->
// Read the modules from the index.json
def inputFile = new File("${distPath}/index.json")
def projectModules = new JsonSlurper().parseText(inputFile.text)
// For each module we execute the `test -c ${projectModule} command
projectModules.each { projectModuleVal ->
def projectModule = projectModuleVal.path
def additionalBuildParams = ""
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
exec {
workingDir "${buildDir}/${devtoolsProject}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat init test"
}
exec {
workingDir "${buildDir}/${devtoolsProject}"
commandLine 'cmd', '/c',
"${distPath}/bin/bal.bat test ${additionalBuildParams}"
}
} else {
exec {
workingDir "${distPath}/${devtoolsProject}"
commandLine 'sh', '-c',
"${distPath}/bin/bal init test"
}
exec {
workingDir "${distPath}/${devtoolsProject}"
commandLine 'sh', '-c',
"${distPath}/bin/bal test ${additionalBuildParams}"
}
}
}
}
}
}
task generateCache(dependsOn: ':cache-generator:build') {
doFirst {
copy {
from "${project.rootDir}/cache-generator/build/libs/cache-generator-${project.version}.jar"
into "$buildDir/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/bre/lib"
}
}
doLast {
def distPath = "${project.rootDir}/ballerina/build/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
/* Standard Libraries */
def stdlibJson = file("${project.rootDir}/ballerina/build/target/extensions.json")
def parsedJson = new JsonSlurper().parseText(stdlibJson.text)
def maxLevel = 0
parsedJson.standard_library.each { module ->
def moduleLevel = module.level
if (moduleLevel > maxLevel) {
maxLevel = moduleLevel
}
}
def sortedLibs = []
def dependencyMap = [:]
for (currentLevel in 1..maxLevel) {
parsedJson.standard_library.each { module ->
if (module.level == currentLevel) {
def libName = module.name.toString().split("-")[-1] + "-ballerina-zip"
sortedLibs.add(libName)
}
}
}
def configStdlibs = []
configurations.ballerinaStdLibs.resolvedConfiguration.resolvedArtifacts.each { artifact ->
configStdlibs.add(artifact.name + "-zip")
}
sortedLibs.each { lib ->
if (configStdlibs.contains(lib)) {
def versionList = [file(file("${buildDir}/target/extracted-distributions/" + lib +
"/bala").listFiles()[0].toString() + "/" + lib.split("-")[0]).listFiles()[0].name]
dependencyMap["${lib}"] = versionList
}
}
sortedLibs.each { lib ->
if (configStdlibs.contains(lib)) {
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + lib
String balaDir = artifactExtractedPath + "/bala"
new File("${balaDir}").listFiles().each { orgDirFile ->
String orgDir = orgDirFile.toString()
String moduleDir = new File("${orgDir}").listFiles()[0].toString()
String versionDir = new File("${moduleDir}").listFiles()[0].toString()
String balaPath = new File("${versionDir}").listFiles()[0].toString()
String jbalPath = "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
exec {
workingDir "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat gencache ${balaPath} ${jbalPath}"
ignoreExitValue true
}
} else {
exec {
workingDir "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
commandLine 'sh', '-c', "${distPath}/bin/bal gencache ${balaPath} ${jbalPath}"
ignoreExitValue true
}
}
def dependencyGraphJson = file("${balaPath}/dependency-graph.json")
def parsedDependencyJson = new JsonSlurper().parseText(dependencyGraphJson.text)
parsedDependencyJson.modules.dependencies.each { dependency ->
if ( dependency.module_name.size() > 0 ) {
for ( i in 0..dependency.module_name.size()) {
def moduleName = dependency.module_name[i].toString() + "-ballerina-zip"
if ( sortedLibs.contains(moduleName) ) {
if ( !dependencyMap["${moduleName}"].contains(dependency.version[i].toString()))
dependencyMap["${moduleName}"].add(dependency.version[i].toString())
}
}
}
}
}
}
}
/* C2C Libraries */
configurations.ballerinaC2cLibs.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
String balaDir = artifactExtractedPath + "/bala"
String orgDir = new File("${balaDir}").listFiles()[0].toString()
String moduleDir = new File("${orgDir}").listFiles()[0].toString()
String versionDir = new File("${moduleDir}").listFiles()[0].toString()
String balaPath = new File("${versionDir}").listFiles()[0].toString()
String jbalPath = "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
exec {
workingDir "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat gencache ${balaPath} ${jbalPath}"
ignoreExitValue true
}
}
else {
exec {
workingDir "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
commandLine 'sh', '-c', "${distPath}/bin/bal gencache ${balaPath} ${jbalPath}"
ignoreExitValue true
}
}
}
/* OpenAPI Module */
configurations.openapiModule.resolvedConfiguration.resolvedArtifacts.each { artifact ->
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + artifact.name + "-zip"
String balaDir = artifactExtractedPath + "/bala"
String orgDir = new File("${balaDir}").listFiles()[0].toString()
String moduleDir = new File("${orgDir}").listFiles()[0].toString()
String versionDir = new File("${moduleDir}").listFiles()[0].toString()
String balaPath = new File("${versionDir}").listFiles()[0].toString()
String jbalPath = "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
exec {
workingDir "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
commandLine 'cmd', '/c', "${distPath}/bin/bal.bat gencache ${balaPath} ${jbalPath}"
ignoreExitValue true
}
}
else {
exec {
workingDir "${buildDir}/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}"
commandLine 'sh', '-c', "${distPath}/bin/bal gencache ${balaPath} ${jbalPath}"
ignoreExitValue true
}
}
}
delete "$buildDir/target/extracted-distributions/jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/bre/lib/cache-generator-${project.version}.jar"
delete "${project.rootDir}/cache-generator/build/libs/cache-generator-${project.version}.jar"
delete "${project.rootDir}/module_list.json"
}
}
task generateBalToolsToml () {
doFirst {
download.run {
src "https://raw.githubusercontent.com/ballerina-platform/ballerina-release/${balstdlibBranch}/dependabot/resources/extensions.json"
dest "${project.rootDir}/ballerina/build/target/"
}
}
doLast {
def configBalTools = []
configurations.ballerinaTools.resolvedConfiguration.resolvedArtifacts.each { artifact ->
configBalTools.add(artifact.name + "-zip")
}
def stdlibJson = file("${project.rootDir}/ballerina/build/target/extensions.json")
def parsedJson = new JsonSlurper().parseText(stdlibJson.text)
def maxLevel = 0
parsedJson.standard_library.each { module ->
def moduleLevel = module.level
if (moduleLevel > maxLevel) {
maxLevel = moduleLevel
}
}
def sortedLibs = []
for (currentLevel in 1..maxLevel) {
parsedJson.standard_library.each { module ->
if (module.level == currentLevel) {
def libName = module.name.toString() + "-zip"
sortedLibs.add(libName)
}
}
}
def balToolsTomlString = ""
String balToolsTomlPath = "${buildDir}/target/extracted-distributions/" +
"jballerina-tools-zip/jballerina-tools-${ballerinaLangVersion}/resources/bal-tools.toml"
def file = new File(balToolsTomlPath)
configBalTools.each { lib ->
if (sortedLibs.contains(lib)) {
def artifactExtractedPath = "${buildDir}/target/extracted-distributions/" + lib
String balaDir = artifactExtractedPath + "/bala"
new File("${balaDir}").listFiles().each { orgDirFile ->
String orgDir = orgDirFile.toString()
String moduleDir = new File("${orgDir}").listFiles()[0].toString()
String versionDir = new File("${moduleDir}").listFiles()[0].toString()
String balaPath = new File("${versionDir}").listFiles()[0].toString()
def toolJson = new File("${balaPath}/tool/bal-tool.json")
def parsedToolJson = new JsonSlurper().parseText(toolJson.text)
def packageJson = new File("${balaPath}/package.json")
def parsedPackageJson = new JsonSlurper().parseText(packageJson.text)
balToolsTomlString += "[[tool]]\n" +
"id = \"" + parsedToolJson.tool_id.toString() + "\"\n" +
"org = \"" + parsedPackageJson.organization.toString() + "\"\n" +
"name = \"" + parsedPackageJson.name.toString() + "\"\n" +
"version = \"" + parsedPackageJson.version.toString() + "\"\n" +
"active = true\n\n"
}
}
}
file.write(balToolsTomlString)
}
}
/* Unpack Dependencies */
unpackJballerinaTools.dependsOn unpackBallerinaJre
unpackDevTools.dependsOn unpackJballerinaTools
unpackStdLibs.dependsOn unpackDevTools
unpackBalTools.dependsOn unpackStdLibs
generateBalToolsToml.dependsOn unpackBalTools // Generate bal-tools.toml
unpackC2cLibs.dependsOn generateBalToolsToml
unpackC2cTooling.dependsOn unpackC2cLibs
unpackOpenapiModule.dependsOn unpackC2cTooling
downloadBalCommand.dependsOn unpackOpenapiModule
/* Cache Generation */
generateCache.dependsOn unpackStdLibs
generateCache.dependsOn unpackC2cTooling
generateCache.dependsOn unpackOpenapiModule
generateCache.dependsOn copyDevToolsDocUi
generateCache.dependsOn generateBalToolsToml
/* Extract JRE */
extractJreForLinux.dependsOn unpackBalCommand
extractJreForMac.dependsOn extractJreForLinux
extractJreForLinuxArm.dependsOn extractJreForMac
extractJreForMacArm.dependsOn extractJreForLinuxArm
extractJreForWindows.dependsOn extractJreForMacArm
copyOtherRepos.dependsOn extractJreForWindows
copyDevToolsCoverageReport.dependsOn copyOtherRepos
copyDevToolsDocUi.dependsOn copyDevToolsCoverageReport
copyDevToolsDocUi.dependsOn downloadDocUi
buildDistRepo.dependsOn copyDevToolsDocUi
buildDistRepo.dependsOn generateCache
filterApiDocs.dependsOn buildDistRepo
combineDocs.dependsOn filterApiDocs
/* Package Distributions */
packageDist.dependsOn buildDistRepo
packageDist.dependsOn combineDocs
packageDist.dependsOn generateCache
packageDistZip.dependsOn packageDist
packageDistLinux.dependsOn packageDistZip
packageDistLinux.dependsOn unzipDistForTests
packageDistLinuxArm.dependsOn packageDistLinux
packageDistMac.dependsOn packageDistLinuxArm
packageDistMacArm.dependsOn packageDistMac
packageDistWindows.dependsOn packageDistMacArm
unzipDistForTests.dependsOn packageDistZip
unpackStdLibs.dependsOn unpackBallerinaJre
unpackStdLibs.dependsOn unpackJballerinaTools
unpackBalCommand.dependsOn downloadBalCommand
testExamples.dependsOn unzipDistForTests
testStdlibs.dependsOn unzipDistForTests
testDevToolsIntegration.dependsOn unzipDistForTests
/* Delete Temporary Files */
deleteTemporaryFiles.dependsOn packageDistWindows
deleteTemporaryFiles.dependsOn testExamples
deleteTemporaryFiles.dependsOn testStdlibs
deleteTemporaryFiles.dependsOn testDevToolsIntegration
test.dependsOn deleteTemporaryFiles
test.dependsOn testExamples
test.dependsOn testStdlibs
================================================
FILE: ballerina/lib/run.bat
================================================
@echo off
REM Change directory to provided path
cd %1
REM run ballerina file as background process
START "" /b %2 run %3
REM wait till server starts
ping -n 10 127.0.0.0 > nul
REM invoke curl commnads
cmd<%4
REM kill the process which is using the server ports
for /f "tokens=*" %%b in (%5) do (
IF "%%b."=="." (echo "empty") ELSE (
netstat /o /a | find /i "listening" | find ":%%b" > nul 2>nul && (
for /f "tokens=5" %%a in ('netstat -aon ^| find ":%%b" ^| find "LISTENING"') do taskkill /f /pid %%a
) || (
echo %%b is not open
)
)
)
================================================
FILE: ballerina/lib/run.sh
================================================
#!/bin/bash
kill_service() {
case "$(uname -s)" in
Darwin)
kill $(lsof -ti:$1)
;;
Linux)
fuser -k $1/tcp
;;
*) ;;
esac
}
# Change directory to provided path
cd ${1}
# run ballerina file as background process
my_array=($(${2} run ${3} ${6})) &
sleep 10
# invoke curl commnads
. ${4}
# kill the process which is using the server ports
while read -r line; do
if lsof -Pi :$line -sTCP:LISTEN -t >/dev/null; then
kill_service $line
else
echo "Service is not running"
fi
done <${5}
================================================
FILE: ballerina/lib/version.txt
================================================
Ballerina Platform version: @version@
================================================
FILE: ballerina-test/build.gradle
================================================
/*
~ * 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.
~ */
description = 'Ballerina - Tools Test'
configurations {
jBallerinaDistribution
ballerinaDistribution
ballerinaLinuxDistribution
ballerinaMacDistribution
ballerinaWindowsDistribution
}
dependencies {
testImplementation "org.testng:testng:${testngVersion}"
testImplementation "net.lingala.zip4j:zip4j:${netLingalaZip4jVersion}"
testImplementation "commons-io:commons-io:${commonsIoVersion}"
testImplementation "org.apache.commons:commons-lang3:${commonsLang3Version}"
jBallerinaDistribution project(path: ":ballerina", configuration: "jBallerinaDistribution")
ballerinaDistribution project(path: ":ballerina", configuration: "ballerinaDistribution")
ballerinaLinuxDistribution project(path: ":ballerina", configuration: "ballerinaLinuxDistribution")
ballerinaMacDistribution project(path: ":ballerina", configuration: "ballerinaMacDistribution")
ballerinaWindowsDistribution project(path: ":ballerina", configuration: "ballerinaWindowsDistribution")
}
task distributionTest {
dependsOn configurations.jBallerinaDistribution
dependsOn configurations.ballerinaDistribution
dependsOn configurations.ballerinaLinuxDistribution
dependsOn configurations.ballerinaLinuxDistribution
dependsOn configurations.ballerinaWindowsDistribution
test {
systemProperty "distributions.dir", "$buildDir/../../ballerina/build/distributions"
systemProperty "target.dir", "$buildDir"
systemProperty "maven.version", "$version"
systemProperty "examples.dir", "$buildDir/../../examples"
systemProperty "line.check.extensions", ".java, .bal"
systemProperty "short.version", "$version".split("-")[0]
systemProperty "version.display.text", "$version".split("-")[0]
systemProperty "spec.version", "$specVersion"
systemProperty "tool.version", "$ballerinaCommandVersion"
systemProperty "code.name", "$project.codeName"
systemProperty "openapi.version", "$openapiModuleVersion"
useTestNG() {
suites 'src/test/resources/testng.xml'
suites 'src/test/resources/testing-line-length.xml'
}
testLogging {
showStandardStreams = true
}
}
}
test.dependsOn distributionTest
================================================
FILE: ballerina-test/gradle.properties
================================================
filePath=/../examples
debug=true
fileExtensions=.java, .bal
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/lengthValidation/LengthValidator.java
================================================
/*
~ * 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.
~ */
package org.ballerinalang.distribution.lengthValidation;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.NoSuchElementException;
import org.testng.annotations.Test;
import org.testng.Assert;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import static org.ballerinalang.distribution.utils.TestUtils.EXAMPLES_DIR;
import static org.ballerinalang.distribution.utils.TestUtils.EXTENSTIONS_TO_BE_FILTERED_FOR_LINE_CHECKS;
import static org.ballerinalang.distribution.utils.TestUtils.OUT;
/**
Used to validate the filtered
file inside a example directory
*/
public class LengthValidator {
private static int LINE_MAX_LIMIT = 120;
private static String[] fileFilterExtensions;
private static String defaultFilerExtension = ".bal";
private static boolean isValidationFailure = false;
private static Pattern descriptionLine = Pattern.compile("^\\s*//");
public static void logger(String message) {
OUT.println(message);
}
/**
Check the length count of each lines in a file
*/
public static void validateLineLength(String path, String fileName) throws LineLengthExceededException, NoSuchElementException {
BufferedReader reader;
String relativePath = String.format("/examples/%s", EXAMPLES_DIR.relativize(Paths.get(path)).toString());
try {
reader = new BufferedReader(new FileReader(path));
String line;
int lineCount = 1;
do {
line = reader.readLine();
// Ignore comments line length
if (line != null && !descriptionLine.matcher(line).find()) {
int count = line.length();
if(count > LINE_MAX_LIMIT){
logger(String.format("[%s] Line number %d.", relativePath, lineCount));
isValidationFailure = true;
}
}
lineCount += 1;
} while (line != null);
reader.close();
} catch (IOException e) {
logger(String.format("[%s] An IOException occurred for file name : %s", fileName, path));
throw new NoSuchElementException("An error occurred during IO operation");
} catch(NoSuchElementException e) {
logger(String.format("Invalid file name given : %s", relativePath));
throw new NoSuchElementException("Invalid file name");
}
}
/**
Check the extension of the files
*/
public static void validateFileExtension(File fileEntry, String[] extensions) throws LineLengthExceededException {
for(String extension: extensions) {
if(fileEntry.getName().endsWith(extension)) {
LengthValidator.validateLineLength(fileEntry.getPath(), fileEntry.getName());
}
}
}
/**
Check the files in folder
*/
public static void listFilesForFolder(final File folder) throws LineLengthExceededException {
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory() && !fileEntry.getName().endsWith("tests")) {
listFilesForFolder(fileEntry);
} else {
validateFileExtension(fileEntry, fileFilterExtensions);
}
}
}
@Test
public void validateLength() throws LineLengthExceededException, IOException, InterruptedException {
String filePath = EXAMPLES_DIR.toString();
if (!EXTENSTIONS_TO_BE_FILTERED_FOR_LINE_CHECKS.isEmpty()) {
String[] splittedArg = EXTENSTIONS_TO_BE_FILTERED_FOR_LINE_CHECKS.split(",");
fileFilterExtensions = new String[splittedArg.length];
int counter = 0;
for (String extension : splittedArg) {
fileFilterExtensions[counter] = extension.trim();
counter += 1;
}
} else {
fileFilterExtensions = new String[] { defaultFilerExtension };
}
LengthValidator.listFilesForFolder(new File(filePath));
Assert.assertFalse(isValidationFailure, "bal files should only contain lines of 120 character length");
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/lengthValidation/LineLengthExceededException.java
================================================
/*
~ * 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.
~ */
package org.ballerinalang.distribution.lengthValidation;
public class LineLengthExceededException extends Exception{
LineLengthExceededException(String message) {
super(message);
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/ArtifactBuildTest.java
================================================
/*
* 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.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import java.io.IOException;
import static org.ballerinalang.distribution.utils.TestUtils.DISTRIBUTIONS_DIR;
import static org.ballerinalang.distribution.utils.TestUtils.MAVEN_VERSION;
/**
* Tests related to artifact generation.
*/
public class ArtifactBuildTest {
@DataProvider(name = "distribution-provider")
public Object[][] distributionNameProvider() {
return new Object[][]{
{"ballerina-" + MAVEN_VERSION},
{"ballerina-linux-" + MAVEN_VERSION},
{"ballerina-macos-" + MAVEN_VERSION}
};
}
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
for (Object[] dist : distributionNameProvider()) {
String distName = (String) dist[0];
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(distName + ".zip"));
}
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/BallerinaCommandTest.java
================================================
/*
* 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.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Test ballerina commands.
*/
public class BallerinaCommandTest {
private static final String DIST_NAME = "ballerina-" + TestUtils.MAVEN_VERSION;
public static final String VERSION = System.getProperty("maven.version");
public static final String SHORT_VERSION = System.getProperty("short.version");
private static final String SPEC_VERSION = System.getProperty("spec.version");
public static final String VERSION_DISPLAY_TEXT = System.getProperty("version.display.text");
private static final String TOOL_VERSION = System.getProperty("tool.version");
private static final String path = TestUtils.TEST_DISTRIBUTION_PATH.resolve(DIST_NAME).resolve("bin").resolve("bal").toString();
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
TestUtils.prepareDistribution(TestUtils.DISTRIBUTIONS_DIR.resolve(DIST_NAME + ".zip"));
}
@Test(description = "Execute smoke testing to verify installation.")
public void testVersionCommand() throws IOException {
TestUtils.testInstallation(path, VERSION, SPEC_VERSION, TOOL_VERSION, VERSION_DISPLAY_TEXT);
}
@Test(description = "Execute smoke testing to verify build command.", dependsOnMethods = {"testVersionCommand"})
public void testBuildCommand() throws IOException {
String projectName = "project1";
String moduleName = "module1";
TestUtils.executeCommand(path + " new " + projectName);
TestUtils.executeCommand("cd " + projectName + " && " + path + " add " + moduleName);
String actualOutput = TestUtils.executeCommand("cd " + projectName + " && " + path + " build");
Assert.assertTrue(actualOutput.contains("Compiling source"));
Assert.assertTrue(actualOutput.contains("Generating executable"));
}
@Test(description = "Execute smoke testing to verify dist commands.", dependsOnMethods = {"testVersionCommand"})
public void testDistCommands() throws IOException {
Path ballerinaHome = Paths.get(TestUtils.getUserHome()).resolve(".ballerina").resolve("ballerina-version");
// test bal dist list
String actualOutput = TestUtils.executeCommand(path + " dist list");
Assert.assertTrue(actualOutput.contains("Distributions available locally:"));
Assert.assertTrue(actualOutput.contains("Distributions available remotely:"));
Assert.assertTrue(actualOutput.contains("1.* channel"));
Assert.assertTrue(actualOutput.contains("1.0.0"));
Assert.assertTrue(actualOutput.contains("1.1.0"));
Assert.assertTrue(actualOutput.contains("1.2.0"));
Assert.assertTrue(actualOutput.contains("Swan Lake channel"));
Assert.assertTrue(actualOutput.contains("slp1"));
Assert.assertTrue(actualOutput.contains("slp8"));
// test bal dist pull and fetching dependencies
actualOutput = TestUtils.executeCommand(path + " dist pull 1.2.3");
Assert.assertTrue(actualOutput.contains("Fetching the '1.2.3' distribution from the remote server..."));
Assert.assertTrue(actualOutput.contains("Fetching the dependencies for '1.2.3' from the remote server..."));
Assert.assertTrue(actualOutput.contains("Downloading jdk8u202-b08-jre"));
Assert.assertTrue(actualOutput.contains("'1.2.3' successfully set as the active distribution"));
TestUtils.testInstallation(path, "1.2.3", "2020R1", TOOL_VERSION, "1.2.3");
// test bal dist update
actualOutput = TestUtils.executeCommand(path + " dist update");
Assert.assertTrue(actualOutput.contains("Fetching the latest patch distribution for 'jballerina-1.2.3' from the remote server..."));
Assert.assertTrue(actualOutput.contains("Successfully set the latest patch distribution"));
Assert.assertEquals(TestUtils.getContent(ballerinaHome).split("-")[1].trim(),
actualOutput.split("Downloading ")[1].split(" ")[0]);
actualOutput = TestUtils.executeCommand(path + " dist update");
Assert.assertTrue(actualOutput.contains("is already the active distribution"));
// test bal dist use
actualOutput = TestUtils.executeCommand(path + " dist use 1.2.3");
Assert.assertTrue(actualOutput.contains("'1.2.3' successfully set as the active distribution"));
TestUtils.testInstallation(path, "1.2.3", "2020R1", TOOL_VERSION, "1.2.3");
actualOutput = TestUtils.executeCommand(path + " dist pull slp7");
Assert.assertTrue(actualOutput.contains("Fetching the 'slp7' distribution from the remote server..."));
Assert.assertTrue(actualOutput.contains("Fetching the dependencies for 'slp7' from the remote server..."));
Assert.assertTrue(actualOutput.contains("Downloading jdk-21.0.5+11-jre"));
Assert.assertTrue(actualOutput.contains("'slp7' successfully set as the active distribution"));
TestUtils.testInstallation(path, "swan-lake-preview7", "v2020-09-22", TOOL_VERSION, "Preview 7");
// test bal dist remove
actualOutput = TestUtils.executeCommand(path + " dist remove slp7");
Assert.assertTrue(actualOutput.contains("The active Ballerina distribution cannot be removed"));
actualOutput = TestUtils.executeCommand(path + " dist remove " + SHORT_VERSION);
Assert.assertTrue(actualOutput.contains("Distribution '" + SHORT_VERSION + "' successfully removed"));
actualOutput = TestUtils.executeCommand(path + " dist use " + SHORT_VERSION);
Assert.assertTrue(actualOutput.contains("Distribution '" + SHORT_VERSION + "' not found"));
actualOutput = TestUtils.executeCommand(path + " dist update");
Assert.assertTrue(actualOutput.contains("Fetching the latest patch distribution for 'ballerina-slp7' from the remote server..."));
Assert.assertTrue(actualOutput.contains("Successfully set the latest patch distribution"));
Assert.assertEquals(actualOutput.split("Downloading ")[1].split(" ")[0],
actualOutput.split("Downloading ")[1].split(" ")[0]);
// test bal dist remove -a
actualOutput = TestUtils.executeCommand(path + " dist remove -a");
Assert.assertTrue(actualOutput.contains("All non-active distributions are successfully removed"));
actualOutput = TestUtils.executeCommand(path + " dist use 1.2.3");
Assert.assertTrue(actualOutput.contains("Distribution '1.2.3' not found"));
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/DistributionArtifactCheckTest.java
================================================
/*
* 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.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.ballerinalang.distribution.utils.TestUtils.DISTRIBUTIONS_DIR;
import static org.ballerinalang.distribution.utils.TestUtils.SHORT_VERSION;
import static org.ballerinalang.distribution.utils.TestUtils.TEST_DISTRIBUTION_PATH;
/**
* Check if necessary files exists to build in the distribution.
*/
public class DistributionArtifactCheckTest {
private static final String DIST_NAME = "ballerina-" + SHORT_VERSION;
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DIST_NAME + ".zip"));
}
@Test()
public void c2cExistsTest() {
Path cachePath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("repo")
.resolve("cache")
.resolve("ballerina")
.resolve("cloud");
Path dockerBbePath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("examples")
.resolve("c2c-docker-deployment");
Path k8sBbePath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("examples")
.resolve("c2c-k8s-deployment");
Path docsPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("docs")
.resolve("ballerina")
.resolve("cloud");
Assert.assertTrue(Files.exists(cachePath));
Assert.assertTrue(Files.exists(dockerBbePath));
Assert.assertTrue(Files.exists(k8sBbePath));
Assert.assertTrue(Files.exists(docsPath));
}
@Test()
public void c2cToolingExistsTest() {
Path c2cToolingLibPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("lib")
.resolve("tools")
.resolve("lang-server")
.resolve("lib");
Assert.assertNotNull(TestUtils.findFileOrDirectory(c2cToolingLibPath, "cloud-tooling-"));
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/GraphqlToolTest.java
================================================
/*
* Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com) All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import static org.ballerinalang.distribution.utils.TestUtils.*;
public class GraphqlToolTest {
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DISTRIBUTION_FILE_NAME + ".zip"));
}
@Test(description = "Check GraphQL client generation")
public void testGraphqlClientGenerationUsingEndpoint() throws IOException, InterruptedException {
Path testResource = Paths.get("/graphql/client-gen/project_1");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("graphql_endpoint.config.yaml");
boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("client.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("config_types.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("utils.bal")));
TestUtils.deleteGeneratedFiles("client.bal", GRAPHQL_CMD);
}
@Test(description = "Check GraphQL client generation")
public void testGraphqlClientGenerationUsingSchemaFile() throws IOException, InterruptedException {
Path testResource = Paths.get("/graphql/client-gen/project_2");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("graphql_schema.config.yaml");
boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("client.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("config_types.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("utils.bal")));
TestUtils.deleteGeneratedFiles("client.bal", GRAPHQL_CMD);
}
@Test(description = "Check GraphQL schema generation")
public void testGraphqlSchemaGeneration() throws IOException, InterruptedException {
Path testResource = Paths.get("/graphql/schema-gen");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("service.bal");
boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("schema_graphql.graphql")));
TestUtils.deleteGeneratedFiles("schema_graphql.graphql", GRAPHQL_CMD);
}
@Test(description = "Check GraphQL schema generation")
public void testGraphqlSchemaGenerationWithServicePathFlag() throws IOException, InterruptedException {
Path testResource = Paths.get("/graphql/schema-gen/project_1");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("main.bal");
buildArgs.add("-s");
buildArgs.add("/gql");
boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("schema_gql.graphql")));
TestUtils.deleteGeneratedFiles("schema_gql.graphql", GRAPHQL_CMD);
}
@Test(description = "Check GraphQL service generation")
public void testGraphqlServiceGeneration() throws IOException, InterruptedException {
Path testResource = Paths.get("/graphql/service-gen");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("schema_starwars.graphql");
boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("service.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal")));
TestUtils.deleteGeneratedFiles("service.bal", GRAPHQL_CMD);
}
@Test(description = "Check GraphQL service generation")
public void testGraphqlServiceGenerationWithRecordFlag() throws IOException, InterruptedException {
Path testResource = Paths.get("/graphql/service-gen");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("schema_book.graphql");
buildArgs.add("-r");
boolean successful = TestUtils.executeGraphql(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("service.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("types.bal")));
TestUtils.deleteGeneratedFiles("service.bal", GRAPHQL_CMD);
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/GrpcToolingTest.java
================================================
/*
* Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com) 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.
*/
package org.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.ballerinalang.distribution.utils.TestUtils.DISTRIBUTIONS_DIR;
import static org.ballerinalang.distribution.utils.TestUtils.RESOURCES_PATH;
import static org.ballerinalang.distribution.utils.TestUtils.WHITESPACE_PATTERN;
import static org.ballerinalang.distribution.utils.TestUtils.DISTRIBUTION_FILE_NAME;
import static org.ballerinalang.distribution.utils.TestUtils.getStringFromGivenBalFile;
public class GrpcToolingTest {
private String helpCommandOutput = "NAMEballerina-grpc-GenerateBallerinasourcesforthegivenProtocolBufferdefinitionSYNOPSISbalgrpc--input[--output][--modemode][--proto-path]DESCRIPTIONGeneratetheBallerinagRPCclient/servicesourcesforagivengRPCprotocolbuffer(Protobuf)definition.OPTIONS--inputPathtoa'.proto'fileoradirectorycontainingmultiple'.proto'files.--outputLocationofthegeneratedBallerinasourcefiles.Iftheoutputpathisnotspecified,theoutputwillbewrittentoadirectorycorrespondingtothepackageintheprotocolbufferdefinition.Ifapackageisnotspecified,theoutputwillbewrittentoa'temp'directoryinthecurrentlocation.--modemodeSetthe'client'or'service'modetogeneratesamplecode.Ifnotspecified,onlythestubfileisgenerated.--proto-pathPathtoadirectoryinwhichtolookfor'.proto'fileswhenresolvingimportdirectives.EXAMPLESGeneratetheBallerinagRPCstubfile(forthegiven'.proto'file)ina'stub'directory.$balgrpc--inputchat.proto--outputstubGeneratetheBallerinagRPCstubfileandclientsamplecode(forthegiven'.proto'file)ina'client'directory.$balgrpc--inputchat.proto--outputclient--modeclientGeneratetheBallerinagRPCstubfileandservicesamplecode(forthegiven'.proto'file)ina'service'directory.$balgrpc--inputchat.proto--outputservice--modeservice";
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DISTRIBUTION_FILE_NAME + ".zip"));
}
@Test
public void grpcCommandWithoutInputOptionTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/grpc");
List buildArgs = new LinkedList<>();
InputStream result = TestUtils.executeGrpcCommand(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs, true);
String expectedMsg = "ballerina:missingrequiredoption'--input='Run'balhelp'forusage.";
Assert.assertEquals(readOutputFromStreamAsString(result), expectedMsg);
}
@Test
public void grpcCommandWithoutInputValueTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/grpc");
List buildArgs = new LinkedList<>();
buildArgs.add("--input");
InputStream result = TestUtils.executeGrpcCommand(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs, true);
String expectedMsg = "ballerina:flag'--input'()needsanargumentRun'balhelp'forusage.";
Assert.assertEquals(readOutputFromStreamAsString(result), expectedMsg);
}
@Test
public void grpcCommandWithValidInputTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/grpc");
List buildArgs = new LinkedList<>();
buildArgs.add("--input");
buildArgs.add("proto-files/route_guide.proto");
buildArgs.add("--output");
buildArgs.add(TestUtils.getResource(testResource).toString());
InputStream result = TestUtils.executeGrpcCommand(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs, true);
Assert.assertEquals(readOutputFromStreamAsString(result), "");
String generatedTypes = getStringFromGivenBalFile(TestUtils.getResource(testResource)
.resolve("route_guide_pb.bal"));
String expectedTypes = getStringFromGivenBalFile(RESOURCES_PATH
.resolve("grpc/expected-files/route_guide_pb.bal"));
Assert.assertEquals(generatedTypes, expectedTypes);
}
@Test
public void grpcHelpCommandTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/grpc");
List buildArgs = new LinkedList<>();
buildArgs.add("--help");
InputStream result = TestUtils.executeGrpcCommand(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs, false);
Assert.assertEquals(readOutputFromStreamAsString(result), helpCommandOutput);
}
@Test
public void grpcHCommandTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/grpc");
List buildArgs = new LinkedList<>();
buildArgs.add("-h");
InputStream result = TestUtils.executeGrpcCommand(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs, false);
Assert.assertEquals(readOutputFromStreamAsString(result), helpCommandOutput);
}
private String readOutputFromStreamAsString(InputStream result) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(result))) {
Stream logLines = br.lines();
String generatedLog = logLines.collect(Collectors.joining("\n"));
logLines.close();
generatedLog = (generatedLog.trim()).replaceAll(WHITESPACE_PATTERN, "");
return generatedLog;
}
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/OpenAPIArtifactBuildTest.java
================================================
/*
* 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.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.ballerinalang.distribution.utils.TestUtils.*;
/**
* OpenAPI Tests related to artifact generation.
*/
public class OpenAPIArtifactBuildTest {
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DISTRIBUTION_FILE_NAME + ".zip"));
}
@Test(description = "Check openapi to ballerina generator command")
public void buildOpenAPIToBallerinaTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/openapi");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("petstore.yaml");
boolean successful = TestUtils.executeOpenAPI(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("petstore_service.bal")));
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("client.bal")));
TestUtils.deleteGeneratedFiles("petstore", OPENAPI_CMD);
}
@Test(description = "Check openapi to ballerina generator command with service file only.")
public void buildOpenAPIToBallerinaServiceFileGenerationTest() throws IOException,
InterruptedException {
Path testResource = Paths.get("/openapi");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("petstore.yaml");
buildArgs.add("--mode");
buildArgs.add("service");
boolean successful = TestUtils.executeOpenAPI(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("petstore_service.bal")));
TestUtils.deleteGeneratedFiles("petstore", OPENAPI_CMD);
}
@Test(description = "Check openapi to ballerina generator command for given tags")
public void buildOpenAPIToBallerinaWithFilterTagsTest() throws IOException,
InterruptedException {
Path testResource = Paths.get("/openapi");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("petstoreTags.yaml");
buildArgs.add("--tags");
buildArgs.add("list");
boolean successful = TestUtils.executeOpenAPI(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Path expectedServiceFile = RESOURCES_PATH.resolve("openapi/expected/filtered_tags.bal");
Stream expectedServiceLines = Files.lines(expectedServiceFile);
String expectedService = expectedServiceLines.collect(Collectors.joining("\n"));
if (Files.exists(RESOURCES_PATH.resolve("openapi/petstoretags_service.bal"))) {
Path generatedServiceFile = TestUtils.getResource(testResource).resolve("petstoretags_service.bal");
Stream serviceLines = Files.lines(generatedServiceFile);
String generatedService = serviceLines.collect(Collectors.joining("\n"));
serviceLines.close();
// expectedService = replaceContractPath(expectedServiceLines, expectedService, generatedService);
expectedService = (expectedService.trim()).replaceAll(WHITESPACE_PATTERN, "");
generatedService = (generatedService.trim()).replaceAll(WHITESPACE_PATTERN, "");
if (expectedService.equals(generatedService)) {
Assert.assertTrue(true);
} else {
Assert.fail("Expected content and actual generated content is mismatched for: petstoreTags.yaml");
}
//Clean the generated files
TestUtils.deleteGeneratedFiles("petstoretags", OPENAPI_CMD);
}
}
@Test(description = "Check openapi to ballerina client generator command")
public void buildOpenAPIToBallerinaClientGenerationTests() throws IOException,
InterruptedException {
Path testResource = Paths.get("/openapi");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("openapi_client.yaml");
buildArgs.add("--mode");
buildArgs.add("client");
buildArgs.add("--client-methods");
buildArgs.add("remote");
boolean successful = TestUtils.executeOpenAPI(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
if (Files.exists(RESOURCES_PATH.resolve("openapi/client.bal")) &&
Files.exists(RESOURCES_PATH.resolve("openapi/types.bal")) &&
Files.exists(RESOURCES_PATH.resolve("openapi/utils.bal"))) {
String generatedClient = getStringFromGivenBalFile(TestUtils.getResource(testResource).resolve("client.bal"));
String expectedClient = getStringFromGivenBalFile(RESOURCES_PATH.resolve("openapi/expected/client.bal"));
String generatedTypes = getStringFromGivenBalFile(TestUtils.getResource(testResource).resolve("types.bal"));
String expectedTypes = getStringFromGivenBalFile(RESOURCES_PATH.resolve("openapi/expected/types.bal"));
String generatedUtils = getStringFromGivenBalFile(TestUtils.getResource(testResource).resolve("utils.bal"));
String expectedUtils = getStringFromGivenBalFile(RESOURCES_PATH.resolve("openapi/expected/utils.bal"));
Assert.assertEquals(expectedClient, generatedClient);
Assert.assertEquals(expectedTypes, generatedTypes);
Assert.assertEquals(expectedUtils, generatedUtils);
TestUtils.deleteGeneratedFiles("client.bal", OPENAPI_CMD);
} else {
Assert.fail("Client generation failed");
}
}
@Test(description = "Check ballerina to openapi generator command")
public void buildBallerinaToOpenAPITest() throws IOException, InterruptedException {
Path testResource = Paths.get("/openapi");
List buildArgs = new LinkedList<>();
buildArgs.add("-i");
buildArgs.add("petstore.bal");
boolean successful = TestUtils.executeOpenAPI(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
Assert.assertTrue(successful);
Assert.assertTrue(Files.exists(TestUtils.getResource(testResource).resolve("hello_openapi.yaml")));
TestUtils.deleteGeneratedFiles("hello_openapi.yaml", OPENAPI_CMD);
}
//OpenAPI integration tests
@Test(description = "Test for openapi validator off")
public void buildOpenAPIValidatorOffTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/openapi/integration-tests/testFiles");
List buildArgs = new LinkedList<>();
buildArgs.add("openapi-validator-off.bal");
InputStream outputs = TestUtils.executeOpenapiBuild(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
String msg = "WARNING [openapi-validator-off.bal";
try (BufferedReader br = new BufferedReader(new InputStreamReader(outputs))) {
Stream logLines = br.lines();
String generatedLog = logLines.collect(Collectors.joining("\n"));
logLines.close();
generatedLog = (generatedLog.trim()).replaceAll(WHITESPACE_PATTERN, "");
msg = (msg.trim()).replaceAll(WHITESPACE_PATTERN, "");
if (generatedLog.contains(msg)) {
Assert.assertTrue(true);
} else {
Assert.fail("OpenAPIValidator Off execution fail.");
}
}
}
@Test(description = "Tests for openapi validator on")
public void buildOpenAPIValidatorONTest() throws IOException, InterruptedException {
Path testResource = Paths.get("/openapi/integration-tests/testFiles");
List buildArgs = new LinkedList<>();
buildArgs.add("openapi-validator-on.bal");
InputStream outputs = TestUtils.executeOpenapiBuild(DISTRIBUTION_FILE_NAME, TestUtils.getResource(testResource),
buildArgs);
String msg = "ERROR [openapi-validator-on.bal:";
try (BufferedReader br = new BufferedReader(new InputStreamReader(outputs))) {
Stream logLines = br.lines();
String generatedLog = logLines.collect(Collectors.joining("\n"));
logLines.close();
generatedLog = (generatedLog.trim()).replaceAll(WHITESPACE_PATTERN, "");
msg = (msg.trim()).replaceAll(WHITESPACE_PATTERN, "");
if (generatedLog.contains(msg)) {
Assert.assertTrue(true);
} else {
Assert.fail("OpenAPIValidator On execution fail.");
}
}
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
//Replace contract file path in generated service file with common URL.
public String replaceContractPath(Stream expectedServiceLines, String expectedService,
String generatedService) {
Pattern pattern = Pattern.compile("\\bcontract\\b: \"(.*?)\"");
Matcher matcher = pattern.matcher(generatedService);
matcher.find();
String contractPath = "contract: " + "\"" + matcher.group(1) + "\"";
expectedService = expectedService.replaceAll("\\bcontract\\b: \"(.*?)\"",
Matcher.quoteReplacement(contractPath));
expectedServiceLines.close();
return expectedService;
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/OpenAPIDistributionArtifactCheck.java
================================================
/*
* 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.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.ballerinalang.distribution.utils.TestUtils.DISTRIBUTIONS_DIR;
import static org.ballerinalang.distribution.utils.TestUtils.OPENAPI_VERSION;
import static org.ballerinalang.distribution.utils.TestUtils.SHORT_VERSION;
import static org.ballerinalang.distribution.utils.TestUtils.TEST_DISTRIBUTION_PATH;
/**
* Check if necessary openAPI files exists to build in the distribution.
*/
public class OpenAPIDistributionArtifactCheck {
private static final String DIST_NAME = "ballerina-" + SHORT_VERSION;
private static final String OPENAPI_VERSION_DIR = OPENAPI_VERSION.contains("-") ?
OPENAPI_VERSION.substring(0, OPENAPI_VERSION.indexOf("-")) : OPENAPI_VERSION;
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(DIST_NAME + ".zip"));
}
@Test
public void openapiAnnotationExistsTest() {
Path birPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("repo")
.resolve("bala")
.resolve("ballerina")
.resolve("openapi")
.resolve(OPENAPI_VERSION_DIR)
.resolve("bir");
Path jarPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("repo")
.resolve("bala")
.resolve("ballerina")
.resolve("openapi")
.resolve(OPENAPI_VERSION_DIR)
.resolve("java21")
.resolve("compiler-plugin")
.resolve("libs");
Path toolOpenApiLibsPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("repo")
.resolve("bala")
.resolve("ballerina")
.resolve("tool.openapi")
.resolve(OPENAPI_VERSION_DIR)
.resolve("java21")
.resolve("tool")
.resolve("libs");
Path breLibPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("bre")
.resolve("lib");
Path docsPath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("docs")
.resolve("ballerina")
.resolve("openapi");
Assert.assertTrue(Files.exists(birPath));
Assert.assertTrue(Files.exists(jarPath.resolve("openapi-validator-" + OPENAPI_VERSION + ".jar")));
Assert.assertTrue(Files.exists(toolOpenApiLibsPath.resolve("ballerina-to-openapi-" + OPENAPI_VERSION +".jar")));
Assert.assertTrue(Files.exists(toolOpenApiLibsPath.resolve("openapi-bal-task-plugin-" + OPENAPI_VERSION +".jar")));
Assert.assertTrue(Files.exists(toolOpenApiLibsPath.resolve("openapi-cli-" + OPENAPI_VERSION +".jar")));
Assert.assertTrue(Files.exists(toolOpenApiLibsPath.resolve("openapi-core-" + OPENAPI_VERSION +".jar")));
Assert.assertTrue(Files.exists(docsPath));
}
@AfterClass
public void cleanUp() throws IOException {
// TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/test/PlatformDistributionArtifactCheckTest.java
================================================
/*
* 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.ballerinalang.distribution.test;
import org.ballerinalang.distribution.utils.TestUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import static org.ballerinalang.distribution.utils.TestUtils.*;
/**
* Check if necessary files exists to build in the platform specific distributions.
*/
public class PlatformDistributionArtifactCheckTest {
private static final String DIST_NAME = "ballerina-" + SHORT_VERSION;
@DataProvider(name = "distribution-provider")
public Object[][] distributionNameProvider() {
return new Object[][]{
{"ballerina-" + MAVEN_VERSION + "-" + CODE_NAME},
{"ballerina-" + MAVEN_VERSION + "-" + CODE_NAME + "-linux"},
{"ballerina-" + MAVEN_VERSION + "-" + CODE_NAME + "-macos"},
{"ballerina-" + MAVEN_VERSION + "-" + CODE_NAME + "-windows"}
};
}
@BeforeClass
public void setupDistributions() throws IOException {
TestUtils.cleanDistribution();
for (Object[] dist : distributionNameProvider()) {
String distName = (String) dist[0];
TestUtils.prepareDistribution(DISTRIBUTIONS_DIR.resolve(distName + ".zip"));
}
}
@Test(dataProvider = "distribution-provider", enabled = false)
public void c2cExistsTest(String distributionFileName) {
Path distributionsPath = TEST_DISTRIBUTION_PATH.resolve(distributionFileName).resolve("distributions");
String jballerinaFileName = TestUtils.findFileOrDirectory(distributionsPath, DIST_NAME);
Objects.requireNonNull(jballerinaFileName);
Path cachePath = TEST_DISTRIBUTION_PATH
.resolve(DIST_NAME)
.resolve("repo")
.resolve("cache")
.resolve("ballerina")
.resolve("cloud")
.resolve("1.0.0");
Path breLibPath = distributionsPath
.resolve(jballerinaFileName)
.resolve("bre")
.resolve("lib");
Path bbePath = distributionsPath
.resolve(jballerinaFileName)
.resolve("examples")
.resolve("c2c-deployment");
Path docsPath = distributionsPath
.resolve(jballerinaFileName)
.resolve("docs")
.resolve("ballerina")
.resolve("cloud");
Assert.assertTrue(Files.exists(cachePath));
Assert.assertNotNull(TestUtils.findFileOrDirectory(breLibPath, "c2c-extension-"));
Assert.assertTrue(Files.exists(bbePath));
Assert.assertTrue(Files.exists(docsPath));
}
@Test(dataProvider = "distribution-provider")
public void c2cToolingExistsTest(String distributionFileName) {
Path distributionsPath = TEST_DISTRIBUTION_PATH.resolve(distributionFileName).resolve("distributions");
String jballerinaFileName = TestUtils.findFileOrDirectory(distributionsPath, DIST_NAME);
Objects.requireNonNull(jballerinaFileName);
Path c2cToolingLibPath = distributionsPath
.resolve(jballerinaFileName)
.resolve("lib")
.resolve("tools")
.resolve("lang-server")
.resolve("lib");
Assert.assertNotNull(TestUtils.findFileOrDirectory(c2cToolingLibPath, "cloud-tooling-"));
}
@AfterClass
public void cleanUp() throws IOException {
TestUtils.cleanDistribution();
}
}
================================================
FILE: ballerina-test/src/test/java/org/ballerinalang/distribution/utils/TestUtils.java
================================================
/*
* 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.ballerinalang.distribution.utils;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.testng.Assert;
import java.io.BufferedReader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.sql.Timestamp;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Utility class for tests
*/
public class TestUtils {
public static final PrintStream OUT = System.out;
public static final Path TARGET_DIR = Paths.get(System.getProperty("target.dir"));
public static final Path MAVEN_VERSION = Paths.get(System.getProperty("maven.version"));
public static final Path SHORT_VERSION = Paths.get(System.getProperty("short.version"));
public static final Path CODE_NAME = Paths.get(System.getProperty("code.name"));
public static final String OPENAPI_VERSION = System.getProperty("openapi.version");
public static final Path DISTRIBUTIONS_DIR = Paths.get(System.getProperty("distributions.dir"));
public static final Path TEST_DISTRIBUTION_PATH = TARGET_DIR.resolve("test-distribution");
public static final Path EXAMPLES_DIR = Paths.get(System.getProperty("examples.dir"));
public static final String EXTENSTIONS_TO_BE_FILTERED_FOR_LINE_CHECKS = System.getProperty("line.check.extensions");
public static final Path RESOURCES_PATH = TARGET_DIR.resolve("resources/test");
private static final String SWAN_LAKE_KEYWORD = "swan-lake";
public static final String WHITESPACE_PATTERN = "\\s+";
private static String balFile = "bal";
public static final String DISTRIBUTION_FILE_NAME = "ballerina-" + MAVEN_VERSION + "-" + CODE_NAME;
public static final String OPENAPI_CMD = "openapi";
public static final String GRAPHQL_CMD = "graphql";
/**
* Log the output of an input stream.
*
* @param inputStream The stream.
* @throws IOException Error reading the stream.
*/
private static void logOutput(InputStream inputStream) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
br.lines().forEach(OUT::println);
}
}
/**
* Execute ballerina build command.
*
* @param distributionName The name of the distribution.
* @param sourceDirectory The directory where the sources files are location.
* @param args The arguments to be passed to the build command.
* @return True if build is successful, else false.
* @throws IOException Error executing build command.
* @throws InterruptedException Interrupted error executing build command.
*/
public static boolean executeBuild(String distributionName, Path sourceDirectory, List args) throws
IOException, InterruptedException {
if (System.getProperty("os.name").startsWith("Windows")) {
balFile = "bal.bat";
}
args.add(0, "build");
args.add(0, TEST_DISTRIBUTION_PATH.resolve(distributionName).resolve("bin").resolve(balFile).toString());
OUT.println("Executing: " + StringUtils.join(args, ' '));
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(sourceDirectory.toFile());
Process process = pb.start();
int exitCode = process.waitFor();
logOutput(process.getInputStream());
logOutput(process.getErrorStream());
return exitCode == 0;
}
/**
* Execute ballerina build command with openAPI annotation.
*
* @param distributionName The name of the distribution.
* @param sourceDirectory The directory where the sources files are location.
* @param args The arguments to be passed to the build command.
* @return inputream with log outputs
* @throws IOException Error executing build command.
* @throws InterruptedException Interrupted error executing build command.
*/
public static InputStream executeOpenapiBuild(String distributionName, Path sourceDirectory, List args) throws
IOException, InterruptedException {
args.add(0, "build");
Process process = getProcessBuilderResults(distributionName, sourceDirectory, args);
return process.getErrorStream();
}
/**
* Execute bal grpc command with provided input
*
* @param distributionName The name of the distribution.
* @param sourceDirectory The directory where the sources files are located.
* @param args The arguments to be passed to the build command.
* @return error stream from the `grpc` command as an `InputStream`
* @throws IOException Error executing build command.
* @throws InterruptedException Interrupted error executing build command.
*/
public static InputStream executeGrpcCommand(String distributionName, Path sourceDirectory, List args,
boolean getError) throws
IOException, InterruptedException {
args.add(0, "grpc");
Process process = getProcessBuilderResults(distributionName, sourceDirectory, args);
process.waitFor();
return getError ? process.getErrorStream() : process.getInputStream();
}
/**
* Execute ballerina openapi command.
*
* @param distributionName The name of the distribution.
* @param sourceDirectory The directory where the sources files are location.
* @param args The arguments to be passed to the build command.
* @return True if build is successful, else false.
* @throws IOException Error executing build command.
* @throws InterruptedException Interrupted error executing build command.
*/
public static boolean executeOpenAPI(String distributionName, Path sourceDirectory, List args) throws
IOException, InterruptedException {
args.add(0, OPENAPI_CMD);
Process process = getProcessBuilderResults(distributionName, sourceDirectory, args);
int exitCode = process.waitFor();
logOutput(process.getInputStream());
logOutput(process.getErrorStream());
return exitCode == 0;
}
/**
* Execute ballerina graphql command.
*
* @param distributionName The name of the distribution.
* @param sourceDirectory The directory where the sources files are location.
* @param args The arguments to be passed to the build command.
* @return True if build is successful, else false.
* @throws IOException Error executing build command.
* @throws InterruptedException Interrupted error executing build command.
*/
public static boolean executeGraphql(String distributionName, Path sourceDirectory, List args) throws
IOException, InterruptedException {
args.add(0, GRAPHQL_CMD);
Process process = getProcessBuilderResults(distributionName, sourceDirectory, args);
int exitCode = process.waitFor();
logOutput(process.getInputStream());
logOutput(process.getErrorStream());
return exitCode == 0;
}
/**
* Get Process from given arguments.
* @param distributionName The name of the distribution.
* @param sourceDirectory The directory where the sources files are location.
* @param args The arguments to be passed to the build command.
* @return process
* @throws IOException Error executing build command.
* @throws InterruptedException Interrupted error executing build command.
*/
public static Process getProcessBuilderResults(String distributionName, Path sourceDirectory, List args)
throws IOException, InterruptedException {
if (System.getProperty("os.name").startsWith("Windows")) {
balFile = "bal.bat";
}
args.add(0, TEST_DISTRIBUTION_PATH.resolve(distributionName).resolve("bin").resolve(balFile).toString());
OUT.println("Executing: " + StringUtils.join(args, ' '));
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(sourceDirectory.toFile());
Process process = pb.start();
int exitCode = process.waitFor();
return process;
}
/**
* Extracts a distribution to a temporary directory.
*
* @param distributionZipPath Path to the distribution.
* @throws ZipException Error occurred when extracting.
*/
public static void prepareDistribution(Path distributionZipPath) throws ZipException {
OUT.println("Extracting: " + distributionZipPath.normalize());
ZipFile zipFile = new ZipFile(distributionZipPath.toFile());
zipFile.extractAll(TEST_DISTRIBUTION_PATH.toAbsolutePath().toString());
}
/**
* Delete the temporary directory used to extract distributions.
*
* @throws IOException If temporary directory does not exists.
*/
public static void cleanDistribution() throws IOException {
FileUtils.deleteDirectory(TEST_DISTRIBUTION_PATH.toFile());
}
/**
* Get the exact path to a test resource.
*
* @param resource Path of the file or directory.
* @return The exact path of the file or directory.
*/
public static Path getResource(Path resource) {
File file = new File(TestUtils.class.getResource(resource.toString()).getFile());
return file.toPath();
}
/**
* Find the name of a file or directory that starts with a given name.
*
* @param dir Directory to find in.
* @param dirName The name of the file or directory to find.
* @return Name of the file or directory if found. Else null.
*/
public static String findFileOrDirectory(Path dir, String dirName) {
FilenameFilter fileNameFilter = (dir1, name) -> name.startsWith(dirName);
String[] fileNames = Objects.requireNonNull(dir.toFile().list(fileNameFilter));
return fileNames.length > 0 ? fileNames[0] : null;
}
/**
* Delete openapi and graphql generated files.
*
* @param generatedFileName file name that need to delete.
*/
public static void deleteGeneratedFiles(String generatedFileName, String command) throws IOException {
Path resourcesPath = RESOURCES_PATH.resolve(command);
if (Files.exists(resourcesPath)) {
List listFiles = Arrays.asList(
Objects.requireNonNull(new File(String.valueOf(resourcesPath)).listFiles()));
for (File existsFile: listFiles) {
String fileName = existsFile.getName();
if (fileName.equals(generatedFileName) || fileName.equals(generatedFileName + "_service.bal") ||
fileName.equals("client.bal") || fileName.equals("types.bal") || fileName.equals("utils.bal") ||
fileName.equals("config_types.bal")) {
existsFile.delete();
}
}
Path directoryPath = resourcesPath.resolve("tests");
if (Files.isDirectory(directoryPath)) {
File file = new File(directoryPath.toString());
FileUtils.deleteDirectory(file);
}
}
}
/**
* Execute the given command.
*
* @param command command needs to be execute
* @return output of the executed command
*/
public static String executeCommand(String command) throws IOException {
String output = "";
File file = new File(getUserHome() + File.separator
+ "temp-" + new Timestamp(System.currentTimeMillis()).getTime() + ".sh");
file.createNewFile();
file.setExecutable(true);
PrintWriter writer = new PrintWriter(file.getPath(), StandardCharsets.UTF_8);
writer.println(command);
writer.close();
ProcessBuilder pb = new ProcessBuilder(file.getPath());
Process process = pb.start();
InputStream inputStream = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
output += line + "\n";
}
if (output.isEmpty()) {
inputStream = process.getErrorStream();
reader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = reader.readLine()) != null) {
output += line + "\n";
}
}
file.delete();
return output;
}
/**
* Provide user home directory based on command.
*
* @return user home directory
*/
public static String getUserHome() {
String userHome = System.getenv("HOME");
if (userHome == null) {
userHome = System.getProperty("user.home");
}
return userHome;
}
/**
* Execute smoke testing to verify installation.
*
* @param path Path to the bal file
* @param jBallerinaVersion Installed jBallerina version
* @param specVersion Installed language specification
* @param toolVersion Installed tool version
* @param versionDisplayText Installed version display text
*/
public static void testInstallation(String path, String jBallerinaVersion, String specVersion, String toolVersion,
String versionDisplayText) throws IOException {
Assert.assertEquals(TestUtils.executeCommand(path + " -v"),
TestUtils.getVersionOutput(jBallerinaVersion, specVersion, toolVersion, versionDisplayText));
}
/**
* Get version output for version command.
* @param jBallerinaVersion Installed jBallerina version
* @param specVersion Installed language specification
* @param toolVersion Installed tool version
* @param versionDisplayText display text for installed jBallerina version
*
* @return version output
*/
public static String getVersionOutput(String jBallerinaVersion, String specVersion, String toolVersion,
String versionDisplayText) {
String toolText = TestUtils.isOldToolVersion(toolVersion) ? "Ballerina tool" : "Update Tool";
if (jBallerinaVersion.contains(TestUtils.SWAN_LAKE_KEYWORD)) {
return "Ballerina " + versionDisplayText + " (Swan Lake)\n" + "Language specification " + specVersion +
"\n" + toolText + " " + toolVersion + "\n";
}
String ballerinaReference = isSupportedRelease(jBallerinaVersion) ? "jBallerina" : "Ballerina";
return ballerinaReference + " " + jBallerinaVersion + "\n" + "Language specification " + specVersion + "\n" +
toolText + " " + toolVersion + "\n";
}
/**
* To check whether older tool version before swan lake support
*
* @param toolVersion
* @return returns is a older version
*/
public static boolean isOldToolVersion(String toolVersion) {
return toolVersion.equals("0.8.5") || toolVersion.equals("0.8.0");
}
/**
* To check whether installation is a 1.0.x release.
*
* @return returns is a 1.0.x release
*/
public static boolean isSupportedRelease(String version) {
if (version.contains(SWAN_LAKE_KEYWORD)) {
return true;
}
String[] versions = version.split("\\.");
return !(versions[0].equals("1") && versions[1].equals("0"));
}
/**
* Get the content of the file.
* @param filePath Path to the file
* @return content of the file
* @throws IOException
*/
public static String getContent(Path filePath) throws IOException {
return Files.readString(filePath);
}
public static String getStringFromGivenBalFile(Path expectedServiceFile) throws IOException {
Stream expectedServiceLines = Files.lines(expectedServiceFile);
String expectedServiceContent = expectedServiceLines.collect(Collectors.joining(System.lineSeparator()));
expectedServiceLines.close();
return expectedServiceContent.trim().replaceAll(
WHITESPACE_PATTERN, "").replaceAll(System.lineSeparator(), "");
}
}
================================================
FILE: ballerina-test/src/test/resources/graphql/client-gen/project_1/graphql_endpoint.config.yaml
================================================
## The GraphQL schema. E.g., https://countries.trevorblades.com or ./schemas/country.graphql
schema: https://countries.trevorblades.com
## The GraphQL documents that have queries/mutations
documents:
- ./query_country.graphql
================================================
FILE: ballerina-test/src/test/resources/graphql/client-gen/project_1/query_country.graphql
================================================
query countryByCode($code: ID!) {
country(code: $code) {
name
}
}
================================================
FILE: ballerina-test/src/test/resources/graphql/client-gen/project_2/graphql_schema.config.yaml
================================================
## The GraphQL schema. E.g., https://countries.trevorblades.com or ./schemas/country.graphql
schema: ./schema_country.graphql
## The GraphQL documents that have queries/mutations
documents:
- ./queries/query_country.graphql
================================================
FILE: ballerina-test/src/test/resources/graphql/client-gen/project_2/queries/query_country.graphql
================================================
query countryByCode($code: ID!) {
country(code: $code) {
name
}
}
================================================
FILE: ballerina-test/src/test/resources/graphql/client-gen/project_2/schema_country.graphql
================================================
schema {
query: Query
}
"Directs the executor to include this field or fragment only when the `if` argument is true"
directive @include(
"Included when true."
if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
"Directs the executor to skip this field or fragment when the `if`'argument is true."
directive @skip(
"Skipped when true."
if: Boolean!
) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
directive @cacheControl(maxAge: Int, scope: CacheControlScope) on OBJECT | FIELD_DEFINITION | INTERFACE
"Marks the field, argument, input field or enum value as deprecated"
directive @deprecated(
"The reason for the deprecation"
reason: String = "No longer supported"
) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION
"Exposes a URL that specifies the behaviour of this scalar."
directive @specifiedBy(
"The URL that specifies the behaviour of this scalar."
url: String!
) on SCALAR
type Continent {
code: ID!
countries: [Country!]!
name: String!
}
type Country {
capital: String
code: ID!
continent: Continent!
currency: String
emoji: String!
emojiU: String!
languages: [Language!]!
name: String!
native: String!
phone: String!
states: [State!]!
}
type Language {
code: ID!
name: String
native: String
rtl: Boolean!
}
type Query {
continent(code: ID!): Continent
continents(filter: ContinentFilterInput): [Continent!]!
countries(filter: CountryFilterInput): [Country!]!
country(code: ID!): Country
language(code: ID!): Language
languages(filter: LanguageFilterInput): [Language!]!
}
type State {
code: String
country: Country!
name: String!
}
enum CacheControlScope {
PRIVATE
PUBLIC
}
"The `Upload` scalar type represents a file upload."
scalar Upload
input ContinentFilterInput {
code: StringQueryOperatorInput
}
input CountryFilterInput {
code: StringQueryOperatorInput
continent: StringQueryOperatorInput
currency: StringQueryOperatorInput
}
input LanguageFilterInput {
code: StringQueryOperatorInput
}
input StringQueryOperatorInput {
eq: String
in: [String!]
ne: String
nin: [String!]
regex: String
}
================================================
FILE: ballerina-test/src/test/resources/graphql/schema-gen/project_1/Ballerina.toml
================================================
[package]
org = "ballerina_sdl_file_generator_test"
name = "project_1"
version = "0.1.0"
================================================
FILE: ballerina-test/src/test/resources/graphql/schema-gen/project_1/main.bal
================================================
// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org). All Rights Reserved.
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import ballerina/graphql;
public type Person record {|
string name;
int age;
|};
service /'service/gql on new graphql:Listener(4000) {
resource function get profile() returns Person {
return {
name: "Walter White",
age: 52
};
}
}
service /gql on new graphql:Listener(4001) {
resource function get school() returns School {
return new;
}
}
service /'service/gql/'new on new graphql:Listener(4002) {
resource function get name() returns string {
return "Ballerina";
}
resource function get age() returns int {
return 27;
}
}
service class School {
resource function get name(string name) returns string {
return name;
}
}
================================================
FILE: ballerina-test/src/test/resources/graphql/schema-gen/service.bal
================================================
// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org). All Rights Reserved.
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import ballerina/graphql;
# Episodes of Starwars Series
public enum Episode {
# The episode new hope.
# `Luke Skywalker` joins forces with a Jedi Knight.
#
# Check new lines in the documentation
#
NEWHOPE,
#
EMPIRE,
# The episode jedi
JEDI
}
#
# + stars - Number of stars
# + episode - The episode
public type Review record {
int stars;
Episode episode?;
string commentary?;
};
# The review input type
public type ReviewInput record {
# Number of stars
int stars;
string commentary;
};
public type Profile distinct service object {
# The name of the human
# + id -
# + age -
# This should be an integer
#
# This is a Non-Null type argument
# + return - The name
resource function get name(string id, int age, boolean isAdult) returns string;
};
# Planet of the Human
public type Planet distinct service object {
# The home planet of the human, or null if unknown
# + return - The homePlanet
resource function get homePlanet() returns string?;
};
# A mechanical character from the Star Wars universe.
# It can be a **Human** or a ~Droid~
public type Character distinct service object {
*Profile;
# The unique identifier of the character
# + return - The id
resource function get id() returns string;
# The episodes this character appears in
# + return - The episodes
# # Deprecated
# This field is deprecated.
# Use `appears` field instead of this.
@deprecated
resource function get appearsIn() returns Episode[];
};
# A humanoid creature from the Star Wars universe
distinct service class Human {
*Character;
*Planet;
# The unique identifier of the human
# The type of the id is String
#
# The "id" returns a string
# + return - The id
resource function get id() returns string {
return "";
}
# The name of the human
# + return - The name
resource function get name(string id, int age, boolean isAdult) returns string {
return "";
}
# The home planet of the human, or null if unknown
# + return - The homePlanet
resource function get homePlanet() returns string? {
return;
}
# Height in meters, or null if unknown
# + return - The height
resource function get height() returns float? {
return ;
}
# Mass in kilograms, or null if unknown
# + return - The mass
resource function get mass() returns int? {
return ;
}
# The episodes this human appears in
# + return - The episodes
resource function get appearsIn() returns Episode[] {
return [JEDI];
}
}
service /graphql on new graphql:Listener(9000) {
# Fetch the hero of the Star Wars
# + episode - The episode which hero appears
# This is a Nullable input
#
# + return - The hero
resource function get hero(Episode? episode) returns Profile {
return new Human();
}
# Returns reviews of the Star Wars
# + episode - The episode
# Default value of the `episode` is ""JEDI""
# + return - The reviews
resource function get reviews(Episode episode = JEDI, string name = "Luke") returns Review?[] {
return [];
}
# Returns characters by id, or null if character is not found
# + name - Name of the character
#
# + return - The characters
resource function get characters(string[] idList, string name) returns Character?[] {
Character[] characters = [new Human()];
return characters;
}
# Returns a human by id,
# or null if human is not found
# + id - **id** of the human
# + return - The Human
resource function get human(string id) returns Human? {
if id.includes("human") {
return new Human();
}
return;
}
# The home planet of the human, or null if unknown
# + return - The homePlanet
resource function get planet() returns Planet? {
return;
}
# Add new reviews.
# Return the updated review values
# + episode - Episode name
# + reviewInput - Review of the episode.
#
# This should be an `input object` type value
# + return - The reviews
remote function createReview(Episode episode, ReviewInput reviewInput) returns Review {
Review review = {
stars: reviewInput.stars
};
return review;
}
}
================================================
FILE: ballerina-test/src/test/resources/graphql/service-gen/schema_book.graphql
================================================
type Query {
"Fetch all the books from database"
books: [Book]
"Fetch a book by its id"
book(
id: Int!
): Book
}
type Book {
id: Int!
title: String!
}
================================================
FILE: ballerina-test/src/test/resources/graphql/service-gen/schema_starwars.graphql
================================================
type Query {
"Fetch the hero of the Star Wars"
hero(episode: Episode): Character!
"Returns reviews of the Star Wars"
reviews(episode: Episode!): [Review]!
"Returns characters by id, or null if character is not found"
characters(idList: [String!]!): [Character]!
"Returns a droid by id, or null if droid is not found"
droid(id: String! = ""): Droid
"Returns a human by id, or null if human is not found"
human(id: String!): Human
"Returns a starship by id, or null if starship is not found"
starship(id: String!): Starship
"Returns search results by text, or null if search item is not found"
search(text: String!): [SearchResult!]
}
"A mechanical character from the Star Wars universe"
interface Character {
"The unique identifier of the character"
id: String!
"The name of the character"
name: String!
"This character's friends, or an empty list if they have none"
friends: [Character!]!
"The episodes this character appears in"
appearsIn: [Episode!]!
}
"A humanoid creature from the Star Wars universe"
type Human implements Character {
"The unique identifier of the human"
id: String!
"The name of the human"
name: String!
"The home planet of the human, or null if unknown"
homePlanet: String
"Height in meters, or null if unknown"
height: Float
"Mass in kilograms, or null if unknown"
mass: Int
"This human's friends, or an empty list if they have none"
friends: [Character!]!
"The episodes this human appears in"
appearsIn: [Episode!]!
"A list of starships this person has piloted, or an empty list if none"
starships: [Starship!]!
}
enum Episode {
JEDI
EMPIRE
NEWHOPE
}
"A ship from the Star Wars universe"
type Starship {
"The unique identifier of the starship"
id: String!
"The name of the starship"
name: String!
"The length of the starship, or null if unknown"
length: Float
"Cordinates of the starship, or null if unknown"
cordinates: [[Float!]!]
}
"An autonomous mechanical character in the Star Wars universe"
type Droid implements Character {
"The unique identifier of the droid"
id: String!
"The name of the droid"
name: String!
"This droid's friends, or an empty list if they have none"
friends: [Character!]!
"The episodes this droid appears in"
appearsIn: [Episode!]!
"This droid's primary function"
primaryFunction: String
}
type Review {
episode: Episode!
stars: Int!
commentary: String
}
"auto-generated union type from Ballerina"
union SearchResult = Human|Droid|Starship
type Mutation {
"Add new reviews and return the review values"
createReview(
"Episode name"
episode: Episode!
"Review of the episode"
reviewInput: ReviewInput!
): Review!
}
input ReviewInput {
stars: Int!
commentary: String
}
type Subscription {
"Subscribe to review updates"
reviewAdded(
"Episode name"
episode: Episode!
): Review!
}
================================================
FILE: ballerina-test/src/test/resources/grpc/expected-files/route_guide_pb.bal
================================================
import ballerina/grpc;
import ballerina/protobuf;
public const string ROUTE_GUIDE_DESC = "0A11726F7574655F67756964652E70726F746F120A726F757465677569646522410A05506F696E74121A0A086C6174697475646518012001280552086C61746974756465121C0A096C6F6E67697475646518022001280552096C6F6E67697475646522510A0952656374616E676C6512210A026C6F18012001280B32112E726F75746567756964652E506F696E7452026C6F12210A02686918022001280B32112E726F75746567756964652E506F696E7452026869224C0A074665617475726512120A046E616D6518012001280952046E616D65122D0A086C6F636174696F6E18022001280B32112E726F75746567756964652E506F696E7452086C6F636174696F6E22540A09526F7574654E6F7465122D0A086C6F636174696F6E18012001280B32112E726F75746567756964652E506F696E7452086C6F636174696F6E12180A076D65737361676518022001280952076D6573736167652293010A0C526F75746553756D6D617279121F0A0B706F696E745F636F756E74180120012805520A706F696E74436F756E7412230A0D666561747572655F636F756E74180220012805520C66656174757265436F756E74121A0A0864697374616E6365180320012805520864697374616E636512210A0C656C61707365645F74696D65180420012805520B656C617073656454696D653285020A0A526F757465477569646512360A0A4765744665617475726512112E726F75746567756964652E506F696E741A132E726F75746567756964652E466561747572652200123E0A0C4C697374466561747572657312152E726F75746567756964652E52656374616E676C651A132E726F75746567756964652E4665617475726522003001123E0A0B5265636F7264526F75746512112E726F75746567756964652E506F696E741A182E726F75746567756964652E526F75746553756D6D61727922002801123F0A09526F7574654368617412152E726F75746567756964652E526F7574654E6F74651A152E726F75746567756964652E526F7574654E6F746522002801300142680A1B696F2E677270632E6578616D706C65732E726F7574656775696465420F526F757465477569646550726F746F50015A36676F6F676C652E676F6C616E672E6F72672F677270632F6578616D706C65732F726F7574655F67756964652F726F7574656775696465620670726F746F33";
public isolated client class RouteGuideClient {
*grpc:AbstractClientEndpoint;
private final grpc:Client grpcClient;
public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? {
self.grpcClient = check new (url, config);
check self.grpcClient.initStub(self, ROUTE_GUIDE_DESC);
}
isolated remote function GetFeature(Point|ContextPoint req) returns Feature|grpc:Error {
map headers = {};
Point message;
if req is ContextPoint {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeSimpleRPC("routeguide.RouteGuide/GetFeature", message, headers);
[anydata, map] [result, _] = payload;
return result;
}
isolated remote function GetFeatureContext(Point|ContextPoint req) returns ContextFeature|grpc:Error {
map headers = {};
Point message;
if req is ContextPoint {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeSimpleRPC("routeguide.RouteGuide/GetFeature", message, headers);
[anydata, map] [result, respHeaders] = payload;
return {content: result, headers: respHeaders};
}
isolated remote function RecordRoute() returns RecordRouteStreamingClient|grpc:Error {
grpc:StreamingClient sClient = check self.grpcClient->executeClientStreaming("routeguide.RouteGuide/RecordRoute");
return new RecordRouteStreamingClient(sClient);
}
isolated remote function ListFeatures(Rectangle|ContextRectangle req) returns stream|grpc:Error {
map headers = {};
Rectangle message;
if req is ContextRectangle {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeServerStreaming("routeguide.RouteGuide/ListFeatures", message, headers);
[stream, map] [result, _] = payload;
FeatureStream outputStream = new FeatureStream(result);
return new stream(outputStream);
}
isolated remote function ListFeaturesContext(Rectangle|ContextRectangle req) returns ContextFeatureStream|grpc:Error {
map headers = {};
Rectangle message;
if req is ContextRectangle {
message = req.content;
headers = req.headers;
} else {
message = req;
}
var payload = check self.grpcClient->executeServerStreaming("routeguide.RouteGuide/ListFeatures", message, headers);
[stream, map] [result, respHeaders] = payload;
FeatureStream outputStream = new FeatureStream(result);
return {content: new stream(outputStream), headers: respHeaders};
}
isolated remote function RouteChat() returns RouteChatStreamingClient|grpc:Error {
grpc:StreamingClient sClient = check self.grpcClient->executeBidirectionalStreaming("routeguide.RouteGuide/RouteChat");
return new RouteChatStreamingClient(sClient);
}
}
public isolated client class RecordRouteStreamingClient {
private final grpc:StreamingClient sClient;
isolated function init(grpc:StreamingClient sClient) {
self.sClient = sClient;
}
isolated remote function sendPoint(Point message) returns grpc:Error? {
return self.sClient->send(message);
}
isolated remote function sendContextPoint(ContextPoint message) returns grpc:Error? {
return self.sClient->send(message);
}
isolated remote function receiveRouteSummary() returns RouteSummary|grpc:Error? {
var response = check self.sClient->receive();
if response is () {
return response;
} else {
[anydata, map] [payload, _] = response;
return payload;
}
}
isolated remote function receiveContextRouteSummary() returns ContextRouteSummary|grpc:Error? {
var response = check self.sClient->receive();
if response is () {
return response;
} else {
[anydata, map] [payload, headers] = response;
return {content: payload, headers: headers};
}
}
isolated remote function sendError(grpc:Error response) returns grpc:Error? {
return self.sClient->sendError(response);
}
isolated remote function complete() returns grpc:Error? {
return self.sClient->complete();
}
}
public class FeatureStream {
private stream anydataStream;
public isolated function init(stream anydataStream) {
self.anydataStream = anydataStream;
}
public isolated function next() returns record {|Feature value;|}|grpc:Error? {
var streamValue = self.anydataStream.next();
if streamValue is () {
return streamValue;
} else if streamValue is grpc:Error {
return streamValue;
} else {
record {|Feature value;|} nextRecord = {value: streamValue.value};
return nextRecord;
}
}
public isolated function close() returns grpc:Error? {
return self.anydataStream.close();
}
}
public isolated client class RouteChatStreamingClient {
private final grpc:StreamingClient sClient;
isolated function init(grpc:StreamingClient sClient) {
self.sClient = sClient;
}
isolated remote function sendRouteNote(RouteNote message) returns grpc:Error? {
return self.sClient->send(message);
}
isolated remote function sendContextRouteNote(ContextRouteNote message) returns grpc:Error? {
return self.sClient->send(message);
}
isolated remote function receiveRouteNote() returns RouteNote|grpc:Error? {
var response = check self.sClient->receive();
if response is () {
return response;
} else {
[anydata, map] [payload, _] = response;
return payload;
}
}
isolated remote function receiveContextRouteNote() returns ContextRouteNote|grpc:Error? {
var response = check self.sClient->receive();
if response is () {
return response;
} else {
[anydata, map] [payload, headers] = response;
return {content: payload, headers: headers};
}
}
isolated remote function sendError(grpc:Error response) returns grpc:Error? {
return self.sClient->sendError(response);
}
isolated remote function complete() returns grpc:Error? {
return self.sClient->complete();
}
}
public isolated client class RouteGuideRouteSummaryCaller {
private final grpc:Caller caller;
public isolated function init(grpc:Caller caller) {
self.caller = caller;
}
public isolated function getId() returns int {
return self.caller.getId();
}
isolated remote function sendRouteSummary(RouteSummary response) returns grpc:Error? {
return self.caller->send(response);
}
isolated remote function sendContextRouteSummary(ContextRouteSummary response) returns grpc:Error? {
return self.caller->send(response);
}
isolated remote function sendError(grpc:Error response) returns grpc:Error? {
return self.caller->sendError(response);
}
isolated remote function complete() returns grpc:Error? {
return self.caller->complete();
}
public isolated function isCancelled() returns boolean {
return self.caller.isCancelled();
}
}
public isolated client class RouteGuideRouteNoteCaller {
private final grpc:Caller caller;
public isolated function init(grpc:Caller caller) {
self.caller = caller;
}
public isolated function getId() returns int {
return self.caller.getId();
}
isolated remote function sendRouteNote(RouteNote response) returns grpc:Error? {
return self.caller->send(response);
}
isolated remote function sendContextRouteNote(ContextRouteNote response) returns grpc:Error? {
return self.caller->send(response);
}
isolated remote function sendError(grpc:Error response) returns grpc:Error? {
return self.caller->sendError(response);
}
isolated remote function complete() returns grpc:Error? {
return self.caller->complete();
}
public isolated function isCancelled() returns boolean {
return self.caller.isCancelled();
}
}
public isolated client class RouteGuideFeatureCaller {
private final grpc:Caller caller;
public isolated function init(grpc:Caller caller) {
self.caller = caller;
}
public isolated function getId() returns int {
return self.caller.getId();
}
isolated remote function sendFeature(Feature response) returns grpc:Error? {
return self.caller->send(response);
}
isolated remote function sendContextFeature(ContextFeature response) returns grpc:Error? {
return self.caller->send(response);
}
isolated remote function sendError(grpc:Error response) returns grpc:Error? {
return self.caller->sendError(response);
}
isolated remote function complete() returns grpc:Error? {
return self.caller->complete();
}
public isolated function isCancelled() returns boolean {
return self.caller.isCancelled();
}
}
public type ContextRouteNoteStream record {|
stream content;
map headers;
|};
public type ContextPointStream record {|
stream content;
map headers;
|};
public type ContextFeatureStream record {|
stream content;
map headers;
|};
public type ContextRouteSummary record {|
RouteSummary content;
map headers;
|};
public type ContextRouteNote record {|
RouteNote content;
map headers;
|};
public type ContextRectangle record {|
Rectangle content;
map headers;
|};
public type ContextPoint record {|
Point content;
map headers;
|};
public type ContextFeature record {|
Feature content;
map headers;
|};
@protobuf:Descriptor {value: ROUTE_GUIDE_DESC}
public type RouteSummary record {|
int point_count = 0;
int feature_count = 0;
int distance = 0;
int elapsed_time = 0;
|};
@protobuf:Descriptor {value: ROUTE_GUIDE_DESC}
public type RouteNote record {|
Point location = {};
string message = "";
|};
@protobuf:Descriptor {value: ROUTE_GUIDE_DESC}
public type Rectangle record {|
Point lo = {};
Point hi = {};
|};
@protobuf:Descriptor {value: ROUTE_GUIDE_DESC}
public type Point record {|
int latitude = 0;
int longitude = 0;
|};
@protobuf:Descriptor {value: ROUTE_GUIDE_DESC}
public type Feature record {|
string name = "";
Point location = {};
|};
================================================
FILE: ballerina-test/src/test/resources/grpc/proto-files/route_guide.proto
================================================
// Copyright 2015 gRPC authors.
//
// 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.
// Taken from: https://github.com/grpc/grpc-go/blob/v1.38.0/examples/route_guide/routeguide/route_guide.proto
syntax = "proto3";
option go_package = "google.golang.org/grpc/examples/route_guide/routeguide";
option java_multiple_files = true;
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";
package routeguide;
// Interface exported by the server.
service RouteGuide {
// A simple RPC.
//
// Obtains the feature at a given position.
//
// A feature with an empty name is returned if there's no feature at the given
// position.
rpc GetFeature(Point) returns (Feature) {}
// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
message Rectangle {
// One corner of the rectangle.
Point lo = 1;
// The other corner of the rectangle.
Point hi = 2;
}
// A feature names something at a given point.
//
// If a feature could not be named, the name is empty.
message Feature {
// The name of the feature.
string name = 1;
// The point where the feature is detected.
Point location = 2;
}
// A RouteNote is a message sent while at a given point.
message RouteNote {
// The location from which the message is sent.
Point location = 1;
// The message to be sent.
string message = 2;
}
// A RouteSummary is received in response to a RecordRoute rpc.
//
// It contains the number of individual points received, the number of
// detected features, and the total distance covered as the cumulative sum of
// the distance between each point.
message RouteSummary {
// The number of points received.
int32 point_count = 1;
// The number of known features passed while traversing the route.
int32 feature_count = 2;
// The distance covered in metres.
int32 distance = 3;
// The duration of the traversal in seconds.
int32 elapsed_time = 4;
}
================================================
FILE: ballerina-test/src/test/resources/openapi/expected/client.bal
================================================
// AUTO-GENERATED FILE. DO NOT MODIFY.
// This file is auto-generated by the Ballerina OpenAPI tool.
import ballerina/http;
# The Stripe REST API. Please see https://stripe.com/docs/api for more details.
public isolated client class Client {
final http:Client clientEp;
# Gets invoked to initialize the `connector`.
#
# + config - The configurations to be used when initializing the `connector`
# + serviceUrl - URL of the target service
# + return - An error if connector initialization failed
public isolated function init(ConnectionConfig config, string serviceUrl) returns error? {
http:ClientConfiguration httpClientConfig = {auth: config.auth, httpVersion: config.httpVersion, timeout: config.timeout, forwarded: config.forwarded, poolConfig: config.poolConfig, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, validation: config.validation};
do {
if config.http1Settings is ClientHttp1Settings {
ClientHttp1Settings settings = check config.http1Settings.ensureType(ClientHttp1Settings);
httpClientConfig.http1Settings = {...settings};
}
if config.http2Settings is http:ClientHttp2Settings {
httpClientConfig.http2Settings = check config.http2Settings.ensureType(http:ClientHttp2Settings);
}
if config.cache is http:CacheConfig {
httpClientConfig.cache = check config.cache.ensureType(http:CacheConfig);
}
if config.responseLimits is http:ResponseLimitConfigs {
httpClientConfig.responseLimits = check config.responseLimits.ensureType(http:ResponseLimitConfigs);
}
if config.secureSocket is http:ClientSecureSocket {
httpClientConfig.secureSocket = check config.secureSocket.ensureType(http:ClientSecureSocket);
}
if config.proxy is http:ProxyConfig {
httpClientConfig.proxy = check config.proxy.ensureType(http:ProxyConfig);
}
}
http:Client httpEp = check new (serviceUrl, httpClientConfig);
self.clientEp = httpEp;
return;
}
# Retrieves a PaymentMethod object.
#
# + payment_method - Payment Method
# + headers - Headers to be sent with the request
# + queries - Queries to be sent with the request
# + return - Successful response.
remote isolated function getPaymentMethodsPaymentMethod(string payment_method, GetPaymentMethodsPaymentMethodHeaders headers, *GetPaymentMethodsPaymentMethodQueries queries) returns json|error {
string resourcePath = string `/v1/payment_methods/${getEncodedUri(payment_method)}`;
resourcePath = resourcePath + check getPathForQueryParam(queries);
map httpHeaders = http:getHeaderMap(headers);
return self.clientEp->get(resourcePath, httpHeaders);
}
# Creates a new customer object.
#
# + customer - Customer ID
# + headers - Headers to be sent with the request
# + payload - Customer Details
# + return - Successful response.
remote isolated function postCustomers(string customer, customer_customer_body payload, map headers = {}) returns customer|error {
string resourcePath = string `/v1/customer/${getEncodedUri(customer)}`;
http:Request request = new;
map requestBodyEncoding = {"address": {style: DEEPOBJECT, explode: true}};
string encodedRequestBody = createFormURLEncodedRequestBody(payload, requestBodyEncoding);
request.setPayload(encodedRequestBody, "application/x-www-form-urlencoded");
return self.clientEp->post(resourcePath, request, headers);
}
}
================================================
FILE: ballerina-test/src/test/resources/openapi/expected/filtered_tags.bal
================================================
// AUTO-GENERATED FILE.
// This file is auto-generated by the Ballerina OpenAPI tool.
import ballerina/http;
listener http:Listener ep0 = new (80, config = {host: "petstore.openapi.io"});
service /v1 on ep0 {
# List all pets
#
# + 'limit - How many items to return at one time (max 100)
# + return - returns can be any of following types
# http:Ok (An paged array of pets)
# http:DefaultStatusCodeResponse (unexpected error)
resource function get pets(int:Signed32? 'limit) returns Pets|ErrorDefault {
}
}
================================================
FILE: ballerina-test/src/test/resources/openapi/expected/types.bal
================================================
// AUTO-GENERATED FILE. DO NOT MODIFY.
// This file is auto-generated by the Ballerina OpenAPI tool.
import ballerina/constraint;
import ballerina/http;
public type customer_address record {
@constraint:String {maxLength: 5000}
string city?;
@constraint:String {maxLength: 5000}
string country?;
@constraint:String {maxLength: 5000}
string line1?;
@constraint:String {maxLength: 5000}
string line2?;
@constraint:String {maxLength: 5000}
string postal_code?;
@constraint:String {maxLength: 5000}
string state?;
};
# Represents the Headers record for the operation: getPaymentMethodsPaymentMethod
public type GetPaymentMethodsPaymentMethodHeaders record {
# limit of the payment
string X\-LIMIT;
};
# Provides settings related to HTTP/1.x protocol.
public type ClientHttp1Settings record {|
# Specifies whether to reuse a connection for multiple requests
http:KeepAlive keepAlive = http:KEEPALIVE_AUTO;
# The chunking behaviour of the request
http:Chunking chunking = http:CHUNKING_AUTO;
# Proxy server related options
ProxyConfig proxy?;
|};
# Represents the Queries record for the operation: getPaymentMethodsPaymentMethod
public type GetPaymentMethodsPaymentMethodQueries record {
# Payment Method
string payment\ method\ name;
};
# Proxy server configurations to be used with the HTTP client endpoint.
public type ProxyConfig record {|
# Host name of the proxy server
string host = "";
# Proxy server port
int port = 0;
# Proxy server username
string userName = "";
# Proxy server password
@display {label: "", kind: "password"}
string password = "";
|};
# Provides a set of configurations for controlling the behaviours when communicating with a remote HTTP endpoint.
@display {label: "Connection Config"}
public type ConnectionConfig record {|
# Configurations related to client authentication
http:CredentialsConfig auth;
# The HTTP version understood by the client
http:HttpVersion httpVersion = http:HTTP_2_0;
# Configurations related to HTTP/1.x protocol
ClientHttp1Settings http1Settings?;
# Configurations related to HTTP/2 protocol
http:ClientHttp2Settings http2Settings?;
# The maximum time to wait (in seconds) for a response before closing the connection
decimal timeout = 60;
# The choice of setting `forwarded`/`x-forwarded` header
string forwarded = "disable";
# Configurations associated with request pooling
http:PoolConfiguration poolConfig?;
# HTTP caching related configurations
http:CacheConfig cache?;
# Specifies the way of handling compression (`accept-encoding`) header
http:Compression compression = http:COMPRESSION_AUTO;
# Configurations associated with the behaviour of the Circuit Breaker
http:CircuitBreakerConfig circuitBreaker?;
# Configurations associated with retrying
http:RetryConfig retryConfig?;
# Configurations associated with inbound response size limits
http:ResponseLimitConfigs responseLimits?;
# SSL/TLS-related options
http:ClientSecureSocket secureSocket?;
# Proxy server related options
http:ProxyConfig proxy?;
# Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default
boolean validation = true;
# Enables relaxed data binding on the client side. When enabled, `nil` values are treated as optional,
# and absent fields are handled as `nilable` types. Enabled by default.
boolean laxDataBinding = true;
|};
public type customer_customer_body record {
customer_address address?;
# An integer amount in %s that represents the customer's current balance, which affect the customer's future invoices. A negative amount represents a credit that decreases the amount due on an invoice; a positive amount increases the amount due on an invoice.
int balance?;
};
public type customer record {
# The customer's address.
customer_address? address?;
string name?;
};
================================================
FILE: ballerina-test/src/test/resources/openapi/expected/utils.bal
================================================
// AUTO-GENERATED FILE. DO NOT MODIFY.
// This file is auto-generated by the Ballerina OpenAPI tool.
import ballerina/http;
import ballerina/url;
type SimpleBasicType string|boolean|int|float|decimal;
# Represents encoding mechanism details.
type Encoding record {
# Defines how multiple values are delimited
string style = FORM;
# Specifies whether arrays and objects should generate as separate fields
boolean explode = true;
# Specifies the custom content type
string contentType?;
# Specifies the custom headers
map headers?;
};
enum EncodingStyle {
DEEPOBJECT, FORM, SPACEDELIMITED, PIPEDELIMITED
}
final Encoding & readonly defaultEncoding = {};
# Generate client request when the media type is given as application/x-www-form-urlencoded.
#
# + encodingMap - Includes the information about the encoding mechanism
# + anyRecord - Record to be serialized
# + return - Serialized request body or query parameter as a string
isolated function createFormURLEncodedRequestBody(record {|anydata...;|} anyRecord, map encodingMap = {}) returns string {
string[] payload = [];
foreach [string, anydata] [key, value] in anyRecord.entries() {
Encoding encodingData = encodingMap.hasKey(key) ? encodingMap.get(key) : defaultEncoding;
if value is SimpleBasicType {
payload.push(key, "=", getEncodedUri(value.toString()));
} else if value is SimpleBasicType[] {
payload.push(getSerializedArray(key, value, encodingData.style, encodingData.explode));
} else if (value is record {}) {
if encodingData.style == DEEPOBJECT {
payload.push(getDeepObjectStyleRequest(key, value));
} else {
payload.push(getFormStyleRequest(key, value));
}
} else if (value is record {}[]) {
payload.push(getSerializedRecordArray(key, value, encodingData.style, encodingData.explode));
}
payload.push("&");
}
_ = payload.pop();
return string:'join("", ...payload);
}
# Serialize the record according to the deepObject style.
#
# + parent - Parent record name
# + anyRecord - Record to be serialized
# + return - Serialized record as a string
isolated function getDeepObjectStyleRequest(string parent, record {} anyRecord) returns string {
string[] recordArray = [];
foreach [string, anydata] [key, value] in anyRecord.entries() {
if value is SimpleBasicType {
recordArray.push(parent + "[" + key + "]" + "=" + getEncodedUri(value.toString()));
} else if value is SimpleBasicType[] {
recordArray.push(getSerializedArray(parent + "[" + key + "]" + "[]", value, DEEPOBJECT, true));
} else if value is record {} {
string nextParent = parent + "[" + key + "]";
recordArray.push(getDeepObjectStyleRequest(nextParent, value));
} else if value is record {}[] {
string nextParent = parent + "[" + key + "]";
recordArray.push(getSerializedRecordArray(nextParent, value, DEEPOBJECT));
}
recordArray.push("&");
}
_ = recordArray.pop();
return string:'join("", ...recordArray);
}
# Serialize the record according to the form style.
#
# + parent - Parent record name
# + anyRecord - Record to be serialized
# + explode - Specifies whether arrays and objects should generate separate parameters
# + return - Serialized record as a string
isolated function getFormStyleRequest(string parent, record {} anyRecord, boolean explode = true) returns string {
string[] recordArray = [];
if explode {
foreach [string, anydata] [key, value] in anyRecord.entries() {
if value is SimpleBasicType {
recordArray.push(key, "=", getEncodedUri(value.toString()));
} else if value is SimpleBasicType[] {
recordArray.push(getSerializedArray(key, value, explode = explode));
} else if value is record {} {
recordArray.push(getFormStyleRequest(parent, value, explode));
}
recordArray.push("&");
}
_ = recordArray.pop();
} else {
foreach [string, anydata] [key, value] in anyRecord.entries() {
if value is SimpleBasicType {
recordArray.push(key, ",", getEncodedUri(value.toString()));
} else if value is SimpleBasicType[] {
recordArray.push(getSerializedArray(key, value, explode = false));
} else if value is record {} {
recordArray.push(getFormStyleRequest(parent, value, explode));
}
recordArray.push(",");
}
_ = recordArray.pop();
}
return string:'join("", ...recordArray);
}
# Serialize arrays.
#
# + arrayName - Name of the field with arrays
# + anyArray - Array to be serialized
# + style - Defines how multiple values are delimited
# + explode - Specifies whether arrays and objects should generate separate parameters
# + return - Serialized array as a string
isolated function getSerializedArray(string arrayName, anydata[] anyArray, string style = "form", boolean explode = true) returns string {
string key = arrayName;
string[] arrayValues = [];
if anyArray.length() > 0 {
if style == FORM && !explode {
arrayValues.push(key, "=");
foreach anydata i in anyArray {
arrayValues.push(getEncodedUri(i.toString()), ",");
}
} else if style == SPACEDELIMITED && !explode {
arrayValues.push(key, "=");
foreach anydata i in anyArray {
arrayValues.push(getEncodedUri(i.toString()), "%20");
}
} else if style == PIPEDELIMITED && !explode {
arrayValues.push(key, "=");
foreach anydata i in anyArray {
arrayValues.push(getEncodedUri(i.toString()), "|");
}
} else if style == DEEPOBJECT {
foreach anydata i in anyArray {
arrayValues.push(key, "[]", "=", getEncodedUri(i.toString()), "&");
}
} else {
foreach anydata i in anyArray {
arrayValues.push(key, "=", getEncodedUri(i.toString()), "&");
}
}
_ = arrayValues.pop();
}
return string:'join("", ...arrayValues);
}
# Serialize the array of records according to the form style.
#
# + parent - Parent record name
# + value - Array of records to be serialized
# + style - Defines how multiple values are delimited
# + explode - Specifies whether arrays and objects should generate separate parameters
# + return - Serialized record as a string
isolated function getSerializedRecordArray(string parent, record {}[] value, string style = FORM, boolean explode = true) returns string {
string[] serializedArray = [];
if style == DEEPOBJECT {
int arayIndex = 0;
foreach var recordItem in value {
serializedArray.push(getDeepObjectStyleRequest(parent + "[" + arayIndex.toString() + "]", recordItem), "&");
arayIndex = arayIndex + 1;
}
} else {
if !explode {
serializedArray.push(parent, "=");
}
foreach var recordItem in value {
serializedArray.push(getFormStyleRequest(parent, recordItem, explode), ",");
}
}
_ = serializedArray.pop();
return string:'join("", ...serializedArray);
}
# Get Encoded URI for a given value.
#
# + value - Value to be encoded
# + return - Encoded string
isolated function getEncodedUri(anydata value) returns string {
string|error encoded = url:encode(value.toString(), "UTF8");
if encoded is string {
return encoded;
} else {
return value.toString();
}
}
# Generate query path with query parameter.
#
# + queryParam - Query parameter map
# + encodingMap - Details on serialization mechanism
# + return - Returns generated Path or error at failure of client initialization
isolated function getPathForQueryParam(map queryParam, map encodingMap = {}) returns string|error {
map queriesMap = http:getQueryMap(queryParam);
string[] param = [];
if queriesMap.length() > 0 {
param.push("?");
foreach var [key, value] in queriesMap.entries() {
if value is () {
_ = queriesMap.remove(key);
continue;
}
Encoding encodingData = encodingMap.hasKey(key) ? encodingMap.get(key) : defaultEncoding;
if value is SimpleBasicType {
param.push(key, "=", getEncodedUri(value.toString()));
} else if value is SimpleBasicType[] {
param.push(getSerializedArray(key, value, encodingData.style, encodingData.explode));
} else if value is record {} {
if encodingData.style == DEEPOBJECT {
param.push(getDeepObjectStyleRequest(key, value));
} else {
param.push(getFormStyleRequest(key, value, encodingData.explode));
}
} else {
param.push(key, "=", value.toString());
}
param.push("&");
}
_ = param.pop();
}
string restOfPath = string:'join("", ...param);
return restOfPath;
}
================================================
FILE: ballerina-test/src/test/resources/openapi/integration-tests/Ballerina.toml
================================================
[package]
org= "ballerina"
name= "openAPI"
version= "0.1.0"
================================================
FILE: ballerina-test/src/test/resources/openapi/integration-tests/Package.md
================================================
================================================
FILE: ballerina-test/src/test/resources/openapi/integration-tests/testFiles/openapi-validator-off.bal
================================================
import ballerina/http;
import ballerina/log;
import ballerina/openapi;
listener http:Listener ep0 = new (9090, config = {host: "localhost"});
@openapi:ServiceInfo {
contract: "openapi_validator_off.yaml",
failOnErrors: false
}
service /api/v1 on ep0 {
resource function get [string param1]/[string param3](http:Caller caller) returns error? {
string msg = "Hello, " + param1 + " " + param3;
var result = caller->respond(<@untainted>msg);
if (result is error) {
log:printError("Error sending response");
}
}
}
================================================
FILE: ballerina-test/src/test/resources/openapi/integration-tests/testFiles/openapi-validator-on.bal
================================================
import ballerina/http;
import ballerina/log;
import ballerina/openapi;
listener http:Listener ep0 = new(9090, config = {host: "localhost"});
@openapi:ServiceInfo {
contract: "openapi_validator_on.yaml",
failOnErrors: true
}
service /api/v1 on ep0{
resource function get [string param1]/[string param3](http:Caller caller) returns error? {
string msg = "Hello, " + param1 + " " + param3 ;
var result = caller->respond(<@untainted> msg);
if (result is error) {
log:printError("Error sending response", result);
}
}
}
================================================
FILE: ballerina-test/src/test/resources/openapi/integration-tests/testFiles/openapi_validator_off.yaml
================================================
openapi: 3.0.1
info:
title: Openapi validator off
description: test 2 or more uri parameters
version: 1.0.0
servers:
- url: http://localhost/api/v1
paths:
/{param1}/{param2}:
get:
operationId: test2Params
parameters:
- name: param1
in: path
required: true
schema:
type: string
description: param1
- name: param2
in: path
required: true
schema:
type: string
description: param2
responses:
'200':
description: test
================================================
FILE: ballerina-test/src/test/resources/openapi/integration-tests/testFiles/openapi_validator_on.yaml
================================================
openapi: 3.0.1
info:
title: Openapi validator off
description: test 2 or more uri parameters
version: 1.0.0
servers:
- url: http://localhost/api/v1
paths:
/{param1}/{param2}:
get:
operationId: test2Params
parameters:
- name: param1
in: path
required: true
schema:
type: string
description: param1
- name: param2
in: path
required: true
schema:
type: string
description: param2
responses:
'200':
description: test
================================================
FILE: ballerina-test/src/test/resources/openapi/openapi_client.yaml
================================================
openapi: 3.0.0
info:
contact:
email: dev-platform@stripe.com
name: Stripe Dev Platform Team
url: https://stripe.com
description: The Stripe REST API. Please see https://stripe.com/docs/api for more
details.
termsOfService: https://stripe.com/us/terms/
title: Stripe API
version: '2020-08-27'
x-stripeSpecFilename: spec3
paths:
"/v1/payment_methods/{payment_method}":
get:
description: "Retrieves a PaymentMethod object."
operationId: GetPaymentMethodsPaymentMethod
tags:
- "Payment_Methods"
security:
- basicAuth: []
parameters:
- in: path
name: payment_method
description: Payment Method
required: true
schema:
maxLength: 5000
type: string
style: simple
- in: query
name: payment method name
description: Payment Method
required: true
schema:
maxLength: 5000
type: string
style: deepObject
- in: header
name: X-LIMIT
description: limit of the payment
required: true
schema:
maxLength: 5000
type: string
style: simple
responses:
'200':
content:
application/json:
schema: {}
description: Successful response.
"/v1/customer/{customer}":
post:
description: "Creates a new customer object."
operationId: PostCustomers
tags:
- "Customers"
parameters:
- in: path
name: customer
description: Customer ID
schema:
type: string
required: true
security:
- basicAuth: []
requestBody:
content:
application/x-www-form-urlencoded:
encoding:
address:
style: deepObject
explode: true
schema:
properties:
address:
$ref: "#/components/schemas/customer_address"
balance:
description: An integer amount in %s that represents the customer's
current balance, which affect the customer's future invoices.
A negative amount represents a credit that decreases the amount
due on an invoice; a positive amount increases the amount due
on an invoice.
type: integer
description: Customer Details
responses:
'200':
content:
application/json:
schema:
"$ref": "#/components/schemas/customer"
description: Successful response.
components:
schemas:
customer_address:
properties:
city:
maxLength: 5000
type: string
country:
maxLength: 5000
type: string
line1:
maxLength: 5000
type: string
line2:
maxLength: 5000
type: string
postal_code:
maxLength: 5000
type: string
state:
maxLength: 5000
type: string
title: optional_fields_address
type: object
customer:
properties:
address:
anyOf:
- "$ref": "#/components/schemas/customer_address"
description: The customer's address.
nullable: true
name:
type: string
type: object
securitySchemes:
basicAuth:
description: 'Basic HTTP authentication. Allowed headers-- Authorization: Basic
| Authorization: Basic '
scheme: basic
type: http
================================================
FILE: ballerina-test/src/test/resources/openapi/petstore.bal
================================================
import ballerina/http;
import ballerina/log;
listener http:Listener helloEp = new (9090);
service /hello on helloEp {
resource function get hi(http:Caller caller) {
http:Response res = new;
res.setPayload("Hello World!");
var result = caller->respond(res);
if (result is error) {
log:printError("Error when responding", err = result.toString());
}
}
}
================================================
FILE: ballerina-test/src/test/resources/openapi/petstore.yaml
================================================
openapi: "3.0.0"
info:
version: 1.0.0
title: OpenApi Petstore
license:
name: MIT
servers:
- url: http://petstore.{host}.io/v1
description: The production API server
variables:
host:
default: openapi
description: this value is assigned by the service provider
- url: https://{subdomain}.swagger.io:{port}/{basePath}
description: The production API server
variables:
subdomain:
default: petstore
description: this value is assigned by the service provider
port:
enum:
- '8443'
- '443'
default: '443'
basePath:
default: v2
tags:
- name: pets
description: Pets Tag
- name: list
description: List Tag
security:
- petstore_auth:
- write:pets
- read:pets
- user_auth:
- read:user
paths:
/pets:
get:
summary: List all pets
description: Show a list of pets in the system
operationId: listPets
tags:
- pets
- list
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: An paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create pet
operationId: createPets
tags:
- pets
requestBody:
content:
application/json:
schema: {}
responses:
'202':
description: Accepted
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
type:
type: string
Dog:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
properties:
bark:
type: boolean
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
================================================
FILE: ballerina-test/src/test/resources/openapi/petstoreTags.yaml
================================================
openapi: "3.0.0"
info:
version: 1.0.0
title: OpenApi Petstore
license:
name: MIT
servers:
- url: http://petstore.{host}.io/v1
description: The production API server
variables:
host:
default: openapi
description: this value is assigned by the service provider
- url: https://{subdomain}.swagger.io:{port}/{basePath}
description: The production API server
variables:
subdomain:
default: petstore
description: this value is assigned by the service provider
port:
enum:
- '8443'
- '443'
default: '443'
basePath:
default: v2
tags:
- name: pets
description: Pets Tag
- name: list
description: List Tag
security:
- petstore_auth:
- write:pets
- read:pets
- user_auth:
- read:user
paths:
/pets:
get:
summary: List all pets
description: Show a list of pets in the system
operationId: listPets
tags:
- list
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: An paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
requestBody:
content:
application/json:
schema: {}
responses:
'202':
description: Accepted
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
type:
type: string
Dog:
allOf:
- $ref: "#/components/schemas/Pet"
- type: object
properties:
bark:
type: boolean
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
================================================
FILE: ballerina-test/src/test/resources/testing-line-length.xml
================================================
================================================
FILE: ballerina-test/src/test/resources/testng.xml
================================================
================================================
FILE: ballerina-test-automation/build.gradle
================================================
/*
* 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.
*
*/
apply plugin: 'java'
jar {
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
group 'io.ballerina'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
repositories {
mavenLocal()
maven {
url = 'http://maven.wso2.org/nexus/content/repositories/releases/'
}
maven {
url = 'http://maven.wso2.org/nexus/content/repositories/snapshots/'
}
maven {
url = 'http://maven.wso2.org/nexus/content/groups/wso2-public/'
}
maven {
url = 'http://repo.maven.apache.org/maven2'
}
}
buildScan {
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
}
================================================
FILE: ballerina-test-automation/gradle.properties
================================================
swan-lake-latest-version=swan-lake-2201.13.0
swan-lake-latest-spec-version=2024R1
swan-lake-latest-version-display-text=2201.13.0
swan-lake-latest-tool-version=1.5.1
latest-tool-version=1.5.1
1-x-channel-latest-version=1.2.13
1-x-channel-latest-spec-version=2020R1
================================================
FILE: ballerina-test-automation/gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/../gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: ballerina-test-automation/gradlew.bat
================================================
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\..\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: ballerina-test-automation/installer-test/build.gradle
================================================
/*
* Copyright (c) 2021, 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'
}
group 'io.ballerina'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
dependencies {
implementation project(':test-automation')
implementation 'org.testng:testng:6.14.3'
testImplementation 'org.testng:testng'
implementation 'com.googlecode.json-simple:json-simple:1.1.1'
}
repositories {
mavenCentral()
}
configurations {
testClasses {
extendsFrom(testRuntimeOnly)
}
}
task testJar(type: Jar) {
from sourceSets.test.output
}
// add the jar generated by the testJar task to the testClasses dependency
artifacts {
testClasses testJar
}
test {
useTestNG()
options.suites('src/test/resources/testng.xml')
systemProperty 'BALLERINA_VERSION', project.properties['swan-lake-latest-version']
systemProperty 'SPEC_VERSION', project.properties['swan-lake-latest-spec-version']
systemProperty 'VERSION_DISPLAY_TEXT', project.properties['swan-lake-latest-version-display-text']
systemProperty 'TOOL_VERSION', project.properties['swan-lake-latest-tool-version']
systemProperty 'LATEST_TOOL_VERSION', project.properties['latest-tool-version']
systemProperty 'LATEST_PATCH_VERSION', project.properties['1-x-channel-latest-version']
systemProperty 'LATEST_PATCH_SPEC_VERSION', project.properties['1-x-channel-latest-spec-version']
if (System.getProperty('ballerinaInstalled')) {
systemProperty 'BALLERINA_INSTALLED', System.getProperty('ballerinaInstalled')
} else {
systemProperty 'BALLERINA_INSTALLED', "false"
}
testLogging {
showStandardStreams = true
}
}
================================================
FILE: ballerina-test-automation/installer-test/src/test/java/io/ballerina/installer/test/CentralTest.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.installer.test;
import io.ballerina.test.Executor;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class CentralTest {
String version = System.getProperty("BALLERINA_VERSION");
String toolVersion = System.getProperty("TOOL_VERSION");
@DataProvider(name = "getExecutors")
public Object[][] dataProviderMethod() {
Executor[][] result = new Executor[1][1];
result[0][0] = TestUtils.getExecutor(version);
return result;
}
@Test(dataProvider = "getExecutors")
public void testPull(Executor executor) {
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.transferArtifacts();
executor.install();
}
//Checks part as output varies depending on the network speed
Assert.assertTrue(executor.executeCommand("pull ballerinax/googleapis_sheets", false, toolVersion)
.contains("pulled from central successfully"));
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.uninstall();
executor.cleanArtifacts();
}
}
}
================================================
FILE: ballerina-test-automation/installer-test/src/test/java/io/ballerina/installer/test/InstallerTest.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.installer.test;
import io.ballerina.test.Executor;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class InstallerTest {
String version = System.getProperty("BALLERINA_VERSION");
String specVersion = System.getProperty("SPEC_VERSION");
String toolVersion = System.getProperty("TOOL_VERSION");
@DataProvider(name = "getExecutors")
public Object[][] dataProviderMethod() {
Executor[][] result = new Executor[1][1];
result[0][0] = TestUtils.getExecutor(version);
return result;
}
@Test(dataProvider = "getExecutors")
public void testSmoke(Executor executor) {
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.transferArtifacts();
executor.install();
}
TestUtils.testInstallation(executor, version, specVersion, toolVersion, System.getProperty("VERSION_DISPLAY_TEXT"));
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.uninstall();
executor.cleanArtifacts();
}
}
}
================================================
FILE: ballerina-test-automation/installer-test/src/test/java/io/ballerina/installer/test/ProjectTest.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.installer.test;
import io.ballerina.test.Executor;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class ProjectTest {
String version = System.getProperty("BALLERINA_VERSION");
String specVersion = System.getProperty("SPEC_VERSION");
String toolVersion = System.getProperty("TOOL_VERSION");
@DataProvider(name = "getExecutors")
public Object[][] dataProviderMethod() {
Executor[][] result = new Executor[1][1];
result[0][0] = TestUtils.getExecutor(version);
return result;
}
@Test(dataProvider = "getExecutors")
public void testProject(Executor executor) throws InterruptedException {
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.transferArtifacts();
executor.install();
}
TestUtils.testInstallation(executor, version, specVersion, toolVersion,
System.getProperty("VERSION_DISPLAY_TEXT"));
TestUtils.testProject(executor, version, specVersion, toolVersion);
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.uninstall();
executor.cleanArtifacts();
}
}
@Test(dataProvider = "getExecutors")
public void testBBEs(Executor executor) throws InterruptedException {
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.transferArtifacts();
executor.install();
}
TestUtils.testBBEs(executor, version, specVersion, toolVersion);
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.uninstall();
executor.cleanArtifacts();
}
}
@Test(dataProvider = "getExecutors")
public void testDirectoryPath(Executor executor) throws InterruptedException {
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.transferArtifacts();
executor.install();
}
TestUtils.testDirectoryPath(executor, toolVersion);
if (!System.getProperty("BALLERINA_INSTALLED").equals("true")) {
executor.uninstall();
executor.cleanArtifacts();
}
}
}
================================================
FILE: ballerina-test-automation/installer-test/src/test/java/io/ballerina/installer/test/TestUtils.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.installer.test;
import io.ballerina.test.CentOS;
import io.ballerina.test.Executor;
import io.ballerina.test.MacOS;
import io.ballerina.test.Ubuntu;
import io.ballerina.test.Utils;
import io.ballerina.test.Windows;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.testng.Assert;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class TestUtils {
private static final String OS = System.getProperty("os.name").toLowerCase(Locale.getDefault());
private static final String SWAN_LAKE_KEYWORD = "swan-lake";
private static final String VERSION_DISPLAY_TEXT = System.getProperty("VERSION_DISPLAY_TEXT");
/**
* Get version output for version command.
*
* @param jBallerinaVersion Installed jBallerina version
* @param specVersion Installed language specification
* @param toolVersion Installed tool version
* @param versionDisplayText display text for installed jBallerina version
* @return version output
*/
public static String getVersionOutput(String jBallerinaVersion, String specVersion, String toolVersion,
String versionDisplayText) {
String toolText = TestUtils.isOldToolVersion(toolVersion) ? "Ballerina tool" : "Update Tool";
if (jBallerinaVersion.contains(TestUtils.SWAN_LAKE_KEYWORD)) {
String shortVersion = jBallerinaVersion.split("-")[jBallerinaVersion.split("-").length - 1];
int minorVersion = Integer.parseInt(shortVersion.split("\\.")[1]);
String updateVersionText = minorVersion > 0 ? " Update " + minorVersion : "";
return "Ballerina " + versionDisplayText + " (Swan Lake" + updateVersionText + ")\nLanguage specification "
+ specVersion + "\n" + toolText + " " + toolVersion + "\n";
}
String ballerinaReference = isSupportedRelease(jBallerinaVersion) ? "jBallerina" : "Ballerina";
return ballerinaReference + " " + versionDisplayText + System.lineSeparator() + "Language specification "
+ specVersion + System.lineSeparator() + toolText + " " + toolVersion + System.lineSeparator();
}
public static Executor getExecutor(String version) {
Executor executor;
if (OS.contains("win")) {
executor = new Windows(version);
} else if (OS.contains("mac")) {
executor = new MacOS(version);
} else {
String provider = System.getenv("OS_TYPE");
if (provider != null && provider.equalsIgnoreCase("centos")) {
executor = new CentOS(version);
} else {
executor = new Ubuntu(version);
}
}
return executor;
}
public static void testDistCommands(Executor executor, String version, String specVersion, String toolVersion,
String previousVersion, String previousSpecVersion,
String previousVersionsLatestPatch, String latestToolVersion)
throws InterruptedException {
//Test installation
TestUtils.testInstallation(executor, version, specVersion, toolVersion, VERSION_DISPLAY_TEXT);
//Test `ballerina dist list`
String actualOutput = executor.executeCommand("dist list", false, toolVersion);
Assert.assertTrue(actualOutput.contains("1.0.0"));
Assert.assertTrue(actualOutput.contains("1.1.0"));
Assert.assertTrue(actualOutput.contains("1.2.0"));
Assert.assertTrue(actualOutput.contains("slp1"));
//Test `ballerina dist pull`
executor.executeCommand("dist pull "
+ TestUtils.getSupportedVersion(toolVersion, previousVersion), true, toolVersion);
Thread.sleep(10000);
TestUtils.testInstallation(executor, previousVersion, previousSpecVersion, toolVersion, previousVersion);
//Test Update notification message
if (isSupportedRelease(previousVersion)) {
//TODO : This is a bug and have fixed in the update tool. Need to update here once new version is released.
String expectedOutput = "A new version of Ballerina is available: jballerina-" + previousVersionsLatestPatch
+ "\nUse 'ballerina dist pull jballerina-" + previousVersionsLatestPatch
+ "' to download and use the distribution\n\n";
// Assert.assertEquals(executor.executeCommand("ballerina build help", false), expectedOutput);
}
//Test `ballerina dist use`
executor.executeCommand("dist use " + TestUtils.getSupportedVersion(toolVersion, version), true,
toolVersion);
//Verify the the installation
TestUtils.testInstallation(executor, version, specVersion, toolVersion, VERSION_DISPLAY_TEXT);
//Test `ballerina dist update`
executor.executeCommand("dist use " + TestUtils.getSupportedVersion(toolVersion, previousVersion),
true, toolVersion);
executor.executeCommand("dist remove " + TestUtils.getSupportedVersion(toolVersion, version), true,
toolVersion);
//TODO: Temporary attempt
executor.executeCommand("update", true, toolVersion);
executor.executeCommand("dist update", true, latestToolVersion);
Thread.sleep(10000);
TestUtils.testInstallation(executor, previousVersionsLatestPatch, previousSpecVersion, latestToolVersion,
previousVersionsLatestPatch);
//Try `ballerina dist remove`
executor.executeCommand("dist remove " + TestUtils.getSupportedVersion(toolVersion, previousVersion),
true, latestToolVersion);
}
/**
* Execute smoke testing to verify installation.
*
* @param executor Executor for relevant operating system
* @param version Installed jBallerina version
* @param specVersion Installed language specification
* @param toolVersion Installed tool version
*/
public static void testInstallation(Executor executor, String version, String specVersion, String toolVersion,
String versionDisplayText) {
String versionOutput = executor.executeCommand("-v", false, toolVersion);
String cleanedVersionOutput = versionOutput.replaceAll("(\\d+\\.\\d+\\.\\d+)-\\d{8}-\\d{6}-[a-fA-F0-9]+", "$1");
Assert.assertEquals(cleanedVersionOutput,
TestUtils.getVersionOutput(version, specVersion, toolVersion, versionDisplayText));
}
/**
* Execute smoke testing to verify fetching dependencies.
*
* @param executor Executor for relevant operating system
* @param toolVersion Installed tool version
*/
public static void testDependencyFetch(Executor executor, String toolVersion) throws InterruptedException {
String cmdName = Utils.getCommandName(toolVersion);
Path userDir = Paths.get(System.getProperty("user.dir"));
executor.executeCommand("dist list", false, toolVersion);
executor.executeCommand("new project1 && cd project1 &&" + cmdName + "add module1 && " +
cmdName + "build", false, toolVersion);
Path projectPath = userDir.resolve("project1");
Assert.assertTrue(Files.isDirectory(projectPath));
Assert.assertTrue(Files.isDirectory(projectPath.resolve("modules").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath.resolve("target/bin/project1.jar")));
//Test `Fetching compatible JRE dependency`
String output = executor.executeCommand("dist pull slp1", true, toolVersion);
Thread.sleep(10000);
Assert.assertTrue(output.contains("Downloading slp1"));
Assert.assertTrue(output.contains("Fetching the dependencies for 'slp1' from the remote server..."));
Assert.assertTrue(output.contains("jdk8u202-b08-jre"));
Assert.assertTrue(output.contains("'slp1' successfully set as the active distribution"));
TestUtils.testInstallation(executor, "swan-lake-preview1", "v2020-06-18", toolVersion, "Preview 1");
executor.executeCommand("new project2 && cd project2 && " + cmdName + "add module1 && " +
cmdName + "build module1", false, toolVersion);
projectPath = userDir.resolve("project2");
Assert.assertTrue(Files.isDirectory(projectPath));
Assert.assertTrue(Files.isDirectory(projectPath.resolve("src").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath.resolve("target/bin/module1.jar")));
output = executor.executeCommand("dist pull 1.2.10", true, toolVersion);
Thread.sleep(10000);
Assert.assertTrue(output.contains("Downloading 1.2.10"));
Assert.assertTrue(output.contains("Fetching the dependencies for '1.2.10' from the remote server..."));
Assert.assertTrue(output.contains("jdk8u265-b01-jre"));
Assert.assertTrue(output.contains("'1.2.10' successfully set as the active distribution"));
TestUtils.testInstallation(executor, "1.2.10", "2020R1", toolVersion, "1.2.10");
executor.executeCommand("new project3 && cd project3 &&" + cmdName + "add module1 && " +
cmdName + "build module1", false, toolVersion);
projectPath = userDir.resolve("project3");
Assert.assertTrue(Files.isDirectory(projectPath));
Assert.assertTrue(Files.isDirectory(projectPath.resolve("src").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath.resolve("target/bin/module1.jar")));
}
/**
* Execute smoke testing to verify dist list.
*
* @param executor Executor for relevant operating system
*/
public static void verifyDistList(Executor executor, String toolVersion) {
String actualOutput = executor.executeCommand("dist list", false, toolVersion);
Assert.assertTrue(actualOutput.contains("1.0.0"));
Assert.assertTrue(actualOutput.contains("1.1.0"));
Assert.assertTrue(actualOutput.contains("1.2.0"));
Assert.assertTrue(actualOutput.contains("slp1"));
}
/**
* To check whether installation is a 1.0.x release.
*
* @return returns is a 1.0.x release
*/
public static boolean isSupportedRelease(String version) {
if (version.contains(TestUtils.SWAN_LAKE_KEYWORD)) {
return true;
}
String[] versions = version.split("\\.");
return !(versions[0].equals("1") && versions[1].equals("0"));
}
/**
* To check whether older tool version before swan lake support
*
* @param toolVersion
* @return returns is a older version
*/
public static boolean isOldToolVersion(String toolVersion) {
return toolVersion.equals("0.8.5") || toolVersion.equals("0.8.0");
}
/**
* Test project and module creation.
*
* @param executor Executor for relevant operating system
* @param previousVersion Ballerina version to be installed
* @param previousSpecVersion Installed language specification
* @param toolVersion Installed tool version
*/
public static void testProject(Executor executor, String previousVersion, String previousSpecVersion,
String toolVersion) throws InterruptedException {
String cmdName = Utils.getCommandName(toolVersion);
executor.executeCommand("new sampleProject1 && cd sampleProject1 && " + cmdName + "add module1 && " +
cmdName + "build", false, toolVersion);
Path userDir = Paths.get(System.getProperty("user.dir"));
Path projectPath = userDir.resolve("sampleProject1");
Assert.assertTrue(Files.exists(projectPath));
Assert.assertTrue(Files.isDirectory(projectPath.resolve("modules").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath.resolve("target/bin/sampleProject1.jar")));
/*
executor.executeCommand("dist pull " + previousVersion, true, toolVersion);
Thread.sleep(10000);
testInstallation(executor, previousVersion, previousSpecVersion, toolVersion, previousVersion);
executor.executeCommand("new sampleProject2 && cd sampleProject2 && " + cmdName + "add module1 && " +
cmdName + "build module1", false, toolVersion);
projectPath = userDir.resolve("sampleProject2");
Assert.assertTrue(Files.exists(projectPath));
Assert.assertTrue(Files.isDirectory(projectPath.resolve("src").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath.resolve("target/bin/module1.jar")));
*/
}
public static void testBBEs(Executor executor, String previousVersion, String previousSpecVersion,
String toolVersion) throws InterruptedException {
Path userDir = Paths.get(System.getProperty("user.dir"));
Path bbeExamplesPath = userDir.resolve("../../examples");
Path bbeJsonFilePath = bbeExamplesPath.resolve("index.json");
String cmdName = Utils.getCommandName(toolVersion);
List bbeTests = new ArrayList<>();
JSONParser jsonParser = new JSONParser();
try (FileReader reader = new FileReader(bbeJsonFilePath.toString())) {
JSONArray bbeTestData = (JSONArray) jsonParser.parse(reader);
bbeTestData.forEach(testGroup -> {
JSONObject testGroupJsonObject = (JSONObject) testGroup;
JSONArray tests = (JSONArray) testGroupJsonObject.get("samples");
tests.forEach(test -> {
JSONObject testJsonObject = (JSONObject) test;
if (testJsonObject.get("verifyBuild").equals(true) &&
testJsonObject.get("verifyOutput").equals(true)) {
bbeTests.add((String) testJsonObject.get("url"));
}
});
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
bbeTests.forEach(testName -> {
Path balFilePath = bbeExamplesPath.resolve(testName);
executor.executeCommand("version && cd " + balFilePath.toString() + " && " + cmdName + "init && " +
cmdName + "build", false, toolVersion);
Assert.assertTrue(Files.exists(balFilePath));
Assert.assertTrue(Files.exists(balFilePath.resolve("target").resolve("bin").
resolve(testName.replaceAll("-", "_") + ".jar")));
});
}
private static String getSupportedVersion(String toolVersion, String version) {
if (TestUtils.isOldToolVersion(toolVersion)) {
return "jballerina-" + version;
}
if (version.contains(TestUtils.SWAN_LAKE_KEYWORD)) {
if (version.contains("alpha") || version.contains("beta")) {
return "sl" + version.split("-")[0];
} else if (version.contains("preview")) {
return "slp" + version.replace("swan-lake-preview", "");
}
return version.split("-")[0];
}
return version;
}
public static void testDirectoryPath(Executor executor, String toolVersion) throws InterruptedException {
String cmdName = Utils.getCommandName(toolVersion);
executor.executeCommand("version && mkdir \"test space1\" && cd \"test space1\" && " + cmdName +
"new sampleProject1 && cd sampleProject1 && " + cmdName + "add module1 && " +
cmdName + "build", false, toolVersion);
Path userDir = Paths.get(System.getProperty("user.dir"));
Path projectPath1 = userDir.resolve("test space1").resolve("sampleProject1");
Assert.assertTrue(Files.exists(projectPath1));
Assert.assertTrue(Files.isDirectory(projectPath1.resolve("modules").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath1.resolve("target/bin/sampleProject1.jar")));
executor.executeCommand("version && mkdir \"test space2\" && cd \"test space2\" && " + cmdName +
"new sampleProject2 && cd sampleProject2 && " + cmdName + "add module1", false, toolVersion);
executor.executeCommand("version && " + cmdName + "build \"test space2/sampleProject2\"", false, toolVersion);
Path projectPath2 = userDir.resolve("test space2").resolve("sampleProject2");
Assert.assertTrue(Files.exists(projectPath2));
Assert.assertTrue(Files.isDirectory(projectPath2.resolve("modules").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath2.resolve("target/bin/sampleProject2.jar")));
executor.executeCommand("version && mkdir \"test space3\" && cd \"test space3\" && " + cmdName +
"new \"sample project3\" && cd \"sample project3\" && " + cmdName + "add module1", false, toolVersion);
executor.executeCommand("version && cd \"test space3\" && " + cmdName + "build \"sample project3\"",
false, toolVersion);
Path projectPath3 = userDir.resolve("test space3").resolve("sample project3");
Assert.assertTrue(Files.exists(projectPath3));
Assert.assertTrue(Files.isDirectory(projectPath3.resolve("modules").resolve("module1")));
Assert.assertTrue(Files.exists(projectPath3.resolve("target/bin/sample_project3.jar")));
}
}
================================================
FILE: ballerina-test-automation/installer-test/src/test/resources/testng.xml
================================================
================================================
FILE: ballerina-test-automation/settings.gradle
================================================
/*
* Copyright (c) 2021, 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.
*
*/
rootProject.name = 'ballerina-test-automation'
include 'test-automation'
include 'installer-test'
include 'update-tool-test'
project(':test-automation').projectDir = "$rootDir/test-automation" as File
project(':installer-test').projectDir = "$rootDir/installer-test" as File
project(':update-tool-test').projectDir = "$rootDir/update-tool-test" as File
================================================
FILE: ballerina-test-automation/test-automation/build.gradle
================================================
/*
* Copyright (c) 2021, 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'
}
group 'io.ballerina'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
dependencies {
implementation 'org.testng:testng:6.14.3'
testImplementation 'org.testng:testng'
}
repositories {
mavenCentral()
}
================================================
FILE: ballerina-test-automation/test-automation/src/main/java/io/ballerina/test/CentOS.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.test;
public class CentOS implements Executor {
private String packageName;
private String installerName;
private String version;
public CentOS(String version) {
this.version = version;
installerName = "ballerina-linux-x64-" + version + ".rpm";
packageName = "ballerina-" + version;
}
@Override
public String transferArtifacts() {
Utils.downloadFile(version, installerName);
return Utils.executeCommand("cp " + installerName + " ~");
}
@Override
public String install() {
return Utils.executeCommand("sudo rpm -i ~/" + installerName);
}
@Override
public String executeCommand(String command, boolean isAdminMode, String toolVersion) {
String sudoCommand = isAdminMode ? "sudo " : "";
String ballerinaStagingUpdate = Utils.BALLERINA_STAGING_UPDATE ? "BALLERINA_STAGING_UPDATE=true " : "";
return Utils.executeCommand(sudoCommand + ballerinaStagingUpdate + Utils.getCommandName(toolVersion) + command);
}
@Override
public String uninstall() {
return Utils.executeCommand("sudo rpm -e " + packageName);
}
@Override
public String cleanArtifacts() {
return Utils.executeCommand("rm ~/" + installerName + " && rm -rf ~/.ballerina");
}
}
================================================
FILE: ballerina-test-automation/test-automation/src/main/java/io/ballerina/test/Executor.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.test;
public interface Executor {
String transferArtifacts();
String install();
String executeCommand(String command, boolean isAdminMode, String toolVersion);
String uninstall();
String cleanArtifacts();
}
================================================
FILE: ballerina-test-automation/test-automation/src/main/java/io/ballerina/test/MacOS.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.test;
public class MacOS implements Executor {
private String installerName;
private String version;
public MacOS(String version) {
this.version = version;
this.installerName = "ballerina-macos-x64-" + version + ".pkg";
}
@Override
public String transferArtifacts() {
Utils.downloadFile(version, installerName);
return Utils.executeCommand("cp " + installerName + " ~");
}
@Override
public String install() {
return Utils.executeCommand("sudo installer -pkg ~/" + installerName + " -target /");
}
@Override
public String executeCommand(String command, boolean isAdminMode, String toolVersion) {
String sudoCommand = isAdminMode ? "sudo " : "";
Utils.executeCommand("sudo chmod 755 /Library/Ballerina/bin/bal");
String exportCmd = "export BAL_HOME=/Library/Ballerina && export PATH=$PATH:$BAL_HOME/bin && ";
String ballerinaStagingUpdate = Utils.BALLERINA_STAGING_UPDATE ? "BALLERINA_STAGING_UPDATE=true " : "";
return Utils.executeCommand(exportCmd + sudoCommand + ballerinaStagingUpdate +
Utils.getCommandName(toolVersion) + command);
}
@Override
public String uninstall() {
return Utils.executeCommand("sudo rm -rf /Library/Ballerina/");
}
@Override
public String cleanArtifacts() {
return Utils.executeCommand("rm ~/" + installerName + " && sudo rm -rf ~/.ballerina");
}
}
================================================
FILE: ballerina-test-automation/test-automation/src/main/java/io/ballerina/test/Ubuntu.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.test;
public class Ubuntu implements Executor {
private String packageName;
private String installerName;
private String version;
public Ubuntu(String version) {
this.version = version;
this.installerName = "ballerina-linux-x64-" + version + ".deb";
this.packageName = "ballerina-" + version;
}
@Override
public String transferArtifacts() {
Utils.downloadFile(version, installerName);
return Utils.executeCommand("cp " + installerName + " ~");
}
@Override
public String install() {
return Utils.executeCommand("sudo dpkg -i ~/" + installerName);
}
@Override
public String executeCommand(String command, boolean isAdminMode, String toolVersion) {
String sudoCommand = isAdminMode ? "sudo " : "";
String ballerinaStagingUpdate = Utils.BALLERINA_STAGING_UPDATE ? "BALLERINA_STAGING_UPDATE=true " : "";
return Utils.executeCommand(sudoCommand + ballerinaStagingUpdate + Utils.getCommandName(toolVersion) + command);
}
@Override
public String uninstall() {
return Utils.executeCommand("sudo apt-get --yes --force-yes remove " + packageName);
}
@Override
public String cleanArtifacts() {
return Utils.executeCommand("rm ~/" + installerName + " && sudo rm -rf ~/.ballerina");
}
}
================================================
FILE: ballerina-test-automation/test-automation/src/main/java/io/ballerina/test/Utils.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.test;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Timestamp;
import java.util.Locale;
public class Utils {
private static final String OS = System.getProperty("os.name").toLowerCase(Locale.getDefault());
public static final boolean BALLERINA_STAGING_UPDATE = Boolean.parseBoolean(
System.getenv("BALLERINA_STAGING_UPDATE"));
public static final PrintStream OUT = System.out;
public static final String DISTRIBUTION_LOCATION = "https://dist-dev.ballerina.io/downloads/";
public static void downloadFile(String version, String installerName) {
OUT.println("Downloading " + installerName);
try {
String destination = getUserHome();
File output = new File(destination + File.separator + installerName);
if (!output.exists()) {
HttpURLConnection conn = (HttpURLConnection) new URL(
DISTRIBUTION_LOCATION + version + "/" + installerName).openConnection();
conn.setRequestProperty("content-type", "binary/data");
try (InputStream in = conn.getInputStream();
FileOutputStream out = new FileOutputStream(output)) {
byte[] b = new byte[1024];
int count;
while ((count = in.read(b)) > 0) {
out.write(b, 0, count);
}
} catch (IOException e) {
OUT.println(e);
}
}
} catch (Exception e) {
OUT.println("Error occurred while downloading installer: " + e.getMessage());
}
}
public static String executeCommand(String command) {
OUT.println("Executing: " + command);
String output = "";
try {
ProcessBuilder processBuilder;
if (isWindows()) {
processBuilder = new ProcessBuilder("cmd", "/c", command);
} else {
processBuilder = new ProcessBuilder("bash", "-c", command);
}
Process process = processBuilder.start();
int exitCode = process.waitFor();
InputStream inputStream = process.getInputStream();
if (exitCode != 0) {
inputStream = process.getErrorStream();
}
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
output += line + "\n";
}
if (isWindows() && output.isEmpty()) {
inputStream = process.getErrorStream();
reader = new BufferedReader(new InputStreamReader(inputStream));
while ((line = reader.readLine()) != null) {
output += line + "\n";
}
}
OUT.println(output);
} catch (IOException | InterruptedException e) {
OUT.println("Error occurred while executing " + command + ": " + e.getMessage());
}
return output;
}
private static boolean isUnix() {
return OS.contains("nix") || OS.contains("nux") || OS.contains("aix");
}
private static boolean isWindows() {
return OS.contains("win");
}
/**
* Provide user home directory based on command.
*
* @return user home directory
*/
public static String getUserHome() {
String userHome = System.getenv("HOME");
if (isUnix() && userHome.contains("root")) {
userHome = "/home/" + System.getenv("SUDO_USER");
}
if (userHome == null) {
userHome = System.getProperty("user.home");
}
return userHome;
}
/**
* Get the command name(ballerina or bal)
*
* @param toolVersion
* @return returns the command name
*/
public static String getCommandName(String toolVersion) {
String[] version = toolVersion.split("\\.");
//command will be ballerina if update tool version is less than 0.8.10.
return version[0].equals("0") && Integer.parseInt(version[2]) <= 10 ? "ballerina " : "bal ";
}
}
================================================
FILE: ballerina-test-automation/test-automation/src/main/java/io/ballerina/test/Windows.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.test;
public class Windows implements Executor {
private String installerName;
private String version;
public Windows(String version) {
this.version = version;
installerName = "ballerina-windows-x64-" + version + ".msi";
}
@Override
public String transferArtifacts() {
Utils.downloadFile(version, installerName);
return "";
}
@Override
public String install() {
return Utils.executeCommand("msiexec /i " + Utils.getUserHome() + "\\" + installerName
+ " /qn /l \"install-log.log\"");
}
@Override
public String executeCommand(String command, boolean isAdminMode, String toolVersion) {
return Utils.executeCommand(Utils.getCommandName(toolVersion) + command);
}
@Override
public String uninstall() {
return Utils.executeCommand("wmic product where name=\"Ballerina " + version + "\" call uninstall/nointeractive");
}
@Override
public String cleanArtifacts() {
return Utils.executeCommand("rmdir /Q /S " + Utils.getUserHome() + "\\.ballerina");
}
}
================================================
FILE: ballerina-test-automation/update-tool-test/build.gradle
================================================
/*
* Copyright (c) 2021, 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'
}
group 'io.ballerina'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
dependencies {
implementation project(':test-automation')
testImplementation project(path: ':installer-test', configuration: 'testClasses')
implementation 'org.testng:testng:6.14.3'
testImplementation 'org.testng:testng'
}
repositories {
mavenCentral()
}
test {
useTestNG()
options.suites('src/test/resources/testng.xml')
systemProperty 'BALLERINA_VERSION', project.properties['swan-lake-latest-version']
systemProperty 'SPEC_VERSION', project.properties['swan-lake-latest-spec-version']
systemProperty 'VERSION_DISPLAY_TEXT', project.properties['swan-lake-latest-version-display-text']
systemProperty 'TOOL_VERSION', project.properties['swan-lake-latest-tool-version']
systemProperty 'LATEST_TOOL_VERSION', project.properties['latest-tool-version']
systemProperty 'LATEST_PATCH_VERSION', project.properties['1-x-channel-latest-version']
systemProperty 'LATEST_PATCH_SPEC_VERSION', project.properties['1-x-channel-latest-spec-version']
testLogging {
showStandardStreams = true
}
}
================================================
FILE: ballerina-test-automation/update-tool-test/src/test/java/io/ballerina/tool/test/FetchDependencyTest.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.tool.test;
import io.ballerina.installer.test.TestUtils;
import io.ballerina.test.Executor;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class FetchDependencyTest {
String version = System.getProperty("BALLERINA_VERSION");
String specVersion = System.getProperty("SPEC_VERSION");
String toolVersion = System.getProperty("TOOL_VERSION");
String VERSION_DISPLAY_TEXT = System.getProperty("VERSION_DISPLAY_TEXT");
@DataProvider(name = "getExecutors")
public Object[][] dataProviderMethod() {
Executor[][] result = new Executor[1][1];
result[0][0] = TestUtils.getExecutor(version);
return result;
}
@Test(dataProvider = "getExecutors")
public void testFetchDependency(Executor executor) throws InterruptedException {
executor.transferArtifacts();
executor.install();
TestUtils.testInstallation(executor, version, specVersion, toolVersion, VERSION_DISPLAY_TEXT);
TestUtils.testDependencyFetch(executor, toolVersion);
executor.uninstall();
executor.cleanArtifacts();
}
}
================================================
FILE: ballerina-test-automation/update-tool-test/src/test/java/io/ballerina/tool/test/UpdateDistTest.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.tool.test;
import io.ballerina.installer.test.TestUtils;
import io.ballerina.test.Executor;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class UpdateDistTest {
String version = System.getProperty("BALLERINA_VERSION");
String specVersion = System.getProperty("SPEC_VERSION");
String toolVersion = System.getProperty("TOOL_VERSION");
String latestToolVersion = System.getProperty("LATEST_TOOL_VERSION");
String previousVersion = "1.2.0";
String previousSpecVersion = "2020R1";
String previousVersionsLatestPatch = System.getProperty("LATEST_PATCH_VERSION");
@DataProvider(name = "getExecutors")
public Object[][] dataProviderMethod() {
Executor[][] result = new Executor[1][1];
result[0][0] = TestUtils.getExecutor(version);
return result;
}
@Test(dataProvider = "getExecutors")
public void testDistCommands(Executor executor) throws InterruptedException {
executor.transferArtifacts();
executor.install();
TestUtils.testDistCommands(executor, version, specVersion, toolVersion, previousVersion, previousSpecVersion,
previousVersionsLatestPatch, latestToolVersion);
executor.uninstall();
executor.cleanArtifacts();
}
}
================================================
FILE: ballerina-test-automation/update-tool-test/src/test/java/io/ballerina/tool/test/UpdateToolTest.java
================================================
/*
* Copyright (c) 2020, WSO2 Inc. (http://wso2.com) 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.
*/
package io.ballerina.tool.test;
import io.ballerina.installer.test.TestUtils;
import io.ballerina.test.Executor;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class UpdateToolTest {
String version = System.getProperty("BALLERINA_VERSION");
String specVersion = System.getProperty("SPEC_VERSION");
String toolVersion = System.getProperty("TOOL_VERSION");
String latestToolVersion = System.getProperty("LATEST_TOOL_VERSION");
String VERSION_DISPLAY_TEXT = System.getProperty("VERSION_DISPLAY_TEXT");
String previousVersion = "1.2.0";
String previousSpecVersion = "2020R1";
String previousVersionsLatestPatch = System.getProperty("LATEST_PATCH_VERSION");
@DataProvider(name = "getExecutors")
public Object[][] dataProviderMethod() {
Executor[][] result = new Executor[1][1];
result[0][0] = TestUtils.getExecutor(version);
return result;
}
@Test(dataProvider = "getExecutors")
public void testUpdateTool(Executor executor) throws InterruptedException {
executor.transferArtifacts();
executor.install();
//Test dist list
TestUtils.verifyDistList(executor, toolVersion);
//Test installation
TestUtils.testInstallation(executor, version, specVersion, toolVersion, VERSION_DISPLAY_TEXT);
//Test `ballerina update`
executor.executeCommand("update", true, toolVersion);
TestUtils.testInstallation(executor, version, specVersion, latestToolVersion, VERSION_DISPLAY_TEXT);
//Execute all ballerina dist commands once updated
TestUtils.testDistCommands(executor, version, specVersion, latestToolVersion, previousVersion,
previousSpecVersion, previousVersionsLatestPatch, latestToolVersion);
executor.uninstall();
executor.cleanArtifacts();
}
}
================================================
FILE: ballerina-test-automation/update-tool-test/src/test/resources/testng.xml
================================================
================================================
FILE: build-time-tests/build.gradle
================================================
/*
~ * Copyright (c) 2021, 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.
~ */
description = 'Ballerina - Build Time Tests'
apply plugin: 'java'
ext.offline = "false"
def distPath = "$projectDir/../ballerina/build/target/extracted-distributions/ballerina-${version}"
configurations {
ballerinaDistribution
}
dependencies {
ballerinaDistribution project(path: ":ballerina", configuration: "ballerinaDistribution")
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.12.3'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.3'
}
task copySamples {
copy {
from file("$project.projectDir/samples")
into file("$project.projectDir/build/samples")
}
}
task buildSamples {
dependsOn copySamples
dependsOn configurations.ballerinaDistribution
dependsOn (":ballerina:unzipDistForTests")
doLast {
exec {
workingDir "${buildDir}/samples"
commandLine 'sh', '-c', "bash build-samples.sh $distPath"
}
}
}
task processData(type: JavaExec) {
dependsOn buildSamples
classpath = sourceSets.main.runtimeClasspath
setMain('org.ballerina.packages.buildtime.GenerateBuildDataCsv')
args "$buildDir/../../ballerina/build/distributions"
}
================================================
FILE: build-time-tests/samples/build-samples.sh
================================================
#!/bin/bash
# ---------------------------------------------------------------------------
# Copyright (c) 2021, 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.
# ----------------------------------------------------------------------------
BAL_DIST_PATH=$1
BAL_EXEC_PATH=$BAL_DIST_PATH/bin/bal
ARTEFACT_DIR=../build-time-data
SAMPLES_LIST=$(ls -d */)
mkdir -p "$ARTEFACT_DIR/offline"
mkdir -p "$ARTEFACT_DIR/online"
for sample in $SAMPLES_LIST
do
sample_name=$(basename $sample)
echo "Building sample '$sample'"
$BAL_EXEC_PATH build --dump-build-time $sample
cp "$sample/target/build-time.json" "$ARTEFACT_DIR/online/$sample_name-build-time.json"
done
for sample in $SAMPLES_LIST
do
sample_name=$(basename $sample)
echo "Building sample '$sample' offline"
$BAL_EXEC_PATH build --dump-build-time --offline $sample
cp "$sample/target/build-time.json" "$ARTEFACT_DIR/offline/$sample_name-build-time.json"
done
================================================
FILE: build-time-tests/samples/helloservice/Ballerina.toml
================================================
[build-options]
observabilityIncluded = true
================================================
FILE: build-time-tests/samples/helloservice/service.bal
================================================
import ballerina/http;
import ballerina/io;
# A service representing a network-accessible API
# bound to absolute path `/hello` and port `9090`.
service /hello on new http:Listener(9090) {
# A resource respresenting an invokable API method
# accessible at `/hello/sayHello`.
#
# + caller - the client invoking this resource
# + request - the inbound request
resource function get sayHello(http:Caller caller, http:Request request) {
// Send a response back to the caller.
error? result = caller->respond("Hello Ballerina!");
if (result is error) {
io:println("Error in responding: ", result);
}
}
}
================================================
FILE: build-time-tests/samples/helloworld/Ballerina.toml
================================================
[build-options]
observabilityIncluded = true
================================================
FILE: build-time-tests/samples/helloworld/helloworld.bal
================================================
import ballerina/io;
public function main() {
io:println("Hello World!");
}
================================================
FILE: build-time-tests/src/main/java/org/ballerina/packages/buildtime/GenerateBuildDataCsv.java
================================================
/*
* 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.ballerina.packages.buildtime;
//import com.google.gson.Gson;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Class with a main function to accumulate command generated JSON build time data to a CSV file.
*/
public class GenerateBuildDataCsv {
public static void main(String[] args) throws IOException {
Path artefactsDir = Paths.get(System.getProperty("user.dir"), "build", "build-time-data");
Path outputCsvFile = artefactsDir.resolve("buildTimeData.csv");
List jsonFiles = getJsonFiles(artefactsDir);
CsvSchema.Builder csvSchemaBuilder = CsvSchema.builder();
JsonNode jsonTree = new ObjectMapper().readTree(jsonFiles.get(0).toFile());
csvSchemaBuilder.addColumn("name");
jsonTree.fieldNames().forEachRemaining(fieldName ->
csvSchemaBuilder.addColumn(fieldName, CsvSchema.ColumnType.NUMBER));
CsvSchema csvSchema;
CsvMapper csvMapper = new CsvMapper();
if (!Files.exists(outputCsvFile)) {
// This done to avoid duplication of the header in the CSV file
csvSchema = csvSchemaBuilder.build().withHeader();
csvMapper.writerFor(JsonNode.class).with(csvSchema).writeValue(outputCsvFile.toFile(), null);
}
csvSchema = csvSchemaBuilder.build().withoutHeader();
for (Path jsonFile : jsonFiles) {
JsonNode jsonTree1 = new ObjectMapper().readTree(jsonFile.toFile());
// additionally ass the name of the sample to the record
((ObjectNode)jsonTree1).put("name", jsonFile.getFileName().toString().split("-build-time.json")[0]);
ObjectWriter writer = csvMapper.writerFor(JsonNode.class).with(csvSchema);
try (OutputStream outstream = new FileOutputStream(outputCsvFile.toFile(), true)) {
writer.writeValue(outstream, jsonTree1);
}
}
}
private static List getJsonFiles(Path artefactsDir) throws IOException {
try (Stream pathStream = Files.walk(artefactsDir, 2)) {
return pathStream
.filter(path -> path.getFileName().toString().endsWith(".json"))
.collect(Collectors.toList());
}
}
}
================================================
FILE: build.gradle
================================================
/*
* 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 'base'
id 'maven-publish'
id 'net.researchgate.release' version '2.8.0'
id 'de.undercouch.download' version '5.4.0'
id "com.github.johnrengelman.shadow" version "8.1.1"
id "com.github.spotbugs" version "5.0.14"
}
description = 'Ballerina - Tools - Parent'
ext {
ballerinaLangVersion = project.ballerinaLangVersion
shortVersion = project.version.split("-")[0]
c2cVersion = project.c2cVersion
ballerinaJreVersion = project.ballerinaJreVersion
ballerinaCommandVersion = project.ballerinaCommandVersion
picocliVersion = "4.0.1"
testngVersion = "7.6.1"
netLingalaZip4jVersion = "2.8.0"
commonsIoVersion = "2.6"
commonsLang3Version = "3.9"
lsp4jDebugVersion = "0.7.1"
puppycrawlCheckstyleVersion = "10.12.1"
}
allprojects {
apply plugin: 'maven-publish'
group = project.group
version = project.version
repositories {
mavenCentral()
}
release {
// Workaround to fix build task not found issue in release plugin
buildTasks = []
failOnSnapshotDependencies = true
failOnCommitNeeded = false
versionPropertyFile = 'gradle.properties'
tagTemplate = 'v${System.env.GIT_TAG}'
git {
// To release from any branch
requireBranch = ''
}
}
afterReleaseBuild.dependsOn publishToMavenLocal
afterReleaseBuild.dependsOn publish
}
subprojects {
apply plugin: 'java'
sourceCompatibility = 21
targetCompatibility = 21
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
buildscript {
repositories {
/* Central Repository */
maven { url "https://repo.maven.apache.org/maven2" }
/* CWSO2 Releases Repository */
maven { url "http://maven.wso2.org/nexus/content/repositories/releases/" }
/* WSO2 Snapshot Repository */
maven { url "http://maven.wso2.org/nexus/content/repositories/snapshots/" }
/* WSO2 internal Repository */
maven { url "http://maven.wso2.org/nexus/content/groups/wso2-public/" }
}
}
repositories {
/* local maven repository */
mavenLocal()
/* Central Repository */
maven { url "https://repo.maven.apache.org/maven2" }
/* WSO2 Releases Repository */
maven { url "https://maven.wso2.org/nexus/content/repositories/releases/" }
/* WSO2 internal Repository */
maven { url "https://maven.wso2.org/nexus/content/groups/wso2-public/" }
/* Github Package Repository */
maven {
url 'https://maven.pkg.github.com/ballerina-platform/*'
credentials {
username System.getenv('packageUser')
password System.getenv('packagePAT')
}
}
}
configurations {
jbalTools
awsLambdaBala
awsLambdaExamples
azFunctionsBala
azFunctionsExamples
kubernetesAnnotations
kubernetesExamples
ballerinaStdLibs
ballerinaC2cLibs
ballerinaC2cTooling
ballerinaTools
openapiModule
exten {
transitive = false
}
devTools
externalTestJars
}
dependencies {
jbalTools "org.ballerinalang:jballerina-tools:${ballerinaLangVersion}@zip"
/* Ballerina Dev Tools */
devTools "org.ballerinalang:ballerina-dev-tools:${devToolsVersion}"
externalTestJars (group: 'mysql', name: 'mysql-connector-java', version: "8.0.21") {
transitive = false
}
/* Standard libraries */
ballerinaStdLibs "io.ballerina.lib:data.yaml-ballerina:${stdlibDataYamlVersion}"
ballerinaStdLibs "io.ballerina.lib:data.jsondata-ballerina:${stdlibDataJsonDataVersion}"
ballerinaStdLibs "io.ballerina.lib:data.xmldata-ballerina:${stdlibDataXmldataVersion}"
ballerinaStdLibs "io.ballerina.lib:data.csv-ballerina:${stdlibDataCsvVersion}"
ballerinaStdLibs "io.ballerina.stdlib:constraint-ballerina:${stdlibConstraintVersion}"
ballerinaStdLibs "io.ballerina.stdlib:io-ballerina:${stdlibIoVersion}"
ballerinaStdLibs "io.ballerina.stdlib:jballerina.java.arrays-ballerina:${stdlibJavaArraysVersion}"
ballerinaStdLibs "io.ballerina.stdlib:random-ballerina:${stdlibRandomVersion}"
ballerinaStdLibs "io.ballerina.stdlib:time-ballerina:${stdlibTimeVersion}"
ballerinaStdLibs "io.ballerina.stdlib:url-ballerina:${stdlibUrlVersion}"
ballerinaStdLibs "io.ballerina.stdlib:xmldata-ballerina:${stdlibXmldataVersion}"
ballerinaStdLibs "io.ballerina.lib:ldap-ballerina:${stdlibLdapVersion}"
ballerinaStdLibs "io.ballerina.stdlib:crypto-ballerina:${stdlibCryptoVersion}"
ballerinaStdLibs "io.ballerina.stdlib:log-ballerina:${stdlibLogVersion}"
ballerinaStdLibs "io.ballerina.stdlib:os-ballerina:${stdlibOsVersion}"
ballerinaStdLibs "io.ballerina.stdlib:task-ballerina:${stdlibTaskVersion}"
ballerinaStdLibs "io.ballerina.stdlib:xslt-ballerina:${stdlibXsltVersion}"
ballerinaStdLibs "io.ballerina.stdlib:protobuf-ballerina:${stdlibProtobufVersion}"
ballerinaStdLibs "io.ballerina.stdlib:cache-ballerina:${stdlibCacheVersion}"
ballerinaStdLibs "io.ballerina.stdlib:file-ballerina:${stdlibFileVersion}"
ballerinaStdLibs "io.ballerina.stdlib:ftp-ballerina:${stdlibFtpVersion}"
ballerinaStdLibs "io.ballerina.stdlib:mime-ballerina:${stdlibMimeVersion}"
ballerinaStdLibs "io.ballerina.stdlib:mqtt-ballerina:${stdlibMqttVersion}"
ballerinaStdLibs "io.ballerina.stdlib:tcp-ballerina:${stdlibTcpVersion}"
ballerinaStdLibs "io.ballerina.stdlib:udp-ballerina:${stdlibUdpVersion}"
ballerinaStdLibs "io.ballerina.stdlib:uuid-ballerina:${stdlibUuidVersion}"
ballerinaStdLibs "io.ballerina.stdlib:auth-ballerina:${stdlibAuthVersion}"
ballerinaStdLibs "io.ballerina.lib:avro-ballerina:${stdlibAvroVersion}"
ballerinaStdLibs "io.ballerina.stdlib:email-ballerina:${stdlibEmailVersion}"
ballerinaStdLibs "io.ballerina.stdlib:jwt-ballerina:${stdlibJwtVersion}"
ballerinaStdLibs "io.ballerina.stdlib:oauth2-ballerina:${stdlibOAuth2Version}"
ballerinaStdLibs "io.ballerina.stdlib:http-ballerina:${stdlibHttpVersion}"
ballerinaStdLibs "io.ballerina.lib:messaging-ballerina:${stdlibMessagingVersion}"
ballerinaStdLibs "io.ballerina.stdlib:graphql-ballerina:${stdlibGraphqlVersion}"
ballerinaStdLibs "io.ballerina.stdlib:grpc-ballerina:${stdlibGrpcVersion}"
ballerinaStdLibs "org.ballerinalang:transaction-ballerina:${stdlibTransactionVersion}"
ballerinaStdLibs "io.ballerina:observe-ballerina:${observeInternalVersion}"
ballerinaStdLibs "io.ballerina.stdlib:websocket-ballerina:${stdlibWebsocketVersion}"
ballerinaStdLibs "io.ballerina.stdlib:websub-ballerina:${stdlibWebsubVersion}"
ballerinaStdLibs "io.ballerina.stdlib:websubhub-ballerina:${stdlibWebsubhubVersion}"
ballerinaStdLibs "io.ballerina.lib:ai.np-ballerina:${stdlibAiNpVersion}"
ballerinaStdLibs "io.ballerina.stdlib:ai-ballerina:${stdlibAiVersion}"
ballerinaStdLibs "io.ballerina.stdlib:mcp-ballerina:${stdlibMcpVersion}"
ballerinaStdLibs "io.ballerina.stdlib:sql-ballerina:${stdlibSqlVersion}"
ballerinaStdLibs "io.ballerina.stdlib:persist-ballerina:${stdlibPersistVersion}"
ballerinaStdLibs "io.ballerina.stdlib:yaml-ballerina:${stdlibYamlVersion}"
ballerinaStdLibs "io.ballerina.stdlib:toml-ballerina:${stdlibTomlVersion}"
ballerinaStdLibs "io.ballerina.stdlib:soap-ballerina:${stdlibSoapVersion}"
ballerinaStdLibs "io.ballerina.stdlib:observe-ballerina:${observeVersion}"
ballerinaStdLibs "io.ballerina.stdlib:math.vector-ballerina:${stdlibMathVectorVersion}"
ballerinaStdLibs "io.ballerina.stdlib:edi-ballerina:${stdlibEdiVersion}"
/* OpenAPI Module */
openapiModule "io.ballerina:module-ballerina-openapi:${openapiModuleVersion}"
/* Code to cloud extensions */
ballerinaC2cLibs "io.ballerina:cloud-ballerina:${c2cVersion}"
ballerinaC2cTooling "io.ballerina:cloud-tooling:${c2cVersion}"
/* Bal tools */
ballerinaTools "io.ballerina:graphql-tool:${graphqlToolVersion}"
ballerinaTools "io.ballerina:protoc-tool:${protocToolVersion}"
ballerinaTools "io.ballerina:openapi-tool:${openapiToolVersion}"
ballerinaTools "io.ballerina:asyncapi-tool:${asyncapiToolVersion}"
ballerinaTools "io.ballerina:persist-tool:${persistToolVersion}"
}
}
================================================
FILE: cache-generator/build.gradle
================================================
/*
* Copyright (c) 2021, 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.
*
*/
apply from: "$rootDir/gradle/javaProject.gradle"
apply plugin: "java"
apply plugin: "com.github.johnrengelman.shadow"
configurations.all {
resolutionStrategy.preferProjectModules()
}
configurations {
balTools
}
repositories {
mavenCentral()
}
dependencies {
implementation 'junit:junit:4.13.1'
implementation "com.fasterxml.jackson.core:jackson-databind"
implementation "io.swagger.core.v3:swagger-models"
implementation ("io.swagger.parser.v3:swagger-parser-v2-converter") {
exclude group: "io.swagger", module: "swagger-compat-spec-parser"
exclude group: "org.slf4j", module: "slf4j-ext"
exclude group: "javax.validation", module: "validation-api"
}
implementation "javax.ws.rs:javax.ws.rs-api"
implementation "com.github.jknack:handlebars"
implementation "info.picocli:picocli"
implementation "org.ballerinalang:ballerina-lang"
implementation "org.ballerinalang:ballerina-parser"
implementation "org.ballerinalang:formatter-core"
implementation "org.ballerinalang:ballerina-cli"
implementation "org.ballerinalang:ballerina-tools-api"
implementation "io.ballerina.stdlib:http-native"
implementation "com.google.code.findbugs:jsr305"
testImplementation "org.testng:testng"
balTools ("org.ballerinalang:jballerina-tools:${ballerinaLangVersion}") {
transitive = false
}
}
def bDistribution = file("$project.buildDir/extracted-distribution/jballerina-tools-${ballerinaLangVersion}")
shadowJar {
configurations = [project.configurations.runtimeClasspath]
dependencies {
include(dependency('com.github.jknack:handlebars'))
include(dependency('org.antlr:antlr4:4.5'))
include(dependency('io.swagger.parser.v3:swagger-parser'))
include(dependency('com.atlassian.commonmark:commonmark'))
include(dependency('com.atlassian.commonmark:commonmark-ext-gfm-tables'))
exclude('META-INF/*.SF')
exclude('META-INF/*.DSA')
exclude('META-INF/*.RSA')
}
}
compileJava {
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
]
classpath = files()
}
}
================================================
FILE: cache-generator/src/main/java/io/ballerina/gencache/cmd/GenCacheCmd.java
================================================
/*
* 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.gencache.cmd;
import io.ballerina.cli.BLauncherCmd;
import io.ballerina.projects.*;
import io.ballerina.projects.bala.BalaProject;
import io.ballerina.projects.repos.FileSystemCache;
import io.ballerina.tools.diagnostics.Diagnostic;
import picocli.CommandLine;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
@CommandLine.Command(
name = "gencache",
description = "Generates the Ballerina standard library caches"
)
public class GenCacheCmd implements BLauncherCmd{
private static final String CMD_NAME = "gencache";
private PrintStream outStream;
private PrintStream errStream;
private boolean exitWhenFinish;
private static final String BALLERINA_HOME_KEY = "ballerina.home";
private String balaPath;
private String workingDir;
@CommandLine.Parameters
private List argList;
public GenCacheCmd() {
this.outStream = System.out;
this.errStream = System.err;
this.exitWhenFinish = true;
}
@Override
public void execute() {
balaPath = argList.get(0);
workingDir = argList.get(1);
if ( balaPath == null || workingDir == null ) {
errStream.println("missing input options");
exitError(this.exitWhenFinish);
return;
}
try {
outStream.println(workingDir);
outStream.println(balaPath);
Path jBalToolsPath = Paths.get( workingDir);
Path repo = jBalToolsPath.resolve( "repo");
Path modulePath = Paths.get(balaPath);
System.setProperty(BALLERINA_HOME_KEY, repo.getParent().toString());
ProjectEnvironmentBuilder defaultBuilder = ProjectEnvironmentBuilder.getDefaultBuilder();
defaultBuilder.addCompilationCacheFactory(new FileSystemCache.FileSystemCacheFactory(repo.resolve("cache")));
Project balaProject = BalaProject.loadProject(defaultBuilder, modulePath);
PackageCompilation packageCompilation = balaProject.currentPackage().getCompilation();
DiagnosticResult diagnosticResult = packageCompilation.diagnosticResult();
for (Diagnostic diagnostic : diagnosticResult.diagnostics()) {
outStream.println(diagnostic.toString());
}
if (diagnosticResult.hasErrors()) {
return;
}
JBallerinaBackend jBallerinaBackend = JBallerinaBackend.from(packageCompilation, JvmTarget.JAVA_21);
diagnosticResult = jBallerinaBackend.diagnosticResult();
for (Diagnostic diagnostic : diagnosticResult.diagnostics()) {
outStream.println(diagnostic.toString());
}
//TODO : Remove when regeneration is not required
} catch (Error e) {
//TODO : Ignore Error and continue generation as regeneration will be done
outStream.println("Error occurred " + balaPath + " " + e);
} catch (Exception e) {
//TODO : Ignore Exception and continue generation as regeneration will be done
outStream.println("Exception occurred " + balaPath + " " + e);
}
}
@Override
public String getName() {
return CMD_NAME;
}
@Override
public void printLongDesc(StringBuilder out) {
out.append("Build a Ballerina project and produce an executable JAR file. The \n");
out.append("executable \".jar\" file will be created in the /target/bin directory. \n");
out.append("\n");
out.append("Build a single Ballerina file. This creates an executable .jar file in the \n");
out.append("current directory. The name of the executable file will be \n");
out.append(".jar. \n");
out.append("\n");
}
@Override
public void printUsage( StringBuilder out ) {
out.append(" bal gencache [bala-path] [package-path] ");
}
@Override
public void setParentCmdParser( CommandLine parentCmdParser) {
}
private void exitError(boolean exitWhenFinish) {
if ( exitWhenFinish ) {
Runtime.getRuntime().exit(1);
}
}
}
================================================
FILE: cache-generator/src/main/java/module-info.java
================================================
/*
* Copyright (c) 2021, WSO2 Inc. (http://wso2.com) 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.
*/
module io.ballerina.gencache {
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires handlebars;
requires info.picocli;
requires io.ballerina.lang;
requires io.ballerina.parser;
requires io.ballerina.stdlib.http;
requires io.ballerina.cli;
requires io.ballerina.tools.api;
requires io.ballerina.formatter.core;
requires io.swagger.v3.core;
requires io.swagger.v3.oas.models;
requires java.ws.rs;
requires jsr305;
requires org.apache.commons.io;
requires org.slf4j;
requires swagger.core;
requires swagger.parser;
requires swagger.models;
requires swagger.parser.core;
requires swagger.parser.v2.converter;
requires swagger.parser.v3;
requires org.apache.commons.lang3;
// exports io.ballerina.gencache.generators.gencache;
// exports io.ballerina.gencache.cmd;
// exports io.ballerina.gencache.exception;
// exports io.ballerina.gencache.cmd.model;
// exports io.ballerina.gencache.cmd.utils;
}
================================================
FILE: cache-generator/src/main/resources/META-INF/services/io.ballerina.cli.BLauncherCmd
================================================
io.ballerina.gencache.cmd.GenCacheCmd
================================================
FILE: config/checkstyle/build.gradle
================================================
/*
* Copyright (c) 2021, 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 "de.undercouch.download"
}
task downloadMultipleFiles(type: Download) {
src([
'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/checkstyle.xml',
'https://raw.githubusercontent.com/wso2/code-quality-tools/v1.4/checkstyle/jdk-17/suppressions.xml'
])
overwrite false
onlyIfNewer true
dest buildDir
}
jar {
enabled = false
}
clean {
enabled = false
}
artifacts.add('default', file("${rootDir}/config/checkstyle/checkstyle.xml")) {
builtBy('downloadMultipleFiles')
}
artifacts.add('default', file("${rootDir}/config/checkstyle/suppressions.xml")) {
builtBy('downloadMultipleFiles')
}
================================================
FILE: config/checkstyle/checkstyle.xml
================================================
================================================
FILE: config/checkstyle/suppressions.xml
================================================
================================================
FILE: devtools-integration-tests/build.gradle
================================================
/*
~ * 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.
~ */
description = 'Ballerina - Dev Tools Test'
configurations {
jBallerinaDistribution
ballerinaDistribution
ballerinaLinuxDistribution
ballerinaMacDistribution
ballerinaWindowsDistribution
}
dependencies {
testImplementation "org.testng:testng:${testngVersion}"
testImplementation "net.lingala.zip4j:zip4j:${netLingalaZip4jVersion}"
testImplementation "commons-io:commons-io:${commonsIoVersion}"
testImplementation "org.apache.commons:commons-lang3:${commonsLang3Version}"
testImplementation "org.ballerinalang:jballerina-debugger-integration-test:${ballerinaLangVersion}"
testImplementation "org.ballerinalang:ballerina-test-utils:${ballerinaLangVersion}"
testImplementation "org.eclipse.lsp4j:org.eclipse.lsp4j.debug:${lsp4jDebugVersion}"
jBallerinaDistribution project(path: ":ballerina", configuration: "jBallerinaDistribution")
ballerinaDistribution project(path: ":ballerina", configuration: "ballerinaDistribution")
ballerinaLinuxDistribution project(path: ":ballerina", configuration: "ballerinaLinuxDistribution")
ballerinaMacDistribution project(path: ":ballerina", configuration: "ballerinaMacDistribution")
ballerinaWindowsDistribution project(path: ":ballerina", configuration: "ballerinaWindowsDistribution")
}
task devToolsIntegrationTest {
dependsOn configurations.jBallerinaDistribution
dependsOn configurations.ballerinaDistribution
dependsOn configurations.ballerinaLinuxDistribution
dependsOn configurations.ballerinaLinuxDistribution
dependsOn configurations.ballerinaWindowsDistribution
test {
systemProperty "distributions.dir", "$buildDir/../../ballerina/build/distributions"
systemProperty "target.dir", "$buildDir"
systemProperty "maven.version", "$version"
systemProperty "examples.dir", "$buildDir/../../examples"
systemProperty "line.check.extensions", ".java, .bal"
systemProperty "short.version", "$version".split("-")[0]
systemProperty "server.zip", "$buildDir/../../ballerina/build/distributions/ballerina-${shortVersion}.zip"
systemProperty "code.name", "$codeName"
useTestNG() {
suites 'src/test/resources/testng.xml'
}
testLogging {
showStandardStreams = true
}
}
}
test.dependsOn devToolsIntegrationTest
================================================
FILE: devtools-integration-tests/index.json
================================================
[
{
"name": "Testerina Object mocking test",
"path": "testerina-object-mocking-test"
}
]
================================================
FILE: devtools-integration-tests/src/test/java/org/ballerina/devtools/debug/BaseTestCase.java
================================================
/*
* 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.ballerina.devtools.debug;
import org.ballerinalang.debugger.test.utils.DebugTestRunner;
import org.ballerinalang.test.context.BallerinaTestException;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Parent test class for all debug integration test cases.
*/
public class BaseTestCase {
@BeforeSuite(alwaysRun = true)
public void initialize() throws BallerinaTestException, IOException {
Path projectResourcePath = Paths.get("src", "test", "resources", "debug", "project-based-tests").toAbsolutePath();
Path singleFileResourcePath = Paths.get("src", "test", "resources", "debug", "single-file-tests").toAbsolutePath();
DebugTestRunner.initialize(projectResourcePath, singleFileResourcePath);
}
@AfterSuite(alwaysRun = true)
public void destroy() {
DebugTestRunner.destroy();
}
}
================================================
FILE: devtools-integration-tests/src/test/java/org/ballerina/devtools/debug/ExpressionEvaluationTest.java
================================================
/*
* 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.ballerina.devtools.debug;
import org.apache.commons.lang3.tuple.Pair;
import org.ballerinalang.debugger.test.utils.BallerinaTestDebugPoint;
import org.ballerinalang.debugger.test.utils.DebugTestRunner;
import org.ballerinalang.debugger.test.utils.DebugUtils;
import org.ballerinalang.test.context.BallerinaTestException;
import org.eclipse.lsp4j.debug.StoppedEventArguments;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.nio.file.Paths;
/**
* Test class for expression evaluation related scenarios.
*/
public class ExpressionEvaluationTest extends BaseTestCase {
private DebugTestRunner debugTestRunner;
private StoppedEventArguments context;
@BeforeClass
public void setup() throws BallerinaTestException {
prepareForEvaluation();
}
@Test(description = "Test Ballerina standard library related remote method call evaluations", enabled = false)
public void testRemoteCallEvaluation() throws BallerinaTestException {
debugTestRunner.assertExpression(context, "httpClient->get(\"/args/ballerina-platform/repos\");", "Not Found",
"error");
}
@Test(description = "Test Ballerina standard library related object methods evaluations")
public void testObjectMethodEvaluation() throws BallerinaTestException {
debugTestRunner.assertExpression(context, "response.getContentType()", "\"application/json; charset=utf-8\"",
"string");
debugTestRunner.assertExpression(context, "response.getTextPayload()", "\"{\"message\":\"Not Found\"," +
"\"documentation_url\":\"https://docs.github.com/rest\"}\"", "string");
}
@AfterClass(alwaysRun = true)
public void cleanUp() {
debugTestRunner.terminateDebugSession();
this.context = null;
}
protected void prepareForEvaluation() throws BallerinaTestException {
String testProjectName = "evaluation-tests";
String testModuleFileName = Paths.get("main.bal").toString();
debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true);
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 24));
debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN);
Pair debugHitInfo = debugTestRunner.waitForDebugHit(35000);
this.context = debugHitInfo.getRight();
}
}
================================================
FILE: devtools-integration-tests/src/test/java/org/ballerina/devtools/debug/ModuleBreakpointTest.java
================================================
/*
* 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.ballerina.devtools.debug;
import org.apache.commons.lang3.tuple.Pair;
import org.ballerinalang.debugger.test.utils.BallerinaTestDebugPoint;
import org.ballerinalang.debugger.test.utils.DebugTestRunner;
import org.ballerinalang.debugger.test.utils.DebugUtils;
import org.ballerinalang.test.context.BallerinaTestException;
import org.eclipse.lsp4j.debug.StoppedEventArguments;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.ballerinalang.debugger.test.utils.DebugTestRunner.DebugResumeKind;
/**
* Test class for ballerina breakpoints related test scenarios.
*/
public class ModuleBreakpointTest extends BaseTestCase {
DebugTestRunner debugTestRunner;
@BeforeClass
public void setup() {
String testProjectName = "breakpoint-tests";
String testModuleFileName = "main.bal";
debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true);
}
@Test
public void testMultipleBreakpointsInSameFile() throws BallerinaTestException {
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 20));
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 26));
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 30));
debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN);
Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000);
Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(0));
debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugResumeKind.NEXT_BREAKPOINT);
debugHitInfo = debugTestRunner.waitForDebugHit(10000);
Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(1));
debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugResumeKind.NEXT_BREAKPOINT);
debugHitInfo = debugTestRunner.waitForDebugHit(10000);
Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(2));
}
@AfterMethod(alwaysRun = true)
public void cleanUp() {
debugTestRunner.terminateDebugSession();
}
}
================================================
FILE: devtools-integration-tests/src/test/java/org/ballerina/devtools/debug/ServiceDebugTest.java
================================================
/*
* 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.ballerina.devtools.debug;
import org.apache.commons.lang3.tuple.Pair;
import org.ballerinalang.debugger.test.utils.BallerinaTestDebugPoint;
import org.ballerinalang.debugger.test.utils.DebugTestRunner;
import org.ballerinalang.debugger.test.utils.DebugUtils;
import org.ballerinalang.test.context.BallerinaTestException;
import org.eclipse.lsp4j.debug.StackFrame;
import org.eclipse.lsp4j.debug.StoppedEventArguments;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Test class for service related debug scenarios.
*/
public class ServiceDebugTest extends BaseTestCase {
DebugTestRunner debugTestRunner;
@BeforeClass
public void setup() {
String testProjectName = "service-tests";
String testModuleFileName = Paths.get("tests", "hello_service_test.bal").toString();
debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true);
}
@Test(description = "Test for service module debug engage")
public void testModuleServiceDebugScenarios() throws BallerinaTestException {
String fileName = "hello_service.bal";
Path filePath = debugTestRunner.testProjectPath.resolve(fileName);
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(filePath, 23));
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(filePath, 28));
debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.TEST);
// test for debug hits in service level variables
Pair debugHitInfo = debugTestRunner.waitForDebugHit(30000);
Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(0));
// Timeout Exception is expected, since the service get started once the VM is resumed and resource
// function cannot have any debug hits until an HTTP request is received.
debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT);
try {
debugTestRunner.waitForDebugHit(10000);
} catch (BallerinaTestException e) {
if (!e.getMessage().equals("Timeout expired waiting for the debug hit")) {
throw e;
}
}
}
@Test(description = "Test for service call stack representation")
public void serviceCallStackDebugTest() throws BallerinaTestException {
String fileName = "hello_service.bal";
Path filePath = debugTestRunner.testProjectPath.resolve(fileName);
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(filePath, 23));
debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.TEST);
// Test for service call stack representation
Pair debugHitInfo = debugTestRunner.waitForDebugHit(30000);
StackFrame[] frames = debugTestRunner.fetchStackFrames(debugHitInfo.getRight());
debugTestRunner.assertCallStack(frames[0], "init", 23, "hello_service.bal");
}
@Test(description = "Test for single bal file debug engage")
public void testSingleBalFileServiceDebugScenarios() throws BallerinaTestException {
String testProjectName = "";
String testSingleFileName = "hello_service.bal";
debugTestRunner = new DebugTestRunner(testProjectName, testSingleFileName, false);
debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 24));
debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.TEST);
Pair debugHitInfo = debugTestRunner.waitForDebugHit(30000);
Assert.assertEquals(debugHitInfo.getLeft(), debugTestRunner.testBreakpoints.get(0));
}
@AfterMethod(alwaysRun = true)
public void cleanUp() {
debugTestRunner.terminateDebugSession();
}
}
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/breakpoint-tests/Ballerina.toml
================================================
[package]
org = "debug_test_resources"
name = "breakpoint_tests"
version = "0.1.0"
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/breakpoint-tests/main.bal
================================================
// 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/io;
public function main() {
io:println("Hello Ballerina!!!");
sayHello();
int x = intAdd(4, 5);
}
public function sayHello() {
io:println("Hello");
}
public function intAdd(int a, int b) returns int {
return (a + b);
}
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/evaluation-tests/Ballerina.toml
================================================
[package]
org = "debug_test_resources"
name = "evaluation_tests"
version = "0.1.0"
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/evaluation-tests/main.bal
================================================
// 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;
import ballerina/http;
public function main() {
do {
http:Client httpClient = check new ("https://api.github.com");
http:Response response = check httpClient->get("/args/ballerina-platform/repos");
io:println(response.statusCode);
} on fail var e {
io:print(e);
}
}
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/service-tests/Ballerina.toml
================================================
[package]
org = "debug_test_resources"
name = "service_tests"
version = "0.1.0"
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/service-tests/hello_service.bal
================================================
// 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/http;
service / on new http:Listener(9191) {
final int x;
function init() {
self.x = 5;
}
resource function get sayHello() returns string {
return "Hello, World!";
}
}
================================================
FILE: devtools-integration-tests/src/test/resources/debug/project-based-tests/service-tests/tests/hello_service_test.bal
================================================
// 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/test;
import ballerina/io;
import ballerina/http;
# Before Suite Function
@test:BeforeSuite
function beforeSuiteServiceFunc () {
io:println("This will test if a service start during testing");
}
# Test function
@test:Config{}
function testServiceFunction () {
http:Client httpClient = checkpanic new("http://localhost:9191");
http:Response|error response = httpClient->get("/sayHello");
if (response is http:Response) {
test:assertEquals(response.getTextPayload(), "Hello, World!", "Service involation test");
} else {
test:assertFail("Service invocation failed!");
}
}
# After Suite Function
@test:AfterSuite {}
function afterSuiteServiceFunc () {
io:println("Service should stop after this.");
}
================================================
FILE: devtools-integration-tests/src/test/resources/debug/single-file-tests/hello_service.bal
================================================
// 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/http;
import ballerina/test;
service on new http:Listener(9090) {
resource function get sayHello(http:Caller caller, http:Request req) returns error? {
check caller->respond("Hello, World!");
}
}
# Test function
@test:Config{}
function testServiceFunction () {
http:Client httpClient = checkpanic new("http://localhost:9090");
http:Response|error response = httpClient->get("/sayHello");
if (response is http:Response) {
test:assertEquals(response.getTextPayload(), "Hello, World!", "Service involation test");
} else {
test:assertFail("Service invocation failed!");
}
}
================================================
FILE: devtools-integration-tests/src/test/resources/testng.xml
================================================
================================================
FILE: devtools-integration-tests/testerina-object-mocking-test/client.bal
================================================
import ballerina/http;
http:Client orderManagementClient = checkpanic new ("http://localhost:9117/ordermgt");
json responsePayload = {};
public function findOrder(string orderId) returns (json) {
http:Response|error response = orderManagementClient->get("/order/" + orderId);
if (response is http:Response) {
return handleResponse(response);
} else {
return {"Error": "Server error"};
}
}
public function addOrder(json orderPayload) returns (json) {
json|error IDJson = orderPayload.Order.ID;
if (IDJson is json) {
string ID = IDJson.toString();
json foundOrder = findOrder(ID);
if (foundOrder == {"Error": "Order : " + ID + " cannot be found."}) {
http:Response|error response = orderManagementClient->post("/order", orderPayload);
if (response is http:Response) {
return handleResponse(response);
} else {
return "Server error";
}
} else {
return "Order with same ID already exists";
}
} else {
return "Server error";
}
}
function handleResponse(http:Response response) returns (json) {
var jsonPayload = response.getJsonPayload();
if (jsonPayload is json) {
return <@untainted>jsonPayload;
} else {
return {"Error": "Malformed Payload recieved"};
}
}
================================================
FILE: devtools-integration-tests/testerina-object-mocking-test/service.bal
================================================
import ballerina/http;
import ballerina/log;
listener http:Listener httpListener = new(9117);
map ordersMap = {};
service /ordermgt on httpListener {
resource function get 'order/[string orderId](http:Caller caller, http:Request req) {
json? payload = ordersMap[orderId];
http:Response response = new;
if (payload == null) {
payload = { "Error" : "Order : " + orderId + " cannot be found." };
}
response.setJsonPayload(<@untainted> payload);
var result = caller->respond(response);
if (result is error) {
log:printError("Error sending response");
}
}
resource function post 'order(http:Caller caller, http:Request req) {
http:Response response = new;
var orderReq = req.getJsonPayload();
if (orderReq is json) {
json|error orderIdJson = orderReq.Order.ID;
string orderId = "";
if (orderIdJson is json) {
orderId = orderIdJson.toString();
} else {
log:printError("Error sending response");
}
ordersMap[orderId] = <@untainted> orderReq;
json payload = { status: "Order Created.", orderId: orderId };
response.setJsonPayload(payload);
response.statusCode = 201;
response.setHeader("Location",
"http://localhost:9117/ordermgt/order/" + orderId);
var result = caller->respond(response);
if (result is error) {
log:printError("Error sending response");
}
} else {
response.statusCode = 400;
response.setPayload("Invalid payload received");
var result = caller->respond(response);
if (result is error) {
log:printError("Error sending response");
}
}
}
}
================================================
FILE: devtools-integration-tests/testerina-object-mocking-test/tests/main_test.bal
================================================
import ballerina/test;
import ballerina/http;
public client class MockHttpClient {
public string url = "http://mockUrl";
remote function get(@untainted string path, http:RequestMessage message = (),
http:TargetType targetType = http:Response) returns http:Response | anydata |http:ClientError {
http:Response res = new;
res.statusCode = 500;
return res;
}
remote function post(@untainted string path, http:RequestMessage message = (),
http:TargetType targetType = http:Response) returns http:Response|anydata|http:ClientError {
http:Response res = new;
res.statusCode = 500;
return res;
}
}
@test:Config {}
function test_addOrderWithoutMock() {
json orderPayload = {
"Order": {
"ID": "100500",
"Name": "XYZ",
"Description": "Sample order."
}
};
json actual = addOrder(orderPayload);
json expected = {"status":"Order Created.","orderId":"100500"};
test:assertEquals(actual, expected, "Add order test failed");
}
@test:Config { dependsOn: [test_addOrderWithoutMock]}
function test_addOrderAgain() {
json orderPayload = {
"Order": {
"ID": "100500",
"Name": "XYZ",
"Description": "Sample order."
}
};
json actual = addOrder(orderPayload);
json expected = "Order with same ID already exists";
test:assertEquals(actual, expected, "Add order test failed");
}
// Create mock http client
http:Client mockHttpClient = test:mock(http:Client);
json getResponse1 = {
"Order": {
"ID": "100",
"Name": "MOCK",
"Description": "Mock Order."
}
};
json getResponse2 = { "Error" : "Order : 200 cannot be found." };
@test:Config { dependsOn: [test_addOrderWithoutMock]}
function test_findOrder() {
// Create a Specific response for get
http:Response mockGetResponse = new;
mockGetResponse.setJsonPayload(<@untainted> getResponse1);
test:prepare(mockHttpClient).when("get").thenReturn(mockGetResponse);
mockGetResponse = new;
mockGetResponse.setJsonPayload(<@untainted> getResponse2);
test:prepare(mockHttpClient).when("get").withArguments("/order/200").thenReturn(mockGetResponse);
orderManagementClient = mockHttpClient;
// Try to find a non existing order
json actual = findOrder("100");
json expected = getResponse1;
test:assertEquals(actual, expected, "Correct mocked response not recieved");
actual = findOrder("200");
expected = getResponse2;
test:assertEquals(actual, expected, "Correct mocked response not recieved");
}
json postPayload = {
"Order": {
"ID": "200",
"Name": "MOCK",
"Description": "Mock Order."
}
};
json postResponse1 = {
status : "Order Created.",
orderId : "200"
};
@test:Config {
dependsOn : [test_findOrder]
}
function test_addOrder() {
http:Response mockPostResponse = new;
mockPostResponse.setJsonPayload(<@untainted> postResponse1);
test:prepare(mockHttpClient).when("post").withArguments("/order", postPayload).thenReturn(mockPostResponse);
json actual = addOrder(postPayload);
json expected = postResponse1;
test:assertEquals(actual, expected, "Correct mocked response not recieved");
}
================================================
FILE: dist-repo-builder/build.gradle
================================================
plugins {
id 'java'
}
group 'org.ballerinalang'
version '1.0.0'
dependencies {
implementation "org.ballerinalang:docerina:${ballerinaLangVersion}"
implementation "org.ballerinalang:ballerina-lang:${ballerinaLangVersion}"
}
================================================
FILE: dist-repo-builder/src/main/java/io/ballerina/dist/DistRepoBuilder.java
================================================
/*
* 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 io.ballerina.dist;
import io.ballerina.projects.ProjectEnvironmentBuilder;
import io.ballerina.projects.bala.BalaProject;
import io.ballerina.projects.repos.TempDirCompilationCache;
import org.ballerinalang.docgen.docs.BallerinaDocGenerator;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class to build and populate distribution cache.
*
* @since 2.0.0
*/
public class DistRepoBuilder {
final static String balaGlob = "glob:**/bala.json";
final static String jarGlob = "glob:**/*.jar";
final static String docGlob = "glob:**/api-docs.json";
final static String MODULE_BALLERINAI_OBSERVE = "ballerinai" + File.separator +"observe";
final static String OS = System.getProperty("os.name");
public static void main(String args[]) throws Exception {
System.out.println("Building Distribution Repo ...");
if (args.length != 1) {
System.out.println("Invalid Inputs");
System.exit(1);
}
Path jBalToolsPath = Paths.get(args[0]);
Path repo = jBalToolsPath.resolve("repo");
System.setProperty("ballerina.home", jBalToolsPath.toString());
// Find all bala files
List balas = findBalas(repo.resolve("bala"));
// Extract platform libs
boolean valid = true;
// The following list will contain existing docs from ballerina-lang repo
List existingDocs = getExistingDocs(jBalToolsPath.resolve("docs"));
for (Path bala : balas) {
// skipping in windows since character length of path is greater than maximum in windows
if (OS.contains("Windows")) {
continue;
}
// skipping ballerinai-observe module since API docs are not generated for internal modules
if (bala.toString().contains(MODULE_BALLERINAI_OBSERVE)) {
continue;
}
generateDocsFromBala(bala, jBalToolsPath, existingDocs);
// following function was put in to validate if bir and jar exists for packed balas
valid = valid & validateCache(bala, repo);
}
if (!valid) {
System.exit(1);
}
}
private static List getExistingDocs(Path jBalToolsDocPath) throws IOException {
List existingDocs = new ArrayList<>();
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(docGlob);
Files.walkFileTree(jBalToolsDocPath, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if (pathMatcher.matches(path)) {
Path relativePath = jBalToolsDocPath.relativize(path.getParent());
if (!relativePath.toString().equals("")) {
existingDocs.add(jBalToolsDocPath.relativize(path.getParent()));
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
return FileVisitResult.CONTINUE;
}
});
return existingDocs;
}
private static void generateDocsFromBala(Path balaPath, Path jBalToolsPath, List existingDocs) throws Exception {
if (existingDocs.stream().noneMatch(path -> balaPath.toString().contains(path.toString()))) {
try {
ProjectEnvironmentBuilder defaultBuilder = ProjectEnvironmentBuilder.getDefaultBuilder();
defaultBuilder.addCompilationCacheFactory(TempDirCompilationCache::from);
BalaProject balaProject = BalaProject.loadProject(defaultBuilder, balaPath);
BallerinaDocGenerator.generateAPIDocs(balaProject, jBalToolsPath.toString() + "/docs", true);
} catch (Exception e) {
e.printStackTrace();
throw new Exception("Exception when generating docs from bala: " + balaPath.toString());
}
}
}
private static boolean validateCache(Path bala, Path repo) {
boolean valid = true;
String version = bala.getParent().getFileName().toString();
String moduleName = bala.getParent().getParent().getFileName().toString();
String orgName = bala.getParent().getParent().getParent().getFileName().toString();
// Check if the bir exists
Path bir = repo.resolve("cache").resolve(orgName).resolve(moduleName).resolve(version).resolve("bir")
.resolve(moduleName + ".bir");
if (!Files.exists(bir)) {
System.out.println("Bir missing for package :" + orgName + "/" + moduleName);
valid = false;
}
// Check if module jar exists
Path jar = repo.resolve("cache").resolve(orgName).resolve(moduleName).resolve(version).resolve("java21")
.resolve(getJarName(orgName, moduleName, version));
if (!Files.exists(jar)) {
System.out.println("Jar missing for package :" + orgName + "/" + moduleName);
valid = false;
}
return valid;
}
static List findBalas(Path repo) throws IOException {
List balas = new ArrayList<>();
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(balaGlob);
Files.walkFileTree(repo, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
if (pathMatcher.matches(path)
&& Files.notExists(path.getParent().resolve("tool/bal-tool.json"))) {
balas.add(path.getParent());
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
return FileVisitResult.CONTINUE;
}
});
return balas;
}
private static String getJarName(String orgName, String moduleName, String version) {
return orgName + '-' + moduleName + '-' + version + ".jar";
}
private static void setFilePermission(Path filepath) {
File file = filepath.toFile();
file.setReadable(true, false);
file.setWritable(true, true);
}
private static boolean isWindows() {
return System.getProperty("os.name").startsWith("Windows");
}
}
================================================
FILE: doc-guidelines.md
================================================
# Ballerina Doc Guidelines
- [Ballerina Documentation Components](#ballerina-documentation-components)
- [Ballerina By Examples Guidelines](#ballerina-by-examples-guidelines)
- [Ballerina API Documentation Guidelines](#ballerina-api-documentation-guidelines)
## Ballerina Documentation Components
Ballerina language documentation consists of the main components below.
- [Ballerina By Examples](https://ballerina.io/learn/by-example/) - play the role of a quick reference example for language features,libraries, and extensions that are used frequently. It can be thought of as a cut-down version of the User Guide, which tries to introduce the features with quick examples to get an in-depth but practical understanding of a feature.
- [User Guide](https://ballerina.io/learn/) - provides detailed explanations on how a specific feature will work along with some comprehensive examples. It will not necessarily cover all the APIs that are available (that will be covered in API Docs). For example, when considering the I/O APIs, it will not be feasible to cover all the I/O types and operations supported by them.
- [API Docs](https://ballerina.io/learn/api-docs/ballerina/) - include all the possible options and parameter information that are relevant when using an API along with suitable examples.
Therefore, the combination of the Ballerina By Examples, User Guide, and API Docs represent the complete knowledge required for a Ballerina developer. There will be an overlap of content between these. For example, the User Guide has examples and functionality details to explain language-level features.
The below is a list of guidelines that must be followed when updating and adding new BBEs and API Docs.
## Ballerina By Examples Guidelines
1. Make the BBEs small, simple, and straight forward. Do not create examples with large lines of code. Otherwise, it will require to scroll through a large example to understand the use case.
2. Create realistic examples whenever possible. Make sure the example, which explains some feature shows a practical scenario when it can be used. However, sometimes, it may not be possible to create a small-enough practical scenario. It will be required to use your judgement to decide that.
3. Each new BBE should have its own directory named after it. The directory name should be in all lowercase letters. Use hyphens (`-`) in the directory names (e.g., `hello-world`) and underscores (`_`) in the file names (e.g., `hello_world.bal`).
>**Note:** The first letter of every word in the title of the BBE should be uppercase. Never capitalize prepositions and conjunctions of four or fewer letters. Words with five or more letters, regardless of whether the word is a conjunction or preposition, must be capitalized.
4. If a new example is added/deleted, update the `index.json` file as well.
>**Info:** Individual BBEs can be configured to disable the playground link generation and to override the default GitHub edit URL by setting the `disablePlayground` and `githubLink` properties accordignly in the `index.json` file.
For example,
```json
{
"title": "Ballerina Basics",
"column": 0,
"category": "Language concepts",
"samples": [
{
"name": "Functions",
"url": "functions",
"githubLink": "https://github.com/ballerina-platform/ballerina-lang/tree/ballerina-1.2.x/examples/functions/",
"disablePlayground": true
}
]
}
```
5. Each new example should contain at least the following files.
- `.bal` - sample code to display in the Ballerina website.
- `.description` - sample description displayed at the top of each example in the Ballerina website. The name should be the same as the name of the directory with hyphens replaced by underscores.
- `.out` - output of the sample displayed at the bottom inside a black colour box in in the Ballerina website. The output file for a particular `.bal` file should have the same name as the `.bal` file but with the `.out` extension.
- `_test.bal` - Contains the test to validate the output of the BBE during the build time.
For example,
- `.metatags` - Contains the meta description and keywords to build SEO in the webpage. The description should be 50-160 characters long, and there should be 3-5 keywords that are comma-separated.
For example,
[Hello World BBE .metatags file](https://github.com/ballerina-platform/ballerina-distribution/blob/master/examples/hello-world/hello_world.metatags)
description: BBE on how to print “Hello, World!” using a main function in Ballerina. A public function named main is considered as an entry point to a Ballerina program.
keywords: ballerina, ballerina by example, bbe, hello world main function
6. Break the `.description` file content into paragraphs when necessary and use `
` tags to separate them. New lines in the content do not get translated into new lines in the final rendering.
7. As a best practice, use the following format as a common pattern in the `.out` files and customize only when necessary (e.g., when it is needed to add more command line args etc).
For an example with main:
```bash
# To run this sample, navigate to the directory that contains the
# `.bal` file and issue the `bal run` command.
bal run .bal
```
For an example with a service:
```bash
# To start the service, navigate to the directory that contains the
# `.bal` file and issue the `bal run` command.
bal run hello_world_service.bal
```
8. Service examples demonstrating client-server scenarios have a `.bal` file only for the server and two different output files. That is, one to display the server output (`.server.out` file) and the other (`.client.out` file) to display the cURL command and the output. These two separate output files can be introduced with `.server` and `.client` suffixed to the file names.
For example, see the `hello_world_service.server.out` and `hello_world_service.client.out` files below of the [Hello World Service BBE](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/hello-world-service).
>**Note:** The `server.out` file will be displayed first on the website.
9. Unless it is really required, it is not encouraged to have multiple BAL files in the same sample. In that case, each BAL file can have its own name and the `.out` file should match with the name of the `.bal` file. For example,
>**Note:** The `publisher.bal` file and its `publisher.out` file will be displayed first on the website (before the subscriber files).
10. Currently, you can add `.proto`,`.conf`, and `.toml` files also to be displayed on the website. For example, see the [GRPC Bidirectional Streaming BBE](https://ballerina.io/learn/by-example/grpc-bidirectional-streaming.html).
>**Note:** The `.proto` files will be displayed first on the website. The `.conf` and `.toml` files will be displayed just before the last `.out` file.
11. Use language features to make the examples look elegant (and small). For example, string templates, functional iteration, anonymous functions, etc.
12. Use meaningful variable names, function names, etc.
13. Remove unused imports in `.bal` files.
14. Format the Ballerina source using an IDE Plugin.
15. Do not set (too many or any) optional properties in BBEs (e.g., JDBC connection pooling props). Users will figure it out with code assist or with additional documentation as those will be covered in the User Guide. Also, all the possible options will be covered in the API docs.
16. All keywords and any other word, which needs to be highlighted should be used with backquotes (e.g., `xml`). Do not use a single quote as it will not get highlighted in the Ballerina website.
For example, if the keywords are added with backquotes in the BAL file of the BBE as follows,

will get highlighted in the Ballerina website as follows.

17. Simplify error handling. Use `check` whenever possible. These examples do not need to show all possible error-handling situations. These possibilities can be shown in the actual error-handling BBEs.
18. As a practice, use `ballerina/io` methods in main examples and `ballerina/log` methods in the examples with services. Do not use both `io:println` and `log:printInfo` in the same sample.
19. Keep the length of the code lines in BBEs to a maximum character count of 80 per line in BAL files. Else, they get wrapped and you get horizontal scroll bars in the code view in the website reducing the readibility.
20. Add comments to the code blocks as much as possible with “//” as they are used as a mechanism to describe the code. They will be displayed in the RHS section in the Ballerina website.
For example, if a code comment is added in the BAL file of the BBE as follows,

it will be displayed in the Ballerina website as follows.

21. Since comments are displayed in the RHS in the website, they should be valid sentences (i.e. start with an upper case letter and end with a full stop etc.)
22. Try to keep the code-level comments short. If there are multiline large comments, the final website view would not be nice in which there will be large gaps between code lines to accommodate the comments in the right side of the code panel.
23. A comment applies to the subsequent lines in the file until another comment or an empty line is found. Use comments/new lines appropriately to ensure that it applies only to the relevant lines.
24. No restriction is applicable for the maximum character count in comment lines as they will go to RHS side and get wrapped automatically. However, since users can refer to the code in GitHub, it is better if we can have the same char limit as the code lines (i.e., 80) as it increases the readability of the code file.
For example, comments are significantly longer than the code line in the image below, which is not readable.

25. After any update to a BBE is done or a new BBE is added, please add Anjana Fernando (lafernando) and Praneesha as reviewers.
## Running Ballerina By Examples
After writing a Ballerina By Example, you can also run it to test and verify if the output is accurate. Follow the instuctions below to do this.
1. Create a directory with the BBE directories, which you want to test and the `index.json` file (e.g., `/examples`).
2. In the CLI, navigate to the `master` branch of the [ballerina-release](https://github.com/ballerina-platform/ballerina-release) GitHub repo (i.e., ``).
3. Execute the command below to build the BBEs.
>**Info:** You need to change the properties of the above command accordingly. Also, the `` property can be set to `false` while testing BBEs locally since with `true` it takes a longer time to run the tool. In the final run, you can set this to `true`.
```bash
go run ballerinaByExample/tools/generate.go “” “” “” “” “” “”
```
For example,
```bash
go run ballerinaByExample/tools/generate.go "/Documents/examples" "1.2" "by-example" "true" "true" "true"
```
4. Copy the generated `/by-example` directory.
5. Replace the `//learn/by-example`directory with the directory you copied.
6. In the CLI, navigate to the `master` branch of the [ballerina-dev-website](https://github.com/ballerina-platform/ballerina-dev-website) GitHub repo (i.e., ``).
7. Execute the `bundle exec jekyll serve` command to build the website locally.
>**Note:** Alternatively, execute the command below if you do not have Jekyll configured locally.
```bash
docker run -p 4000:4000 --volume="/home/shaf/Documents/source/public/ballerina-dev-website:/srv/jekyll" jekyll/builder:3.8 jekyll serve
```
8. Navigate to the Ballerina By Examples in the dev website built locally and test the BBE updates you did.
## Ballerina API Documentation Guidelines
For Ballerina API Documentation Guidelines, see the [Standard Library API Documentation Guide](https://github.com/ballerina-platform/ballerina-standard-library/blob/main/docs/api-documentation-guide.md).
================================================
FILE: docs/build-ballerina-from-source.md
================================================
# Build Ballerina from source
## Overview
[Building the complete Ballerina distribution](#building-the-complete-ballerina-distribution) provides you access to all the main features of Ballerina such as the runtime, corresponding tools, standard library modules etc.
>**Info:** However, if you need just a plain Ballerina language build with only the basic language features and the JBallerina Java (Java Introp) API in it, you can [build only the Ballerina Runtime with the tools](#building-only-the-ballerina-runtime-with-the-tools)
## Set up the prerequisites
Follow the steps below to set up the prerequisites.
1. Download and [set up](https://adoptopenjdk.net/installation.html) OpenJDK 21 ([Adopt OpenJDK](https://adoptopenjdk.net/) or any other OpenJDK distribution).
>**Info:** You can also use [Oracle JDK](https://www.oracle.com/java/technologies/javase-downloads.html).
2. Set up a [Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) for your GitHub account and configure the following environment variables (the access token should have the `read` package permission).
**For Unix/macOS:**
```bash
export packageUser="";
export packagePAT="";
```
**For Windows:**
```bash
set packageUser=
set packagePAT=
```
## Build the complete Ballerina distribution
Follow the steps below to build the [`ballerina-distribution` repository](https://github.com/ballerina-platform/ballerina-distribution) to get full access to the complete Ballerina distribution.
1. Fork the `ballerina-distribution` repository to your GitHub account and execute following command to clone it.
```bash
git clone --recursive https://github.com//ballerina-distribution.git
```
2. Navigate to the `` directory, and execute the command below to start the build (here, the tests are excluded to speed up the build).
**For Unix/macOS:**
```bash
./gradlew clean build -x test
```
**For Windows:**
```bash
gradlew clean build -x test
```
3. Extract the built Ballerina Language distribution (i.e., the `/ballerina/build/distributions/ballerina-.zip` file) to a preferred location.
4. Configure the environment variables below.
**For Unix/macOS:**
```bash
# Set up the `BALLERINA_HOME` environment variable.
export BALLERINA_HOME="/ballerina-";
# Include the binaries to the system `PATH`.
PATH=$BALLERINA_HOME/bin:$PATH;
export PATH;
```
**For Windows:**
```bash
# Set up the `BALLERINA_HOME` environment variable.
set BALLERINA_HOME="\ballerina-";
# Include the binaries to the system `PATH`.
set PATH=%PATH%;%BALLERINA_HOME%\bin;
```
### Test the distribution build
Since this is a complete Ballerina distribution build, this will have all the Standard Library module dependencies included in it.
Therefore, follow the steps below to write a simple program using the Ballerina [`io` module](https://github.com/ballerina-platform/module-ballerina-io/) to test the distribution build.
1. Create a `hello_world_with_io.bal` file with the code below.
```ballerina
import ballerina/io;
// ballerina hello world program
public function main() {
io:println("Hello, World with IO!");
}
```
2. Execute the command below to build and run this program.
```bash
bal run hello_world_with_io.bal
```
If your build is successful, you view the output below.
```bash
Hello, World with IO!
```
## Build only the Ballerina runtime with the tools
Follow the steps below to build just the Ballerina runtime with the corresponding tools.
1. Fork the [`ballerina-lang` repository](https://github.com/ballerina-platform/ballerina-lang) to your GitHub account and execute following command to clone it.
```bash
git clone --recursive https://github.com//ballerina-lang.git
```
2. Navigate to the `` directory, and execute the command below to update the Git submodules.
```bash
git submodule update --init
```
3. Execute one of the commands below to start the build process.
**For Unix/macOS:**
```bash
./gradlew clean build
```
**For Windows:**
```bash
gradlew clean build
```
4. Extract the built Ballerina Language distribution (i.e., the `/distribution/zip/jballerina-tools/build/distributions/jballerina-tools-.zip` file) to a preferred location.
5. Configure the environment variables below.
**For Unix/macOS:**
```bash
# Set up the `BALLERINA_HOME` environment variable.
export BALLERINA_HOME="/jballerina-tools-";
# Include the binaries to the system `PATH`.
PATH=$BALLERINA_HOME/bin:$PATH;
export PATH;
```
**For Windows:**
```bash
# Set up the `BALLERINA_HOME` environment variable.
set BALLERINA_HOME="\jballerina-tools-";
# Include the binaries to the system `PATH`.
set PATH=%PATH%:%BALLERINA_HOME%\bin;
```
### Test the runtime build
Since this runtime build is just a plain Ballerina language build, you only have the basic language features and JBallerina Java (Java Introp) API in it.
Therefore, follow the steps below to write a basic Ballerina program using only those functionalities to test the runtime build.
1. Create a `hello_world.bal` file with the code below.
```ballerina
import ballerina/jballerina.java;
public function main(string... args) {
var systemOut = out();
println(systemOut, java:fromString("Hello, World!"));
}
// Retrieves the current System output stream
public function out() returns handle = @java:FieldGet {
name: "out",
'class: "java.lang.System"
} external;
// Calls `println` method of the `PrintStream`
function println(handle receiver, handle message) = @java:Method {
paramTypes: ["java.lang.String"],
'class: "java.io.PrintStream"
} external;
```
2. Execute the command below to build and run this program.
```bash
bal run hello_world.bal
```
If your build is successful, you view the output below.
```bash
Hello, World!
```
================================================
FILE: examples/access-json-elements/access_json_elements.bal
================================================
import ballerina/io;
import ballerina/lang.value;
public function main() returns error? {
json[] users = [
{
user: {
name: {
firstName: "John",
lastname: "Smith"
},
age: 24
}
},
null
];
// Field access is allowed on the `json` typed variable. However, the return
// type would be a union of `json` and `error`.
json firstUserName = check users[0].user.name;
// This is converted to `check value:ensureType(firstUserName.firstName, string)`.
// As the expected type is correct, the conversion is successful.
string firstName = check firstUserName.firstName;
io:println("Value of first name: " + firstName);
// This is same as above.
firstName = check value:ensureType(firstUserName.firstName, string);
io:println("Value of first name: " + firstName);
}
================================================
FILE: examples/access-json-elements/access_json_elements.md
================================================
# Access JSON elements
Ballerina defines certain types as lax types for which static typing rules are less strict. `json` is defined to be a lax type along with any `map` where `T` is a lax type.
For example, field access (`.`) and optional field access (`?.`), which are generally allowed on records and objects for fields that are defined in the type descriptors, are also allowed additionally on lax types. For such operations, some of the type checkings are moved from compile time to runtime.
The best way of accessing JSON elements is to convert the `json` value to a user-defined type.
`check Expr` is treated as `check val:ensureType(Expr, s)` when the `Expr` is a subtype of JSON and the expected type is a subtype of `()|boolean|int|float|decimal|string`. `s` is a typedesc value representing the expected type.
::: code access_json_elements.bal :::
Run the example as follows.
::: out access_json_elements.out :::
## Related links
- [JSON type](/learn/by-example/json-type)
- [Check expression](/learn/by-example/check-expression)
- [ensureType function](/learn/by-example/ensureType-function)
================================================
FILE: examples/access-json-elements/access_json_elements.metatags
================================================
description: This BBE demonstrates how to get elements in JSON, check whether a given element is available in JSON, handle errors when a member is not available, get members in JSON array, and access a JSON value in Ballerina.
keywords: ballerina, ballerina by example, bbe, json type, json, check, ensureType, lax, access
================================================
FILE: examples/access-json-elements/access_json_elements.out
================================================
$ bal run access_json_elements.bal
Value of first name: John
Value of first name: John
================================================
FILE: examples/access-optional-json-elements/access_optional_json_elements.bal
================================================
import ballerina/io;
public function main() returns error? {
json user = {
name: {
firstName: "John",
lastname: "Smith"
},
age: 24
};
// Since the availability of `address` field is unknown, optional access is used
string? address = check user?.address;
if address is string {
io:println("Address: " + address);
}
// Optional access can be used again on fields accessed with optional access
string? firstName = check user?.name?.firstName;
if firstName is string {
io:println("First name: " + firstName);
}
}
================================================
FILE: examples/access-optional-json-elements/access_optional_json_elements.md
================================================
# Accessing optional JSON elements
If there is no prior knowledge of the availability of a particular field, optional field access (`?.`) can be used on the `json` value. If the particular field is not available, it will return `nil`.
::: code access_optional_json_elements.bal :::
Run the example as follows.
::: out access_optional_json_elements.out :::
## Related links
- [JSON type](/learn/by-example/json-type/)
- [Check expression](/learn/by-example/check-expression/)
- [Optional fields](/learn/by-example/optional-fields/)
================================================
FILE: examples/access-optional-json-elements/access_optional_json_elements.metatags
================================================
description: This BBE demonstrates how to get elements in json, check whether given element is available in json, handle errors when member is not available, get member in json array, access a json value, optional access on json members.
keywords: ballerina, ballerina by example, bbe, json type, json, check, lax, access
================================================
FILE: examples/access-optional-json-elements/access_optional_json_elements.out
================================================
$ bal run access_optional_json_elements.bal
First name: John
================================================
FILE: examples/advanced-conflict-handling/advanced_conflict_handling.bal
================================================
import ballerina/io;
type Student record {|
string name;
int score;
|};
public function main() {
Student[] students = [
{name: "John", score: 100},
{name: "Jane", score: 150},
{name: "John", score: 200}
];
// Create a map of the student `name` and the `score` where we replace the value of the duplicate key if the new `score` is even.
// The old `score` `100` of `John` will be replaced by the new `score` 200` since `replaceIfSafe()`returns `nil` for even scores.
map|error studentScores = map from var {name, score} in students
select [name, score]
on conflict replaceIfSafe(score);
io:println(studentScores);
students = [
{name: "John", score: 100},
{name: "Jane", score: 150},
{name: "John", score: 211}
];
// The result of this will be an error since the key `John` is duplicated and the `replaceIfSafe()`returns an error for odd `score` `211`.
map|error studentScores2 = map from var {name, score} in students
select [name, score]
on conflict replaceIfSafe(score);
io:println(studentScores2);
}
function replaceIfSafe(int score) returns error? {
if score % 2 != 0 {
return error("Key Conflict", message = "Duplicate key has an odd score");
}
}
================================================
FILE: examples/advanced-conflict-handling/advanced_conflict_handling.md
================================================
# Advanced conflict handling
We can implement a custom conflict-handling policy to determine whether to replace a value or throw an error in the event of conflicting keys when constructing a map or table.
::: code advanced_conflict_handling.bal :::
::: out advanced_conflict_handling.out :::
## Related links
- [On conflict clause](/learn/by-example/on-conflict-clause)
- [Query expressions](/learn/by-example/query-expressions)
- [Sort iterable objects using query](/learn/by-example/sort-iterable-objects)
- [Let clause in query expression](/learn/by-example/let-clause)
- [Limit clause in query expression](/learn/by-example/limit-clause)
- [Joining iterable objects using query](/learn/by-example/joining-iterable-objects)
- [Querying tables](/learn/by-example/querying-tables)
- [Create maps with query expression](/learn/by-example/create-maps-with-query)
- [Create tables with query expression](/learn/by-example/create-tables-with-query)
- [Create streams with query expression](/learn/by-example/create-streams-with-query)
- [Nested query expressions](/learn/by-example/nested-query-expressions)
================================================
FILE: examples/advanced-conflict-handling/advanced_conflict_handling.metatags
================================================
description: This BBE demonstrates writing custom conflict handling logic when constructing a map or a table.
keywords: ballerina, ballerina by example, bbe, duplicate, key, map, table, query, on conflict, error
================================================
FILE: examples/advanced-conflict-handling/advanced_conflict_handling.out
================================================
$ bal run advanced_conflict_handling.bal
{"John":200,"Jane":150}
error("Key Conflict",message="Duplicate key has an odd score")
================================================
FILE: examples/aggregation/aggregation.bal
================================================
import ballerina/io;
public function main() returns error? {
var orders = [
{orderId: 1, itemName: "A", price: 23.4, quantity: 2},
{orderId: 1, itemName: "A", price: 20.4, quantity: 1},
{orderId: 2, itemName: "B", price: 21.5, quantity: 3},
{orderId: 1, itemName: "B", price: 21.5, quantity: 3}
];
var items = from var {orderId, itemName} in orders
// The `group by` clause create groups for each `orderId`.
// The `itemName` is a non-grouping key and it becomes a sequence variable.
group by orderId
select [itemName];
// List of items per `orderId`
io:println(items);
var quantities = from var {itemName, quantity} in orders
// The `group by` clause create groups for each `itemName`.
// The `quantity` is a non-grouping key and it becomes a sequence variable.
group by itemName
select {itemName, quantity: sum(quantity)};
// List of quantity per item
io:println(quantities);
var income = from var {price, quantity} in orders
let var totPrice = price*quantity
// The `collect` clause creates a single group and all variables become
// non-grouping keys
collect sum(totPrice);
// Total Income from orders
io:println(income);
}
================================================
FILE: examples/aggregation/aggregation.md
================================================
# Aggregation
The `group by` clause in the query expression can group the elements in a collection. Grouping happens based on the grouping keys provided in `group by` clause. For each group, grouping keys are unique. All other variables other than grouping keys are called non-grouping keys. For each group, non-grouping keys become sequence variables. Those variables can be used as a list or an argument to a rest parameter of a langlib function.
The `collect` clause collects the collection into one group. All the variables become aggregated variables and those variables can be used as a list or an argument to a rest parameter of a langlib function same as in `group by`.
::: code aggregation.bal :::
::: out aggregation.out :::
## Related links
- [Query expressions](/learn/by-example/query-expressions)
- [Let clause in query expression](/learn/by-example/let-clause)
- [Limit clause in query expression](/learn/by-example/limit-clause)
- [Joining iterable objects using query](/learn/by-example/joining-iterable-objects)
- [Querying tables](/learn/by-example/querying-tables)
- [Create maps with query expression](/learn/by-example/create-maps-with-query)
- [Create tables with query expression](/learn/by-example/create-tables-with-query)
- [Create streams with query expression](/learn/by-example/create-streams-with-query)
- [On conflict clause in query expression](/learn/by-example/on-conflict-clause)
- [Nested query expressions](/learn/by-example/nested-query-expressions)
================================================
FILE: examples/aggregation/aggregation.metatags
================================================
description: This BBE demonstrates how to group a collection, how to handle non grouping keys.
keywords: ballerina, ballerina by example, bbe, group, non-grouping keys, aggregate, collect
================================================
FILE: examples/aggregation/aggregation.out
================================================
$ bal run aggregation.bal
[["A","A","B"],["B"]]
[{"itemName":"A","quantity":3},{"itemName":"B","quantity":6}]
196.2
================================================
FILE: examples/ai-agent-external-endpoint-integration/ai_agent_external_endpoint_integration.bal
================================================
import ballerina/ai;
import ballerina/io;
import ballerinax/googleapis.calendar;
import ballerinax/googleapis.gmail;
// Configuration for external endpoints.
configurable string refreshToken = ?;
configurable string clientId = ?;
configurable string clientSecret = ?;
configurable string refreshUrl = ?;
configurable string userEmail = ?;
// Create clients for Gmail and Calendar APIs.
final gmail:Client gmailClient = check new ({
auth: {refreshToken: refreshToken, clientId, clientSecret, refreshUrl}
});
final calendar:Client calendarClient = check new (config = {
auth: {clientId, clientSecret, refreshToken: refreshToken, refreshUrl}
});
// Define tools for the agent to interact with Gmail and Calendar APIs.
@ai:AgentTool
isolated function readUnreadEmails() returns gmail:Message[]|error {
gmail:ListMessagesResponse messageList =
check gmailClient->/users/me/messages(q = "label:INBOX is:unread");
gmail:Message[]? messages = messageList.messages;
if messages is () {
return [];
}
gmail:Message[] completeMessages = from gmail:Message message in messages
select check gmailClient->/users/me/messages/[message.id](format = "full");
return completeMessages;
}
@ai:AgentTool
isolated function sendEmail(string[] to, string subject, string body)
returns gmail:Message|error {
return gmailClient->/users/me/messages/send.post({to, subject, bodyInText: body});
}
@ai:AgentTool
isolated function getCalendarEvents()
returns stream|error {
return calendarClient->getEvents(userEmail);
}
@ai:AgentTool
isolated function createCalendarEvent(calendar:InputEvent event)
returns calendar:Event|error {
return calendarClient->createEvent(userEmail, event);
}
final ai:Agent personalAssistantAgent = check new (
systemPrompt = {
role: "Personal Assistant",
instructions: string `You are an intelligent personal AI assistant
designed to help users stay organized and efficient. You have
access to the user's email and calendar through secure API
integrations.
Your tasks may require reading and summarizing unread emails,
sending emails on behalf of the user, helping schedule meetings,
and managing calendar events.
When interacting with the user, always adhere to the following:
- Respond in a natural and professional tone.
- Always confirm before making changes to the user's calendar or
sending emails.
- Provide concise summaries when retrieving information unless the
user requests details.
- Prioritize clarity, efficiency, and user convenience in all tasks.`
},
// Use the default model provider (with configuration added via a
// Ballerina VS Code command).
model = check ai:getDefaultModelProvider(),
// Specify the tools the agent can use.
tools = [readUnreadEmails, sendEmail, getCalendarEvents, createCalendarEvent]
);
public function main() returns error? {
while true {
string userInput = io:readln("User (or 'exit' to quit): ");
if userInput == "exit" {
break;
}
// Pass the user input to the agent and get a response.
string response = check personalAssistantAgent.run(userInput);
io:println("Agent: ", response);
}
}
================================================
FILE: examples/ai-agent-external-endpoint-integration/ai_agent_external_endpoint_integration.md
================================================
# AI agents with external endpoints as tools
Ballerina enables developers to easily create intelligent AI agents powered by large language models (LLMs) and integrated with tools, including local tools, MCP tools, and external APIs. These AI agents can automate complex workflows, interact with users through natural language, and seamlessly connect with internal and external systems.
This example demonstrates how to create an AI agent that can access Gmail and Google Calendar by integrating with Google APIs as external endpoints. The agent functions as a personal assistant that can read emails, send emails, view calendar events, and create new calendar entries.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
> Note: Follow the [connector setup guide](https://central.ballerina.io/ballerinax/googleapis.gmail/latest#setup-guide) to obtain the connector configuration.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code ai_agent_external_endpoint_integration.bal :::
::: out ai_agent_external_endpoint_integration.out :::
## Related links
- [The Agent with local tools example](/learn/by-example/ai-agent-local-tools)
- [The Agent with MCP integration example](/learn/by-example/ai-agent-mcp-integration)
- [The Agent with tool kits example](/learn/by-example/ai-agent-tool-kit)
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/ai-agent-external-endpoint-integration/ai_agent_external_endpoint_integration.metatags
================================================
description: This BBE demonstrates AI agents integrated with external endpoints as tools.
keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider, agent
================================================
FILE: examples/ai-agent-external-endpoint-integration/ai_agent_external_endpoint_integration.out
================================================
$ bal run ai_agent_external_endpoint_integration.bal
User (or 'exit' to quit): Do I have any meetings today?
Agent: You have one meeting scheduled for today:
- **Event:** Sync up on the monthly meet up
- **Time:** 3:30 PM to 4:30 PM (Asia/Colombo timezone)
If you need any further assistance regarding this meeting, just let me know!
User (or 'exit' to quit): Can you check my mail to see if I've missed any meeting requests?
Agent: You have an unread email regarding a meeting request:
- **From:** Emma ()
- **Subject:** Catch up
- **Message:** Emma wants to discuss Ballerina's Gen AI app building capabilities with you. She is available at 4 PM today (September 27th) or on Monday (September 29th) at 10 AM.
Would you like to respond to Emma and confirm a time?
User (or 'exit' to quit): Yes, please schedule the meeting. The time is okay with me, but can you check my calendar to see if I have any appointments already?
Agent: You have a meeting scheduled today from 3:30 PM to 4:30 PM. Since Emma's proposed time for the meeting is at 4 PM today, it would overlap with your existing appointment.
Would you prefer to schedule the meeting with Emma for Monday, September 29th at 10 AM instead?
User (or 'exit' to quit): Yes, please
Agent: The meeting with Emma has been successfully scheduled for Monday, September 29th from 10:00 AM to 11:00 AM.
You can view the event [here]().
If there's anything else you need, feel free to ask!
User (or 'exit' to quit): exit
================================================
FILE: examples/ai-agent-local-tools/ai_agent_local_tools.bal
================================================
import ballerina/ai;
import ballerina/io;
import ballerina/time;
import ballerina/uuid;
type Task record {|
string description;
time:Date dueBy?;
time:Date createdAt = time:utcToCivil(time:utcNow());
time:Date completedAt?;
boolean completed = false;
|};
// Simple in-memory task management.
isolated map tasks = {
"a2af0faa-3b73-4184-9be1-87b29a963be6": {
description: "Buy groceries",
dueBy: time:utcToCivil(time:utcAddSeconds(time:utcNow(), 60 * 5))
}
};
// Define the functions that the agent can use as tools.
// The LLM will identify the arguments to pass to these functions
// based on the user input and the tool (function) signatures.
@ai:AgentTool
isolated function addTask(string description, time:Date? dueBy) returns error? {
lock {
tasks[uuid:createRandomUuid()] = {description, dueBy: dueBy.clone()};
}
}
@ai:AgentTool
isolated function listTasks() returns Task[] {
lock {
return tasks.toArray().clone();
}
}
@ai:AgentTool
isolated function getCurrentDate() returns time:Date {
time:Civil {year, month, day} = time:utcToCivil(time:utcNow());
return {year, month, day};
}
// Define an AI agent with a system prompt and a set of tools.
// The agent will use these tools to help manage a task list,
// following the system prompt instructions.
final ai:Agent taskAssistantAgent = check new ({
systemPrompt: {
role: "Task Assistant",
instructions: string `You are a helpful assistant for
managing a to-do list. You can manage tasks and
help a user plan their schedule.`
},
// Specify the functions the agent can use as tools.
tools: [addTask, listTasks, getCurrentDate],
// Use the default model provider (with configuration added
// via a Ballerina VS Code command).
model: check ai:getDefaultModelProvider()
});
public function main() returns error? {
while true {
string userInput = io:readln("User (or 'exit' to quit): ");
if userInput == "exit" {
break;
}
// Pass the user input to the agent and get a response.
string response = check taskAssistantAgent.run(userInput);
io:println("Agent: ", response);
}
}
================================================
FILE: examples/ai-agent-local-tools/ai_agent_local_tools.md
================================================
# AI agents with local tools
Ballerina enables developers to easily create intelligent AI agents powered by large language models (LLMs) and integrated with tools, including local tools, MCP tools, and external APIs. These AI agents can automate complex workflows, interact with users through natural language, and seamlessly connect with internal and external systems.
This example demonstrates how to create an AI agent that can manage a to-do list by using local functions as tools, while maintaining a conversation with the user.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code ai_agent_local_tools.bal :::
::: out ai_agent_local_tools.out :::
## Related links
- [The Agent with MCP integration example](/learn/by-example/ai-agent-mcp-integration)
- [The Agent with external endpoint integration example](/learn/by-example/ai-agent-external-endpoint-integration)
- [The Agent with tool kits example](/learn/by-example/ai-agent-tool-kit)
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/ai-agent-local-tools/ai_agent_local_tools.metatags
================================================
description: This BBE demonstrates AI agents.
keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider, agent
================================================
FILE: examples/ai-agent-local-tools/ai_agent_local_tools.out
================================================
$ bal run ai_agent_local_tools.bal
User (or 'exit' to quit): What do I have on my plate today?
Agent: You have the following task on your plate today:
- **Buy groceries** (Due on September 26, 2025, at 5:44 AM UTC)
Let me know if you need help with anything else!
User (or 'exit' to quit): What about the rest of the week?
Agent: Currently, you only have one task for the week:
- **Buy groceries** (Due today, September 26, 2025, at 5:44 AM UTC)
If you have any other tasks to add or if you need help planning your week, just let me know!
User (or 'exit' to quit): I have to pay my WiFi bill by tomorrow and meet Jane for tea at 4pm on the 28th.
Agent: Your tasks have been successfully added! Here’s your updated to-do list for the week:
1. **Buy groceries** (Due today, September 26, 2025, at 5:44 AM UTC)
2. **Pay WiFi bill** (Due tomorrow, September 27, 2025)
3. **Meet Jane for tea** (Scheduled for September 28, 2025, at 4:00 PM UTC)
Let me know if you need any further assistance!
User (or 'exit' to quit): exit
================================================
FILE: examples/ai-agent-mcp-integration/ai_agent_mcp_integration.bal
================================================
import ballerina/ai;
import ballerina/io;
// Define an MCP toolkit to connect to the MCP service.
// This allows using all the tools registered with the MCP service.
// Alternatively, specific tools can be used by specifying them as the second
// argument (e.g., `check new ("http://localhost:9090/mcp", ["getCurrentWeather"])`).
final ai:McpToolKit weatherMcpConn = check new ("http://localhost:9090/mcp");
final ai:Agent weatherAgent = check new (
systemPrompt = {
role: "Weather-aware AI Assistant",
instructions: string `You are a smart AI assistant that can assist
a user based on accurate and timely weather information.`
},
tools = [weatherMcpConn],
// Use the default model provider (with configuration added
// via a Ballerina VS Code command).
model = check ai:getDefaultModelProvider()
);
public function main() returns error? {
while true {
string userInput = io:readln("User (or 'exit' to quit): ");
if userInput == "exit" {
break;
}
// Pass the user input to the agent and get a response.
string response = check weatherAgent.run(userInput);
io:println("Agent: ", response);
}
}
================================================
FILE: examples/ai-agent-mcp-integration/ai_agent_mcp_integration.md
================================================
# AI agents with MCP tools
Ballerina enables developers to easily create intelligent AI agents powered by large language models (LLMs) and integrated with tools, including local tools, MCP tools, and external APIs. These AI agents can automate complex workflows, interact with users through natural language, and seamlessly connect with internal and external systems.
This example demonstrates how to create an AI agent that can access weather information by integrating with a Model Context Protocol (MCP) service, by simply defining an MCP toolkit.
> Note: You can use this agent with the [MCP service example](/learn/by-example/mcp-service/).
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code ai_agent_mcp_integration.bal :::
::: out ai_agent_mcp_integration.out :::
## Related links
- [The Agent with local tools example](/learn/by-example/ai-agent-local-tools)
- [The Agent with external endpoint integration example](/learn/by-example/ai-agent-external-endpoint-integration)
- [The Agent with tool kits example](/learn/by-example/ai-agent-tool-kit)
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/ai-agent-mcp-integration/ai_agent_mcp_integration.metatags
================================================
description: This BBE demonstrates AI agents integrated with MCP tools.
keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider, agent, mcp
================================================
FILE: examples/ai-agent-mcp-integration/ai_agent_mcp_integration.out
================================================
$ bal run ai_agent_mcp_integration.bal
User (or 'exit' to quit): Should I go for a walk in Colombo today?
Agent: The current weather in Colombo is sunny with a temperature of 27°C and humidity at 80%. It seems like a great day for a walk! Enjoy your time outdoors!
User (or 'exit' to quit): What about tomorrow?
Agent: Tomorrow in Colombo, the weather is expected to be cloudy with a high of 30°C and a low of 26°C. There's a 65% chance of precipitation, and wind speeds will be around 17 km/h.
While it may not be as sunny as today, you could still go for a walk, but keep an eye on the clouds and the potential for light rain. Enjoy!
User (or 'exit' to quit): exit
================================================
FILE: examples/ai-agent-tool-kit/ai_agent_tool_kit.bal
================================================
import ballerina/ai;
import ballerina/io;
import ballerina/time;
import ballerina/uuid;
type Task record {|
string description;
time:Date dueBy?;
time:Date createdAt = time:utcToCivil(time:utcNow());
time:Date completedAt?;
boolean completed = false;
|};
// A tool kit to manage a set of tasks.
public isolated class TaskManagerToolkit {
*ai:BaseToolKit;
private final map tasks = {};
// The `getTools` method describes the tools provided by this tool kit.
public isolated function getTools() returns ai:ToolConfig[] =>
// The `ai:getToolConfigs` function generates the tool configurations for the specified tools.
ai:getToolConfigs([self.addTask, self.listTasks]);
// Tool to add a new task.
@ai:AgentTool
isolated function addTask(string description, time:Date? dueBy = ()) {
lock {
self.tasks[uuid:createRandomUuid()] = {
description: description,
dueBy: dueBy.clone()
};
}
}
// Tool to list all current tasks.
@ai:AgentTool
isolated function listTasks() returns map {
lock {
return self.tasks.clone();
}
}
}
@ai:AgentTool
isolated function getCurrentDate() returns time:Date {
time:Civil {year, month, day} = time:utcToCivil(time:utcNow());
return {year, month, day};
}
// Define an AI agent with a system prompt and a set of tools.
// The agent will use these tools to help manage a task list,
// following the system prompt instructions.
final ai:Agent taskAssistantAgent = check new ({
systemPrompt: {
role: "Task Assistant",
instructions: string `You are a helpful assistant for
managing a to-do list. You can manage tasks and
help a user plan their schedule.`
},
// Include the tool kit in tools the agent can use.
tools: [new TaskManagerToolkit(), getCurrentDate],
// Use the default model provider (with configuration added
// via a Ballerina VS Code command).
model: check ai:getDefaultModelProvider(),
maxIter: 10
});
public function main() returns error? {
while true {
string userInput = io:readln("User (or 'exit' to quit): ");
if userInput == "exit" {
break;
}
// Pass the user input to the agent and get a response.
string response = check taskAssistantAgent.run(userInput);
io:println("Agent: ", response);
}
}
================================================
FILE: examples/ai-agent-tool-kit/ai_agent_tool_kit.md
================================================
# AI agents with tool kits
Ballerina enables developers to easily create intelligent AI agents powered by large language models (LLMs) and integrated with tools, including local tools, MCP tools, and external APIs. These AI agents can automate complex workflows, interact with users through natural language, and seamlessly connect with internal and external systems.
This example demonstrates how to create an AI agent that can manage a to-do list by using a toolkit that encapsulates related tools and state. Toolkits allow for better encapsulation and reusability compared to using standalone functions, especially when building complex agents with multiple related capabilities.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code ai_agent_tool_kit.bal :::
::: out ai_agent_tool_kit.out :::
## Related links
- [The Agent with local tools example](/learn/by-example/ai-agent-local-tools)
- [The Agent with MCP integration example](/learn/by-example/ai-agent-mcp-integration)
- [The Agent with external endpoint integration example](/learn/by-example/ai-agent-external-endpoint-integration)
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/ai-agent-tool-kit/ai_agent_tool_kit.metatags
================================================
description: This BBE demonstrates AI agents with tool kits.
keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider, agent
================================================
FILE: examples/ai-agent-tool-kit/ai_agent_tool_kit.out
================================================
$ bal run ai_agent_tool_kit.bal
User (or 'exit' to quit): Hello
Agent: Hello! How can I assist you today? Do you need help with your to-do list or planning your schedule?
User (or 'exit' to quit): I have to pay my WiFi bill today and meet Jane for tea at 4pm on the 28th.
Agent: I've added your tasks to the to-do list:
1. Pay WiFi bill (due today)
2. Meet Jane for tea at 4 PM on the 28th of October 2025
Would you like to do anything else, such as check your current tasks or add more?
User (or 'exit' to quit): What do I have on my plate today?
Agent: Today, you have the following task:
1. **Pay WiFi bill** (due today)
Would you like to mark it as completed or do anything else?
User (or 'exit' to quit): exit
================================================
FILE: examples/alternate-receive/alternate_receive.bal
================================================
import ballerina/http;
import ballerina/io;
import ballerina/lang.runtime;
type Response record {
record {
string 'worker;
} args;
};
// Concurrently fetch content from two URLs using workers and
// return the first non-error value received.
function getFirstFetched(string url1, string url2) returns string? {
// Workers `w1` and `w2` fetch content from `url1` and `url2` respectively.
worker w1 {
string|error result = fetch(url1);
result -> w3;
}
worker w2 {
runtime:sleep(3);
string|error result = fetch(url2);
result -> w3;
}
// Worker `w3` waits until one of the workers sends a non-error value.
worker w3 returns string? {
// The value of the variable `result` is set as soon as
// a non-error message is received from either worker `w1` or `w2`.
string|error result = <- w1 | w2;
return result is error ? () : result;
}
// The value returned from worker `w3` is set to the variable `w3Result`.
string? w3Result = wait w3;
return w3Result;
}
function fetch(string url) returns string|error {
http:Client cl = check new (url);
Response response = check cl->get("");
return response.args.'worker;
}
public function main() {
// Both arguments passed to the `getFirstFetched` function are valid URLs.
// Thus, the alternate receive action in worker `w3` sets the
// first value it receives from a worker as the result.
string? firstFetched = getFirstFetched("https://postman-echo.com/get?worker=w1",
"https://postman-echo.com/get?worker=w2");
io:println(firstFetched);
// The first argument passed to the `getFirstFetched` function is an invalid URL.
// Therefore, the worker `w1` in the `getFirstFetched` function returns an error.
// Thus, the alternate receive action in worker `w3` waits further
// and sets the value that is received from `w2` as the result.
firstFetched = getFirstFetched("https://postman-echo.com/ge?worker=w4",
"https://postman-echo.com/get?worker=w5");
io:println(firstFetched);
}
================================================
FILE: examples/alternate-receive/alternate_receive.md
================================================
# Alternate receive
The alternate receive action can be used to receive one of multiple values corresponding to multiple send actions. It operates by waiting until it receives a non-error message, a panic termination status on a closed channel, or the closure of all channels. The alternative receive action sets the first non-error value it receives as the result. If all the channels return errors, it sets the last received error as the result.
::: code alternate_receive.bal :::
::: out alternate_receive.out :::
================================================
FILE: examples/alternate-receive/alternate_receive.metatags
================================================
description: This BBE demonstrates the use of the alternate receive action in inter-worker communication
keywords: ballerina, ballerina by example, bbe, worker, alternate receive
================================================
FILE: examples/alternate-receive/alternate_receive.out
================================================
$ bal run alternate_receive.bal
w1
w5
================================================
FILE: examples/alternate-wait/alternate_wait.bal
================================================
import ballerina/http;
import ballerina/io;
// Fetch from A or B.
function altFetch(string urlA, string urlB) returns string|error {
worker A returns string|error {
return fetch(urlA);
}
worker B returns string|error {
return fetch(urlB);
}
// The `wait` action can be used to wait for one of several workers.
// This function will return as soon as the return value of either
// `A` or `B` is available.
return wait A | B;
}
public function main() returns error? {
string res =
check altFetch("https://postman-echo.com/get?lang=ballerina",
"https://postman-echo.com/get?greeting=hello");
io:println(res);
}
function fetch(string url) returns string|error {
http:Client cl = check new (url);
map payload = check cl->get("");
return payload["args"].toString();
}
================================================
FILE: examples/alternate-wait/alternate_wait.md
================================================
# Alternate wait
The `wait` action can be used to wait for one of several workers.
::: code alternate_wait.bal :::
::: out alternate_wait.out :::
================================================
FILE: examples/alternate-wait/alternate_wait.metatags
================================================
description: This BBE demonstrates the alternate wait action
keywords: ballerina, ballerina by example, bbe, worker, alternate wait
================================================
FILE: examples/alternate-wait/alternate_wait.out
================================================
$ bal run alternate_wait.bal
{"lang":"ballerina"}
================================================
FILE: examples/annotations/annotations.bal
================================================
import ballerina/io;
// The `@display` annotation is applied to the `transform` function.
@display {
label: "Transform",
iconPath: "transform.png"
}
public function transform(string s) returns string {
return s.toUpperAscii();
}
type AnnotRecord record {|
string value;
|};
// Declares an annotation tag on the type.
annotation AnnotRecord annot on type;
// The `@annot` annotation applies to the `T1` record type.
@annot {
value: "T1"
}
type T1 record {
string name;
};
public function main() {
T1 a = {name: "John"};
typedesc t = typeof a;
// Access annotation.
AnnotRecord? ann = t.@annot;
io:println(ann);
}
================================================
FILE: examples/annotations/annotations.md
================================================
# Annotations
Annotations start with `@tag` and they come before what they apply to. Unprefixed tags refer to standard platform-defined annotations and prefixed tags refer to annotations declared in modules. The `@tag` can be followed by a record constructor expression.
::: code annotations.bal :::
::: out annotations.out :::
================================================
FILE: examples/annotations/annotations.metatags
================================================
description: This BBE demonstrates how to use annotations in Ballerina
keywords: ballerina, ballerina by example, bbe, annotations,
================================================
FILE: examples/annotations/annotations.out
================================================
$ bal run annotations.bal
{"value":"T1"}
================================================
FILE: examples/anonymous-function/anonymous_function.bal
================================================
import ballerina/io;
public function main() {
// Infer anonymous function.
// The type of `x` is inferred from the function signature in the expected type.
function (int) returns int increment = x => x + 1;
io:println(increment(1));
// The type of `x` and `y` are inferred from the function signature in the expected type.
function (int, int) returns int add = (x, y) => x + y;
io:println(add(1, 2));
// The explicit anonymous function.
// The type of the `x` parameter is explicitly defined in the function signature.
var incrementByTwo = function(int x) returns int => x + 2;
io:println(incrementByTwo(1));
// The type of the `x` and `y` parameters are explicitly defined in the function signature.
var subtract = function(int x, int y) returns int {
return x - y;
};
io:println(subtract(2, 1));
}
================================================
FILE: examples/anonymous-function/anonymous_function.md
================================================
# Anonymous function
The anonymous function is used to create function values. There are two different kinds of syntaxes in Ballerina to create anonymous functions. One is an explicit anonymous function of which the function is specified explicitly as usual with a function signature. The second is to infer an anonymous function of which the function type is inferred from the expected type.
::: code anonymous_function.bal :::
::: out anonymous_function.out :::
## Related links
- [Functions values](/learn/by-example/function-values/)
- [Function types](/learn/by-example/function-types/)
================================================
FILE: examples/anonymous-function/anonymous_function.metatags
================================================
description: This BBE demonstrates creating an anonymous function, which is commonly known as an arrow function or lambda function in Ballerina.
keywords: ballerina, ballerina by example, bbe, anonymous function, lambda function, arrow function, function values
================================================
FILE: examples/anonymous-function/anonymous_function.out
================================================
$ bal run anonymous_function.bal
2
3
3
1
================================================
FILE: examples/any-type/any_type.bal
================================================
import ballerina/io;
// A variable of type `any` can hold any value except an `error` value.
any x = 1;
public function main() {
// Can cast `any` to specific type.
int n = x;
io:println(n);
// The `lang.value` lang library contains functions that apply to multiple basic types.
// `x.toString()` converts `x` to a `string`.
string s = x.toString();
io:println(s == "1");
// Can test its type with the `is` operator.
float f = x is int|float ? x : 0.0;
io:println(f);
}
================================================
FILE: examples/any-type/any_type.md
================================================
# Any type
`any` means any value except an `error` value. Equivalent to a union of all non-error basic types. Use `any|error` for absolutely any value. The `lang.value` lang library contains functions that apply to multiple basic types.
::: code any_type.bal :::
::: out any_type.out :::
================================================
FILE: examples/any-type/any_type.metatags
================================================
description: This BBE demonstrates the any type in Ballerina
keywords: ballerina, ballerina by example, bbe, any, any type
================================================
FILE: examples/any-type/any_type.out
================================================
$ bal run any_type.bal
1
true
1.0
================================================
FILE: examples/anydata-to-yaml-string/anydata_to_yaml_string.bal
================================================
import ballerina/data.yaml;
import ballerina/io;
type ServerConfig record {|
string host;
int port;
DatabaseConfig database;
|};
type DatabaseConfig record {|
string dbName;
string username;
|};
public function main() returns error? {
ServerConfig serverConfig = {
database: {
dbName: "userDB",
username: "testUser"
},
port: 3000,
host: "localhost"
};
// Serialize a Ballerina value to a string in YAML format.
string serverConfigYamlStr = check yaml:toYamlString(serverConfig);
io:println(serverConfigYamlStr);
}
================================================
FILE: examples/anydata-to-yaml-string/anydata_to_yaml_string.md
================================================
# Serialize a Ballerina value to a string in YAML format
The `data.yaml` library provides the `toYamlString` function to serialize a value belonging to `anydata` to a string in YAML format.
For more information on the underlying module, see the [`data.yaml` module](https://lib.ballerina.io/ballerina/data.yaml/latest/).
::: code anydata_to_yaml_string.bal :::
::: out anydata_to_yaml_string.out :::
================================================
FILE: examples/anydata-to-yaml-string/anydata_to_yaml_string.metatags
================================================
description: This BBE demonstrates how to serialize a value belonging to `anydata` to a string in YAML format.
keywords: ballerina, ballerina by example, BBE, yaml, yaml to string, record to yaml
================================================
FILE: examples/anydata-to-yaml-string/anydata_to_yaml_string.out
================================================
$ bal run anydata_to_yaml_string.bal
host: localhost
port: 3000
database:
dbName: userDB
username: testUser
================================================
FILE: examples/anydata-type/anydata_type.bal
================================================
import ballerina/io;
anydata x1 = [1, "string", true];
// `x1.clone()` returns a deep copy with the same mutability.
anydata x2 = x1.clone();
// Checks deep equality.
boolean eq = (x1 == x2);
public function main() {
io:println(x2);
io:println(eq);
}
================================================
FILE: examples/anydata-type/anydata_type.md
================================================
# Anydata type
The type for plain data is `anydata`. It is a subtype of `any`. `==` and `!=` operators test for deep equality. `x.clone()` returns a deep copy with the same mutability. `x.cloneReadOnly()` returns a deep copy that is immutable.
Ballerina syntax uses `readonly` to mean immutable. Both `x.clone()` and `x.cloneReadOnly()` do not copy immutable parts of `x`. `const` structures are allowed. Equality and cloning handle cycles.
::: code anydata_type.bal :::
::: out anydata_type.out :::
================================================
FILE: examples/anydata-type/anydata_type.metatags
================================================
description: This BBE demonstrates anydata type in Ballerina.
keywords: ballerina, ballerina by example, bbe, anydata type
================================================
FILE: examples/anydata-type/anydata_type.out
================================================
$ bal run anydata_type.bal
[1,"string",true]
true
================================================
FILE: examples/array-map-symmetry/array_map_symmetry.bal
================================================
import ballerina/io;
public function main() {
// List constructors are used to construct list values.
// Arrays are lists with a uniform member type.
string[] list = ["foo", "bar"];
// Mapping constructors are used to construct mapping values.
// Maps are mappings with a uniform member type.
map mapping = {x: 1, y: 2};
// Indexing is used with lists and mappings to access members.
// Index type of lists is `int`.
string listMember = list[0];
io:println(listMember);
// Index type of mappings is `string`.
int? mapIndex = mapping["x"];
io:println(mapIndex);
// A tuple type can be used to define per-index member types in a list.
[int, string, boolean] tuple = [1, "John", true];
int firstTupleMember = tuple[0];
string secondTupleMember = tuple[1];
boolean thirdTupleMember = tuple[2];
io:println(`Tuple [${firstTupleMember}, ${secondTupleMember}, ${thirdTupleMember}]`);
// A record type can be used to define per-index member types in a mapping.
record {
int id;
string name;
boolean checked;
} rec = {id: 1, name: "John", checked: true};
int id = rec.id;
string name = rec.name;
boolean checked = rec.checked;
io:println(`record {id: ${id}; name: ${name}; checked: ${checked}}`);
// Declares a tuple type as an open type.
// Tuple type with zero or more boolean values after the first two members.
[int, string, boolean...] openList = [1, "John"];
io:println(openList);
[int, string, boolean...] openList2 = [1, "John", true, false];
io:println(openList2);
// Declares a record type as an open type.
// Record type with zero or more boolean fileds after the first two fields.
record {|
int id;
string name;
boolean...;
|} openRecord = {id: 1, name: "John"};
io:println(openRecord);
record {|
int id;
string name;
boolean...;
|} openRecord2 = {id: 1, name: "John", "checked": true};
io:println(openRecord2);
}
================================================
FILE: examples/array-map-symmetry/array_map_symmetry.md
================================================
# Array/map symmetry
Ballerina provides a fine symmetry between lists and mappings. Lists are indexed by integers and mappings are indexed by strings. In terms of the JSON format, lists are represented by arrays and mappings are represented by objects.
The constructor for lists uses square brackets and the constructor for mappings uses curly brackets. Both list and mappings types can be described in two ways using uniform member types and per-index member types. A list with a uniform `T` member type is an array declared as `T[]`. A mapping with a uniform `T` type member is a map declared as `map`. Similarly, a list with per-index member types `T0` and `T1` is a tuple, declared as `[T0, T1]`, and a mapping with per-index member types is a record declared as `record { Tx x; Ty y; }`. You can have an open type using the `...` notation. In the case of lists, an open type is declared as a tuple type as `[T0, Tr...]`. In the case of mappings, it is a record type declared as `record {| Tx x; Tr...; |}`.
::: code array_map_symmetry.bal :::
::: out array_map_symmetry.out :::
================================================
FILE: examples/array-map-symmetry/array_map_symmetry.metatags
================================================
description: This BBE demonstrates array map symmetry in Ballerina.
keywords: ballerina, ballerina by example, bbe, array, map, array map symmetry
================================================
FILE: examples/array-map-symmetry/array_map_symmetry.out
================================================
$ bal run array_map_symmetry.bal
foo
1
Tuple [1, John, true]
record {id: 1; name: John; checked: true}
[1,"John"]
[1,"John",true,false]
{"id":1,"name":"John"}
{"id":1,"name":"John","checked":true}
================================================
FILE: examples/arrays/arrays.bal
================================================
import ballerina/io;
const LENGTH = 3;
public function main() {
// Declare an int array of length 3.
int[3] numbers = [1, 2, 3];
io:println(numbers);
// Use a constant reference as the array length.
string[LENGTH] animals = ["cat", "dog", "mouse"];
io:println(animals);
// Declare a variable-length array.
int[] indexes = [1, 2];
indexes = [1, 2, 3, 4];
io:println(indexes);
// Length of the array can be inferred using `*`. The example below will declare an array of length 3.
string[*] colors = ["red", "green", "blue"];
io:println(colors);
// Elements of an array can be accessed using member access expression.
string firstColor = colors[0];
io:println(firstColor);
// Members of an array can be updated using member access expression in the LHS of an assignment.
colors[0] = "pink";
io:println(colors);
string[] names = ["Mike", "Amy", "Korina"];
// New members can be pushed to an open array by using the `array:push()` method.
names.push("Peter");
io:println(names);
int length = names.length();
// The `array:length()` method can be used to get the length of an array.
io:println(length);
// An element can be removed using the `array:remove(n)` method by passing the index.
string secondPerson = names.remove(1);
io:println(secondPerson);
io:println(names.length());
string[] fullNames = [];
// Array can be iterated using a foreach statement.
// There are other ways like query expressions for the same purpose.
// This will iterate over the `names` array and create a new array by adding the surnames to each name.
foreach string name in names {
fullNames.push(string `${name} Johnson`);
}
io:println(fullNames);
}
================================================
FILE: examples/arrays/arrays.md
================================================
# Arrays
An array can be used to hold a set of values of the same type. The array type can be defined as `T[n]` in which `T` is the element type and `n` is the fixed length of the array. `n` must be an integer literal or constant reference of type `int`. Optionally, you can create a variable-length array by defining an array without `n` as `T[]`.
The length of the array can be inferred from the context by defining the array as `T[*]`. The length of the array should be known in compile time.
An array with an inferred length is also a fixed-length array, where the length, inferred from the context, cannot be changed afterward.
::: code arrays.bal :::
::: out arrays.out :::
## Related links
- [Manipulating an array `(lang.array)`](https://lib.ballerina.io/ballerina/lang.array)
- [Tuples](/learn/by-example/tuples)
- [Nested arrays](/learn/by-example/nested-arrays)
- [Filler values of a list](/learn/by-example/filler-values-of-a-list)
- [List sub typing](/learn/by-example/list-subtyping)
- [List equality](/learn/by-example/list-equality)
================================================
FILE: examples/arrays/arrays.metatags
================================================
description: Create a collection in Ballerina, Create an array, Create fixed length collection, Create a variable length array, Create array without length, Get array size from the context, Accessing an array, Iterating an array, Updating an array.
keywords: ballerina, ballerina by example, bbe, arrays, mutable list, collection, length, size, list, array type, list type, infer length, access, remove, push, delete
================================================
FILE: examples/arrays/arrays.out
================================================
$ bal run arrays.bal
[1,2,3]
["cat","dog","mouse"]
[1,2,3,4]
["red","green","blue"]
red
["pink","green","blue"]
["Mike","Amy","Korina","Peter"]
4
Amy
3
["Mike Johnson","Korina Johnson","Peter Johnson"]
================================================
FILE: examples/asynchronize-message-passing/asynchronize_message_passing.bal
================================================
import ballerina/io;
public function main() {
worker A {
int num = 10;
// Sends the `10` integer value to the `B` worker asynchronously.
num -> B;
// Receives the `Hello` string from the `B` worker.
string msg = <- B;
io:println(string `Received string "${msg}" from worker B`);
}
worker B {
int num;
// Receives the `10` integer value from the `A` worker.
num = <- A;
io:println(string `Received integer "${num}" from worker A`);
// Sends the `Hello` string to the `A` worker asynchronously.
string msg = "Hello";
msg -> A;
}
}
================================================
FILE: examples/asynchronize-message-passing/asynchronize_message_passing.md
================================================
# Asynchronize message passing
Use `-> W` to send a message asynchronously to a `W` worker. These messages should be of the `anydata` type. The sending worker will send all messages even though the receiving worker panics or returns an error.
::: code asynchronize_message_passing.bal :::
::: out asynchronize_message_passing.out :::
================================================
FILE: examples/asynchronize-message-passing/asynchronize_message_passing.metatags
================================================
desdescription: This BBE demonstrates how the `async send` action can be used for concurrency in Ballerina.
keywords: ballerina, ballerina by example, bbe, async send, concurrency, worker
================================================
FILE: examples/asynchronize-message-passing/asynchronize_message_passing.out
================================================
$ bal run async_send.bal
Received integer "10" from worker A
Received string "Hello" from worker B
================================================
FILE: examples/asynchronous-function-calls/asynchronous_function_calls.bal
================================================
import ballerina/io;
public function main() {
// `start` calls a function asynchronously.
future fut = start foo();
// `wait` for `future` gives `T|error`.
int|error x = wait fut;
io:println(x);
}
function foo() returns int {
return 10;
}
================================================
FILE: examples/asynchronous-function-calls/asynchronous_function_calls.md
================================================
# Asynchronous function calls
`start` calls a function asynchronously and the function runs on a separate logical thread (`strand`). It is cooperatively multitasked by default.
The result will be of type `future` and `future` is a separate basic type. Waiting for the same `future` more than once gives an `error`. Use `f.cancel()` to terminate a `future`.
::: code asynchronous_function_calls.bal :::
::: out asynchronous_function_calls.out :::
================================================
FILE: examples/asynchronous-function-calls/asynchronous_function_calls.metatags
================================================
description: This BBE demonstrates how to use start action to call a function asynchronouly.
keywords: ballerina, ballerina by example, bbe, start, Asynchronous function call
================================================
FILE: examples/asynchronous-function-calls/asynchronous_function_calls.out
================================================
$ bal run asynchronous_function_calls.bal
10
================================================
FILE: examples/avro-serdes/avro_serdes.bal
================================================
import ballerina/avro;
import ballerina/io;
// Define a type, which is a subtype of anydata.
type Student record {
int id;
string name;
};
public function main() returns error? {
// Assign the value to the variable.
Student student = {
id: 1,
name: "John"
};
// Create a schema instance by passing the string value of an Avro schema.
avro:Schema schema = check new (string `{
"namespace": "example.avro",
"type": "record",
"name": "Student",
"fields": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
}
]
}`);
// Serialize the record value to bytes.
byte[] serializedData = check schema.toAvro(student);
// Deserialize the record value to bytes.
Student studentResult = check schema.fromAvro(serializedData);
// Print deserialized data.
io:println("Student ID: ", studentResult.id);
io:println("Student Name: ", studentResult.name);
}
================================================
FILE: examples/avro-serdes/avro_serdes.md
================================================
# Avro - Serialization/Deserialization
The `avro` module allows serializing and deserializing data with the `anydata` type. Initially, an `avro:Schema` instance must be created by providing a `string` value representing an Avro schema. The `toAvro()` and `fromAvro()` methods of the `avro` module serialize and deserialize data using the given Avro schema.
The `toAvro()` method serializes the data according to the specified Avro schema. The `fromAvro()` method accepts a `byte[]` argument containing serialized data and binds the deserialized value to the inferred data type determined by the user.
::: code avro_serdes.bal :::
Run the program by executing the following command.
::: out avro_serdes.out :::
## Related links
- [`avro` - API documentation](https://central.ballerina.io/ballerina/avro/)
- [`avro` - Specification](/spec/avro)
================================================
FILE: examples/avro-serdes/avro_serdes.metatags
================================================
description: This example demonstrates data serialization/deserialization using Avro schemas.
keywords: ballerina, ballerina by example, avro, serdes, serialization, deserialization
================================================
FILE: examples/avro-serdes/avro_serdes.out
================================================
$ bal run avro_serdes.bal
Student ID: 1
Student Name: John
================================================
FILE: examples/aws-lambda-dynamodb-trigger/aws_deploy.out
================================================
$ aws lambda create-function --function-name dynamoDBTrigger --zip-file fileb://aws-ballerina-lambda-functions.zip --handler aws-lambda-dynamodb-trigger.dynamoDBTrigger --runtime provided --role arn:aws:iam::908363916111:role/lambda-role--layers arn:aws:lambda:us-west-1:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
================================================
FILE: examples/aws-lambda-dynamodb-trigger/aws_lambda_dynamodb_trigger.bal
================================================
import ballerina/io;
import ballerinax/aws.lambda;
@lambda:Function
public function dynamoDBTrigger(lambda:Context ctx,
lambda:DynamoDBEvent event) returns json {
io:println(event.Records[0].dynamodb.Keys.toString());
return event.Records[0].dynamodb.Keys.toString();
}
================================================
FILE: examples/aws-lambda-dynamodb-trigger/aws_lambda_dynamodb_trigger.md
================================================
# AWS Lambda - DynamoDB trigger
This example creates a function, which will be executed for each entry added to a database in the [DynamoDB](https://aws.amazon.com/dynamodb/).
For more information, see the [AWS Lambda learn guide](https://ballerina.io/learn/aws-lambda/).
## Set up the prerequisites
For instructions, see [Set up the prerequisites](https://ballerina.io/learn/aws-lambda/#set-up-the-prerequisites).
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code aws_lambda_dynamodb_trigger.bal :::
## Build the function
Execute the command below to generate the AWS Lambda artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the AWS CLI command given by the compiler to create and publish the functions by replacing the respective AWS `$LAMBDA_ROLE_ARN`, `$REGION_ID`, and `$FUNCTION_NAME` values given in the command with your values.
::: out aws_deploy.out :::
## Invoke the function
Follow the instructions below to create a DynamoDB table for invoking this function.
1. In the IAM Console, click the corresponding role in the list, and click **Add permissions**.
2. Select **attach policies** from the drop-down menu, and add the **AWSLambdaDynamoDBExecutionRole** to the role.
3. Go to [DynamoDB](https://console.aws.amazon.com/dynamodbv2), and from the drop-down menu at the top RHS of the screen, select the **AWS region** in which you created the user and role.
4. Click **Create Table**, enter a table name and a partition key, and create the table (if you already have a table created, you can skip this step).
5. Click on the DynamoDB table you created, and then click the **Exports and streams** tab.
6. Click **Turn on** under **DynamoDB stream details**, select **Key attributes only** for the event type, and click **Turn on stream**.
8. Under the **Trigger** section, click **Create trigger**, select the `dynamoDBTrigger` from the drop-down, and click **Create trigger**.
9. Click **Explore table items**, and click **Create items** under the **Items returned** section.
10. Enter a value under the **Attributes** section to add an entry to the DynamoDB table to invoke the Lambda function, and click **Create item**.
11. Click the **Monitor** tab of the Lambda function in the AWS Management Console, and click **View CloudWatch logs** to check the logs via CloudWatch.
11. Under **Log streams** in CloudWatch, click on the topmost stream in the list and verify the object name in the logs.
12. Go to the AWS Lambda function and check the logs via CloudWatch to see the object identifier in the logs.
================================================
FILE: examples/aws-lambda-dynamodb-trigger/aws_lambda_dynamodb_trigger.metatags
================================================
description: This example creates a function, which will be executed for each entry added to a database in the DynamoDB.
keywords: ballerina, ballerina by example, aws lambda, dynamodb, serverless, cloud, function as a service
================================================
FILE: examples/aws-lambda-dynamodb-trigger/bal_build.out
================================================
$ bal build
Compiling source
wso2/aws_lambda_dynamodb_trigger:0.1.0
Generating executable
@awslambda:Function: dynamoDBTrigger
Run the following command to deploy each Ballerina AWS Lambda function:
aws lambda create-function --function-name $FUNCTION_NAME --zip-file fileb:///aws-lambda-s3-trigger/target/bin/aws-ballerina-lambda-functions.zip --handler aws-lambda-dynamodb-trigger.$FUNCTION_NAME --runtime provided --role $LAMBDA_ROLE_ARN --layers arn:aws:lambda:$REGION_ID:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
Run the following command to re-deploy an updated Ballerina AWS Lambda function:
aws lambda update-function-code --function-name $FUNCTION_NAME --zip-file fileb://aws-ballerina-lambda-functions.zip
================================================
FILE: examples/aws-lambda-dynamodb-trigger/bal_new.out
================================================
$ bal new aws_lambda_dynamodb_trigger
Created new package 'aws_lambda_dynamodb_trigger' at /Users/wso2/aws_lambda_dynamodb_trigger.
================================================
FILE: examples/aws-lambda-execution-context/aws_deploy.out
================================================
$ aws lambda create-function --function-name ctxinfo --zip-file fileb://aws-ballerina-lambda-functions.zip --handler aws-lambda-execution-context.ctxinfo --runtime provided --role arn:aws:iam::908363916111:role/lambda-role --layers arn:aws:lambda:us-west-1:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
================================================
FILE: examples/aws-lambda-execution-context/aws_lambda_execution_context.bal
================================================
import ballerinax/aws.lambda;
// The `lambda:Context` object contains request execution context information.
@lambda:Function
public function ctxinfo(lambda:Context ctx, json input) returns json|error {
return {
RequestID: ctx.getRequestId(),
DeadlineMS: ctx.getDeadlineMs(),
InvokedFunctionArn: ctx.getInvokedFunctionArn(),
TraceID: ctx.getTraceId(),
RemainingExecTime: ctx.getRemainingExecutionTime()
};
}
================================================
FILE: examples/aws-lambda-execution-context/aws_lambda_execution_context.md
================================================
# AWS Lambda - Execution context
The example below demonstrates how the execution context information of an AWS function can be retrieved.
For more information, see the [AWS Lambda learn guide](https://ballerina.io/learn/aws-lambda/).
## Set up the prerequisites
For instructions, see [Set up the prerequisites](https://ballerina.io/learn/aws-lambda/#set-up-the-prerequisites).
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code aws_lambda_execution_context.bal :::
## Build the function
Execute the command below to generate the AWS Lambda artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the AWS CLI command given by the compiler to create and publish the functions by replacing the respective AWS `$LAMBDA_ROLE_ARN`, `$REGION_ID`, and `$FUNCTION_NAME` values given in the command with your values.
::: out aws_deploy.out :::
## Invoke the function
Execute the commands below to invoke the function.
::: out invoke_functions.out :::
================================================
FILE: examples/aws-lambda-execution-context/aws_lambda_execution_context.metatags
================================================
description: The example below demonstrates how the execution context information of an AWS function can be retrieved.
keywords: ballerina, ballerina by example, aws lambda, execution context, serverless, cloud, function as a service
================================================
FILE: examples/aws-lambda-execution-context/bal_build.out
================================================
$ bal build
Compiling source
wso2/aws_lambda_execution_context:0.1.0
Generating executable
@awslambda:Function: ctxinfo
Run the following command to deploy each Ballerina AWS Lambda function:
aws lambda create-function --function-name $FUNCTION_NAME --zip-file fileb:///aws-lambda-execution-context/target/bin/aws-ballerina-lambda-functions.zip --handler aws_lambda_deployment.$FUNCTION_NAME --runtime provided --role $LAMBDA_ROLE_ARN --layers arn:aws:lambda:$REGION_ID:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
Run the following command to re-deploy an updated Ballerina AWS Lambda function:
aws lambda update-function-code --function-name $FUNCTION_NAME --zip-file fileb://aws-ballerina-lambda-functions.zip
================================================
FILE: examples/aws-lambda-execution-context/bal_new.out
================================================
$ bal new aws_lambda_execution_context
Created new package 'aws_lambda_execution_context' at /Users/wso2/aws_lambda_execution_context.
================================================
FILE: examples/aws-lambda-execution-context/invoke_functions.out
================================================
$ echo '{"MESSAGE":"HELLO"}' > input.json
$ aws lambda invoke --function-name ctxinfo ctxinfo-response.txt
{
"ExecutedVersion": "$LATEST",
"StatusCode": 200
}
$ cat ctxinfo-response.txt
{"RequestID":"d55f7d06-f2ab-4b6e-8606-482607785a91", "DeadlineMS":1548069389978, "InvokedFunctionArn":"arn:aws:lambda:us-west-2:908363916138:function:ctxinfo", "TraceID":"Root=1-5c45aa03-f8aff4c9e24dc4fbf48f2990;Parent=17ad3b290def98fd;Sampled=0", "RemainingExecTime":9946}
================================================
FILE: examples/aws-lambda-hello-world/aws_deploy.out
================================================
$ aws lambda create-function --function-name echo --zip-file fileb://aws-ballerina-lambda-functions.zip --handler aws-lambda-hello-world.echo --runtime provided --role arn:aws:iam::908363916111:role/lambda-role--layers arn:aws:lambda:us-west-1:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
================================================
FILE: examples/aws-lambda-hello-world/aws_lambda_hello_world.bal
================================================
import ballerina/io;
import ballerinax/aws.lambda;
// The `@lambda:Function` annotation marks a function to generate an AWS Lambda function.
@lambda:Function
public function echo(lambda:Context ctx, json input) returns json {
io:println(input.toJsonString());
return input;
}
================================================
FILE: examples/aws-lambda-hello-world/aws_lambda_hello_world.md
================================================
# AWS Lambda - Hello world
This example demonstrates how to write a simple echo function in AWS Lambda.
For more information, see the [AWS Lambda learn guide](https://ballerina.io/learn/aws-lambda/).
## Set up the prerequisites
For instructions, see [Set up the prerequisites](https://ballerina.io/learn/aws-lambda/#set-up-the-prerequisites).
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code aws_lambda_hello_world.bal :::
## Build the function
Execute the command below to generate the AWS Lambda artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the AWS CLI command given by the compiler to create and publish the functions by replacing the respective AWS `$LAMBDA_ROLE_ARN`, `$REGION_ID`, and `$FUNCTION_NAME` values given in the command with your values.
::: out aws_deploy.out :::
## Invoke the function
Execute the commands below to invoke the function.
::: out invoke_functions.out :::
================================================
FILE: examples/aws-lambda-hello-world/aws_lambda_hello_world.metatags
================================================
description: This example demonstrates how to write a simple echo function in AWS Lambda.
keywords: ballerina, ballerina by example, serverless, aws lambda, cloud, function as a service
================================================
FILE: examples/aws-lambda-hello-world/bal_build.out
================================================
$ bal build
Compiling source
wso2/aws_lambda_hello_world:0.1.0
Generating executable
@awslambda:Function: echo
Run the following command to deploy each Ballerina AWS Lambda function:
aws lambda create-function --function-name $FUNCTION_NAME --zip-file fileb:///aws-lambda-hello-world/target/bin/aws-ballerina-lambda-functions.zip --handler aws-lambda-hello-world.$FUNCTION_NAME --runtime provided --role $LAMBDA_ROLE_ARN --layers arn:aws:lambda:$REGION_ID:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
Run the following command to re-deploy an updated Ballerina AWS Lambda function:
aws lambda update-function-code --function-name $FUNCTION_NAME --zip-file fileb://aws-ballerina-lambda-functions.zip
================================================
FILE: examples/aws-lambda-hello-world/bal_new.out
================================================
$ bal new aws_lambda_hello_world
Created new package 'aws_lambda_hello_world' at /Users/wso2/aws_lambda_hello_world.
================================================
FILE: examples/aws-lambda-hello-world/invoke_functions.out
================================================
$ echo '{"MESSAGE":"HELLO"}' > input.json
$ aws lambda invoke --function-name echo --payload fileb://input.json echo-response.txt
{
"ExecutedVersion": "$LATEST",
"StatusCode": 200
}
$ cat echo-response.txt
{"MESSAGE":"HELLO"}
================================================
FILE: examples/aws-lambda-s3-trigger/aws_deploy.out
================================================
$ aws lambda create-function --function-name s3Trigger --zip-file fileb://aws-ballerina-lambda-functions.zip --handler aws-lambda-s3-trigger.s3Trigger --runtime provided --role arn:aws:iam::908363916111:role/lambda-role--layers arn:aws:lambda:us-west-1:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
================================================
FILE: examples/aws-lambda-s3-trigger/aws_lambda_s3_trigger.bal
================================================
import ballerina/io;
import ballerinax/aws.lambda;
@lambda:Function
public function s3Trigger(lambda:Context ctx,
lambda:S3Event event) returns json {
io:println(event.Records[0].s3.'object.key);
return event.Records[0].s3.'object.key;
}
================================================
FILE: examples/aws-lambda-s3-trigger/aws_lambda_s3_trigger.md
================================================
# AWS Lambda - S3 trigger
This example creates a function, which will be executed for each object creation in AWS S3.
For more information, see the [AWS Lambda learn guide](https://ballerina.io/learn/aws-lambda/).
## Set up the prerequisites
For instructions, see [Set up the prerequisites](https://ballerina.io/learn/aws-lambda/#set-up-the-prerequisites).
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code aws_lambda_s3_trigger.bal :::
## Build the function
Execute the command below to generate the AWS Lambda artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the AWS CLI command given by the compiler to create and publish the functions by replacing the respective AWS `$LAMBDA_ROLE_ARN`, `$REGION_ID`, and `$FUNCTION_NAME` values given in the command with your values.
::: out aws_deploy.out :::
## Invoke the function
Follow the instructions below to create an S3 bucket in AWS for invoking this function.
1. Go to the [AWS S3](https://s3.console.aws.amazon.com/s3/) portal and create a bucket.
>**Note:** Make sure to select the same **AWS region** in which you created the AWS user and role when creating the S3 bucket.
2. Click on the created bucket, go to the **Properties** tab, and click **Create event notification** under the **Event notifications** section.
3. Enable **All object create events** under event types.
4. Under the **Destination** section, select the AWS Lambda function (i.e., `s3Trigger` in this example) from the dropdown.
5. Select the created bucket under the **Buckets** list, click **Upload**, and upload an object to the S3 bucket.
6. Under the **Functions** list of the AWS Management Console, click the AWS Lambda function, and click the **Monitor** tab.
7. If you get a **Missing permissions** notice at the top, click the **Open the IAM Console** in it.
8. In the IAM Console, click the corresponding role in the list, and click **Add permissions**.
9. Select **attach policies** from the drop-down menu, and add the **AWSLambdaBasicExecutionRole** to the role.
10. Click the **Monitor** tab of the Lambda function in the AWS Management Console, and click **View CloudWatch logs** to check the logs via CloudWatch.
11. Under **Log streams** in CloudWatch, click on the topmost stream in the list and verify the object name in the logs.
================================================
FILE: examples/aws-lambda-s3-trigger/aws_lambda_s3_trigger.metatags
================================================
description: This example creates a function, which will be executed for each object creation in AWS S3.
keywords: ballerina, ballerina by example, aws lambda, s3, trigger, serverless, cloud, function as a service
================================================
FILE: examples/aws-lambda-s3-trigger/bal_build.out
================================================
$ bal build
Compiling source
wso2/aws_lambda_s3_trigger:0.1.0
Generating executable
@awslambda:Function: s3Trigger
Run the following command to deploy each Ballerina AWS Lambda function:
aws lambda create-function --function-name $FUNCTION_NAME --zip-file fileb:///aws-lambda-s3-trigger/target/bin/aws-ballerina-lambda-functions.zip --handler aws-lambda-s3-trigger.$FUNCTION_NAME --runtime provided --role $LAMBDA_ROLE_ARN --layers arn:aws:lambda:$REGION_ID:134633749276:layer:ballerina-jre11:6 --memory-size 512 --timeout 10
Run the following command to re-deploy an updated Ballerina AWS Lambda function:
aws lambda update-function-code --function-name $FUNCTION_NAME --zip-file fileb://aws-ballerina-lambda-functions.zip
================================================
FILE: examples/aws-lambda-s3-trigger/bal_new.out
================================================
$ bal new aws_lambda_s3_trigger
Created new package 'aws_lambda_s3_trigger' at /Users/wso2/aws_lambda_s3_trigger.
================================================
FILE: examples/azure-functions-cosmosdb-trigger/az_deploy.out
================================================
$ func azure functionapp publish --script-root target/azure_functions
Getting site publishing info...
Creating archive for current directory...
Uploading 32.71 MB [##############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in :
cosmos - [cosmosDBTrigger]
================================================
FILE: examples/azure-functions-cosmosdb-trigger/azure_functions_cosmosdb_trigger.bal
================================================
import ballerina/io;
import ballerinax/azure.functions;
public type DBEntry record {
string id;
string name;
};
@functions:CosmosDBTrigger {connectionStringSetting: "CosmosDBConnection", databaseName: "db1", collectionName: "c1"}
listener functions:CosmosDBListener cosmosEp = new ();
service "cosmos" on cosmosEp {
remote function onUpdate(DBEntry[] entries) returns @functions:QueueOutput {queueName: "people"} string {
string name = entries[0].name;
io:println(entries.toJsonString());
return "Hello, " + name;
}
}
================================================
FILE: examples/azure-functions-cosmosdb-trigger/azure_functions_cosmosdb_trigger.md
================================================
# Azure Functions - Cosmos DB trigger
This example demonstrates using a Cosmos DB trigger to invoke an Azure function and a queue output binding to write an entry to a queue.
For more information, see the [Azure Functions deployment guide](https://ballerina.io/learn/azure-functions/).
## Set up the prerequisites
Follow the steps below to create a Cosmos DB and a queue to make use of those services later in this example.
1. Set up the [general prerequisites](https://ballerina.io/learn/azure-functions/#set-up-the-prerequisites).
2. Create the queue in the [HTTP trigger](/learn/by-example/azure-functions/http-trigger/) example to resue it in this one.
3. In the [**Azure Cosmos DB** service of the Azure Portal](https://portal.azure.com/#create/Microsoft.DocumentDB), click **Create**, and select **Azure Cosmos DB for NoSQL**.
4. Enter an account name and a resource group name, and click **Review + Create**, and then, click **Create**.
5. Once the database is created, go to the **Data Explorer**, and select **Create Container**.
6. Enter `db1` as the Database ID, `c1` as the collection ID, and click **Ok**.
>**Note:** If you want to change these values, change them in the code as well.
7. Go to the **Keys** tab of the Cosmos DB page.
8. Copy the value of the `PRIMARY CONNECTION STRING`.
9. Click the **Configuration** tab on the function app page.
10. Select **New Application Setting**, and paste the data you copied above as the value.
>**Tip:** For the key, use the value of the `connectionStringSetting` key and save.
Example application settings are as follows.
- Name - `CosmosDBConnection`
- Value - `AccountEndpoint=https://db-cosmos.documents.azure.com:443/;AccountKey=12345asda;`
Now, as all the infrastructure required is up and running and configured, start building and deploying the Azure function.
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code azure_functions_cosmosdb_trigger.bal :::
## Build the function
Execute the command below to generate the Azure Functions artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the Azure CLI command given by the compiler to create and publish the functions by replacing `` with your respective function app name.
## Invoke the function
Once the function is deployed, add an item to the collection.
1. Navigate to the collection created in the **Data Explorer**.
2. Click **New Item** to add a new item to the collection.
3. Go to the queue page and observe the added new entry.
>**Info:** Additionally, for debugging purposes, view the logs under the **Logs stream** in the function app.
================================================
FILE: examples/azure-functions-cosmosdb-trigger/azure_functions_cosmosdb_trigger.metatags
================================================
description: This example demonstrates using a Cosmos DB trigger to invoke an AWS Lambda function and a queue output binding to write an entry to a queue.
keywords: ballerina, ballerina by example, aws lambda, cosmos db, trigger, serverless, cloud, function as a service
================================================
FILE: examples/azure-functions-cosmosdb-trigger/bal_build.out
================================================
$ bal build --cloud="azure_functions"
Compiling source
wso2/azure_functions_cosmosdb_trigger:0.1.0
Generating executable
@azure.functions:Function: cosmos
Execute the command below to deploy the function locally:
$ func start --script-root target/azure_functions --java
Execute the command below to deploy Ballerina Azure Functions:
$ func azure functionapp publish --script-root target/azure_functions
target/bin/azure_functions_cosmosdb_trigger.jar
================================================
FILE: examples/azure-functions-cosmosdb-trigger/bal_new.out
================================================
$ bal new azure_functions_cosmosdb_trigger
Created new package 'azure_functions_cosmosdb_trigger' at /Users/wso2/azure_functions_cosmosdb_trigger.
================================================
FILE: examples/azure-functions-hello-world/az_deploy.out
================================================
$ func azure functionapp publish --script-root target/azure_functions
Getting site publishing info...
Creating archive for current directory...
Uploading 32.69 MB [##############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in :
get-hello - [httpTrigger]
Invoke url: https://azurecosmosdbfunctionapp.azurewebsites.net/hello
================================================
FILE: examples/azure-functions-hello-world/azure_functions_hello_world.bal
================================================
import ballerinax/azure.functions;
// This function gets triggered by an HTTP call with the name query parameter and
// returns a processed HTTP output to the caller.
service / on new functions:HttpListener() {
resource function get hello(string name) returns string {
return string `Hello, ${name}!`;
}
}
================================================
FILE: examples/azure-functions-hello-world/azure_functions_hello_world.md
================================================
# Azure Functions - Hello world
This example demonstrates how to write a simple echo function in Azure Functions.
In Ballerina, triggers are represented by listeners. When the `af:HttpListener` gets attached to the service, it implies that the function is an HTTP Trigger. The resource method behaves exactly the same as a service written from `ballerina/http`. It supports the `http:Payload` and `http:Header` annotations for parameters. Input binding annotations can be used to annotate parameters to make use of external services in Azure. If no annotations are specified for a parameter, it is identified as a query parameter.
Output bindings are defined in the return type definition. For services with the `HttpListener` attachment, `HttpOutput` is the default output binding. You can override the default behavior by specifying them explicitly in the return type.
In the code sample shown above, it has an empty service path and resource path named `hello`. The accessor is `get`. It expects a request with a query parameter for the field name. The required artifact generation and data binding will be handled by the `ballerinax/azure_functions` package automatically.
For more information, see the [Azure deployment guide](https://ballerina.io/learn/azure-functions/).
## Set up the prerequisites
For instructions, see [Set up the prerequisites](https://ballerina.io/learn/azure-functions/#set-up-the-prerequisites).
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code azure_functions_hello_world.bal :::
## Build the function
Execute the command below to generate the Azure Functions artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the Azure CLI command given by the compiler to create and publish the functions by replacing `` with your respective function app name.
::: out az_deploy.out :::
## Invoke the function
Execute the command below to invoke the function by replacing `` with your respective function app name.
::: out execute_function.out :::
================================================
FILE: examples/azure-functions-hello-world/azure_functions_hello_world.metatags
================================================
description: This example demonstrates how to write a hello world function with http trigger and output in Azure Functions.
keywords: ballerina, ballerina by example, azure functions, function as a service, serverless
================================================
FILE: examples/azure-functions-hello-world/bal_build.out
================================================
$ bal build --cloud="azure_functions"
Compiling source
wso2/azure_functions_hello_world:0.1.0
Generating executable
@azure.functions:Function: get-hello
Execute the command below to deploy the function locally:
$ func start --script-root target/azure_functions --java
Execute the command below to deploy Ballerina Azure Functions:
$ func azure functionapp publish --script-root target/azure_functions
target/bin/azure_functions_hello_world.jar
================================================
FILE: examples/azure-functions-hello-world/bal_new.out
================================================
$ bal new azure_functions_hello_world
Created new package 'azure_functions_hello_world' at /Users/wso2/azure_functions_hello_world.
================================================
FILE: examples/azure-functions-hello-world/execute_function.out
================================================
$ curl https://.azurewebsites.net/hello\?name\=Jack
Hello, Jack!
================================================
FILE: examples/azure-functions-http-trigger-with-queue/az_deploy.out
================================================
$ func azure functionapp publish --script-root target/azure_functions
Getting site publishing info...
Creating archive for current directory...
Uploading 32.71 MB [##############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in :
post-queue - [httpTrigger]
Invoke url: https://azurecosmosdbfunctionapp.azurewebsites.net/queue
================================================
FILE: examples/azure-functions-http-trigger-with-queue/azure_functions_http_trigger_with_queue.bal
================================================
import ballerina/http;
import ballerinax/azure.functions;
public type Person record {
string name;
int age;
};
service / on new functions:HttpListener() {
resource function post queue(@http:Payload Person person) returns
[@functions:HttpOutput http:Created, @functions:QueueOutput {queueName: "people"}
string] {
http:Created httpRes = {
body: person.name + " Added to the Queue!"
};
return [httpRes, string `${person.name} is ${person.age.toString()} years old.`];
}
}
================================================
FILE: examples/azure-functions-http-trigger-with-queue/azure_functions_http_trigger_with_queue.md
================================================
# Azure Functions - HTTP trigger with queue
This example demonstrates using an HTTP trigger to invoke an Azure function with multiple output bindings to return the HTTP response and queue output binding to write an entry to a queue.
For more information, see the [Azure Functions deployment guide](https://ballerina.io/learn/azure-functions/).
## Set up the prerequisites
1. Set up the [general prerequisites](https://ballerina.io/learn/azure-functions/#set-up-the-prerequisites).
2. In the AWS Portal, click **Storage accounts** to create a queue to hold the outputs of the function.
2. From the list, click on the storage account entry that corresponds with your function app.
3. Click **Queues** in the sidebar, and click **+ Queue**.
4. Enter a name in the **Add queue** pop-up, and click **Add**.
>**Note:** For the **Queue name**, enter the same value of the `queueName` property in the `QueueOutput` annotation in the [Ballerina source below](https://ballerina.io/learn/by-example/azure-functions-trigger/#write-the-function)
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code azure_functions_http_trigger_with_queue.bal :::
## Build the function
Execute the command below to generate the Azure Functions artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the Azure CLI command given by the compiler to create and publish the functions by replacing `` with your respective function app name.
## Invoke the function
Execute the command below by replacing `` with your respective function app name to invoke the function.
::: out execute_function.out :::
>**Tip:** Refresh the page of the `people` queue in the Azure portal and view the entry added with the message text: `Jack is 21 years old.`
================================================
FILE: examples/azure-functions-http-trigger-with-queue/azure_functions_http_trigger_with_queue.metatags
================================================
description: This example demonstrates using an HTTP trigger to invoke an Azure function with multiple output bindings to return the HTTP response and queue output binding to write an entry to a queue.
keywords: ballerina, ballerina by example, aws lambda, http, trigger, serverless, cloud, function as a service
================================================
FILE: examples/azure-functions-http-trigger-with-queue/bal_build.out
================================================
$ bal build --cloud="azure_functions"
Compiling source
wso2/azure_functions_http_trigger_with_queue:0.1.0
Generating executable
@azure.functions:Function: post-queue
Execute the command below to deploy the function locally:
$ func start --script-root target/azure_functions --java
Execute the command below to deploy Ballerina Azure Functions:
$ func azure functionapp publish --script-root target/azure_functions
target/bin/azure_functions_http_trigger_with_queue.jar
================================================
FILE: examples/azure-functions-http-trigger-with-queue/bal_new.out
================================================
$ bal new azure_functions_http_trigger_with_queue
Created new package 'azure_functions_http_trigger_with_queue' at /Users/wso2/azure_functions_http_trigger_with_queue.
================================================
FILE: examples/azure-functions-http-trigger-with-queue/execute_function.out
================================================
$ curl --header "Content-Type: application/json" \
--request POST \
--data '{"name":"Jack","age":21}' \
"https://.azurewebsites.net/queue"
Jack Added to the Queue!
================================================
FILE: examples/azure-functions-timer-trigger/az_deploy.out
================================================
$ func azure functionapp publish --script-root target/azure_functions
Getting site publishing info...
Creating archive for current directory...
Uploading 32.7 MB [###############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in :
timer - [timerTrigger]
================================================
FILE: examples/azure-functions-timer-trigger/azure_functions_timer_trigger.bal
================================================
import ballerina/time;
import ballerinax/azure.functions;
// This function gets executed every 10 seconds by the Azure Functions app. Once the function is executed, the timer
// details will be stored in the selected queue storage for every invocation.
@functions:TimerTrigger {schedule: "*/10 * * * * *"}
listener functions:TimerListener timerListener = new functions:TimerListener();
service "timer" on timerListener {
remote function onTrigger(functions:TimerMetadata metadata) returns
@functions:QueueOutput {queueName: "timer-queue"} string|error {
time:Utc utc = time:utcNow();
return "Hello from timer: " + time:utcToEmailString(utc);
}
}
================================================
FILE: examples/azure-functions-timer-trigger/azure_functions_timer_trigger.md
================================================
# Azure Functions - Timer trigger
This example demonstrates how a function can be scheduled to execute periodically by the Azure Functions app. Once the function is executed, the timer details will be stored in the selected queue storage for every invocation.
For more information, see the [Azure deployment guide](https://ballerina.io/learn/azure-functions/).
## Set up the prerequisites
For instructions, see [Set up the prerequisites](https://ballerina.io/learn/azure-functions/#set-up-the-prerequisites).
## Write the function
Follow the steps below to write the function.
1. Execute the command below to create a new Ballerina package.
::: out bal_new.out :::
2. Replace the content of the generated Ballerina file with the content below.
::: code azure_functions_timer_trigger.bal :::
## Build the function
Execute the command below to generate the Azure Functions artifacts.
::: out bal_build.out :::
## Deploy the function
Execute the Azure CLI command given by the compiler to create and publish the functions by replacing `` with your respective function app name.
## Invoke the function
The `timer` function is triggered by the Azure Functions app from a timer. Follow the steps below to verify the output in the queue storage of the function app.
1. In the AWS Portal, click **Storage accounts** to view the created queue.
2. From the list, click on the storage account entry that corresponds with your function app.
3. Click ***Queues***, and click on the **queue3** queue.
4. You view the output below getting logged every 10 seconds.
`Hello from timer: `
================================================
FILE: examples/azure-functions-timer-trigger/azure_functions_timer_trigger.metatags
================================================
description: This example demonstrates how a function can be scheduled to execute periodically by the Azure Functions app.
keywords: ballerina, ballerina by example, aws lambda, timer, trigger, serverless, cloud, function as a service
================================================
FILE: examples/azure-functions-timer-trigger/bal_build.out
================================================
$ bal build --cloud="azure_functions"
Compiling source
wso2/azure_functions_timer_trigger:0.1.0
Generating executable
@azure.functions:Function: timer
Execute the command below to deploy the function locally:
$ func start --script-root target/azure_functions --java
Execute the command below to deploy Ballerina Azure Functions:
$ func azure functionapp publish --script-root target/azure_functions
target/bin/azure_functions_timer_trigger.jar
================================================
FILE: examples/azure-functions-timer-trigger/bal_new.out
================================================
$ bal new azure_functions_timer_trigger
Created new package 'azure_functions_timer_trigger' at /Users/wso2/azure_functions_timer_trigger.
================================================
FILE: examples/binary-data/binary_data.bal
================================================
import ballerina/io;
public function main() {
// Creates a `byte` array using the `base16` byte array literal.
byte[] arr1 = base16 `55 EE 66 FF 77 AB`;
io:println(arr1);
// Creates a `byte` array using the `base64` byte array literal.
byte[] arr2 = base64 `ABCD pqrs 5678 +/12`;
io:println(arr2);
}
================================================
FILE: examples/binary-data/binary_data.md
================================================
# Binary data
Binary data is represented by arrays of byte values. It is a special syntax for byte arrays in `Base16` and `Base64`. The encoding tables of both `Base16` and `Base64` are the same as `RFC 4648`. A byte is an int in the range `0` to `0xFF` and it is a subtype of `int`.
::: code binary_data.bal :::
::: out binary_data.out :::
## Related links
- [Arrays](/learn/by-example/arrays)
- [Byte type](/learn/by-example/byte-type)
- [Integers](/learn/by-example/integers)
================================================
FILE: examples/binary-data/binary_data.metatags
================================================
description: Byte array literal, binary data, base16 data format, base64 data format.
keywords: ballerina, ballerina by example, bbe, binary data, byte, Base16, Base64, literal
================================================
FILE: examples/binary-data/binary_data.out
================================================
$ bal run binary_data.bal
[85,238,102,255,119,171]
[0,16,131,166,170,236,231,174,252,251,253,118]
================================================
FILE: examples/binary-operators/binary_operators.bal
================================================
import ballerina/io;
public function main() {
int a = 10;
int b = 20;
// Perform integer addition.
int sum = a + b;
io:println(sum);
// Perform integer subtraction.
int difference = a - b;
io:println(difference);
// Perform integer multiplication.
int product = a * b;
io:println(product);
// Perform integer division.
int quotient = b / a;
io:println(quotient);
// Perform integer remainder.
int remainder = b % a;
io:println(remainder);
// `>`, `<`, `>=` and `<=` are used to test the relative order of two values.
// Check if `a` is less than `b`.
boolean isLessThan = a < b;
io:println(isLessThan);
// Check if `a` is greater than or equal to `b`.
boolean isGreaterThanOrEqual = a >= b;
io:println(isGreaterThanOrEqual);
float e = 10.5;
float f = 20.5;
// Perform floating-point addition.
float floatSum = e + f;
io:println(floatSum);
// Perform floating-point subtraction.
float floatDifference = e - f;
io:println(floatDifference);
// Perform floating-point multiplication.
float floatProduct = e * f;
io:println(floatProduct);
// Perform floating-point division.
float floatQuotient = e / f;
io:println(floatQuotient);
// Check if `e` is less than or equal to `f`.
boolean isLessOrEqual = e <= f;
io:println(isLessOrEqual);
// Check if `e` is greater than `f`.
boolean isGreaterThan = e > f;
io:println(isGreaterThan);
boolean c = true;
boolean d = false;
// Perform the logical AND operation between two boolean values.
boolean logicalAnd = c && d;
io:println(logicalAnd);
// Perform logical OR between two boolean values.
boolean logicalOr = c || d;
io:println(logicalOr);
// For the `<` operator, `d < c` evaluates to `true` when `d` is `false` and `c` is `true`.
boolean isLessThanBoolean = d < c;
io:println(isLessThanBoolean);
int g = 10;
int h = 20;
// Perform bitwise AND between two integers.
int bitwiseAnd = g & h;
io:println(bitwiseAnd);
// Perform bitwise OR between two integers.
int bitwiseOr = g | h;
io:println(bitwiseOr);
// Perform bitwise XOR between two integers.
int bitwiseXor = g ^ h;
io:println(bitwiseXor);
// Left shift an integer by 2 bits.
int leftShift = g << 2;
io:println(leftShift);
// Right shift an integer by 2 bits (signed).
int signedRightShift = g >> 2;
io:println(signedRightShift);
// Right shift an integer by 2 bits (unsigned).
int unsignedRightShift = g >>> 2;
io:println(unsignedRightShift);
string i = "Hello";
string j = "Ballerina";
// Concatenate two strings.
string concatenatedString = i + " " + j;
io:println(concatenatedString);
// Check if `i` is lexicographically greater than `j` in Unicode code point order.
boolean isGreaterThanString = i > j;
io:println(isGreaterThanString);
}
================================================
FILE: examples/binary-operators/binary_operators.md
================================================
# Binary operators
Ballerina provides several binary operators to perform operations on values of various types such as integers, floats, booleans, and strings. Binary arithmetic operators (`+`, `-`, `*`, `/`, and `%`) allow basic mathematical calculations between two values. The operators to compare values (`<`, `>`, `<=`, and `>=`) produce a boolean result.
Ballerina also supports binary logical operators (`&&` and `||`) to perform logical operations on boolean values and bitwise operators (`&`, `|`, `^`, `<<`, `>>`, and `>>>`) to manipulate bits in integer values.
::: code binary_operators.bal :::
::: out binary_operators.out :::
================================================
FILE: examples/binary-operators/binary_operators.metatags
================================================
description: This BBE introduces binary operators in Ballerina.
keywords: ballerina, ballerina by example, bbe, operators, binary operators, bitwise operators, int, float, string, boolean, +, -, /, %, *, , <, >, <=, >=, >>, <<, >>>, ||, &&, |, &, ^
================================================
FILE: examples/binary-operators/binary_operators.out
================================================
$ bal run binary_operators.bal
30
-10
200
2
0
true
false
31.0
-10.0
215.25
0.5121951219512195
true
false
false
true
true
0
30
30
40
2
2
Hello Ballerina
true
================================================
FILE: examples/binding-patterns/binding_patterns.bal
================================================
import ballerina/io;
public function main() {
string name;
int age;
// The following example uses a list binding pattern to destructure the
// returned list and assign it to the two variables, `name` and `age`.
// However, other binding patterns can also be used similarly.
[name, age] = getDetails();
io:println(name);
io:println(age);
}
function getDetails() returns [string, int] {
return ["John", 30];
}
================================================
FILE: examples/binding-patterns/binding_patterns.md
================================================
# Binding Patterns
Binding patterns make it easier to extract information, especially from structured values. The destructuring syntax allows different parts of a single structured value to be assigned to separate variables at the same time. All the variables in a binding pattern must be distinct.
::: code binding_patterns.bal :::
::: out binding_patterns.out :::
## Related links
- [Typed binding pattern](/learn/by-example/typed-binding-pattern/)
- [Wildcard binding pattern](/learn/by-example/wildcard-binding-pattern/)
- [List binding pattern](/learn/by-example/list-binding-pattern/)
- [Mapping binding pattern](/learn/by-example/mapping-binding-pattern/)
- [Error binding pattern](/learn/by-example/error-binding-pattern/)
================================================
FILE: examples/binding-patterns/binding_patterns.metatags
================================================
description: This BBE introduces extracting information from structured values and destructuring information using binding patterns
keywords: ballerina, ballerina by example, bbe, binding pattern, destructuring
================================================
FILE: examples/binding-patterns/binding_patterns.out
================================================
$ bal run binding_patterns.bal
John
30
================================================
FILE: examples/binding-patterns-in-match-statement/binding_patterns_in_match_statement.bal
================================================
import ballerina/io;
type Position record {
int x;
int y;
};
type PositionRecord record {
Position p;
};
function matchFn1(Position position) {
match position {
// The binding pattern below matches mappings that contain at least the fields with the `x` and `y` keys.
// The values of these fields can be accessed via the `x` and `y` variables within this block.
var {x, y} => {
io:println(x, ", ", y);
}
}
}
function matchFn2(Position position) {
match position {
// The binding pattern below also has a rest binding pattern to capture the additional fields
// that may be specified in the open record value assigned to the `position` variable.
// Type of the `rest` variable can be considered a map of `anydata`. However, it cannot contain the
// `x` or `y` keys. This can be represented using the `never` type as explained in the example for
// the `never` type.
var {x, y, ...rest} => {
io:println(x, ", ", y, ", ", rest);
}
}
}
function matchFn3(PositionRecord r) {
match r {
// The pattern below matches a mapping that has a field with the `p` key and a value that is another
// mapping that contains at least the fields with `x` and `y` keys.
var {p: {x, y}} => {
io:println(x, ", ", y);
}
}
}
public function main() {
Position position = {x: 1, y: 2, "u": 3 , "v": 4};
matchFn1(position);
matchFn2(position);
PositionRecord r = {p: position};
matchFn3(r);
}
================================================
FILE: examples/binding-patterns-in-match-statement/binding_patterns_in_match_statement.md
================================================
# Binding patterns in the `match` statement
Binding patterns can be used in a match statement to bind parts of a successful match to variables.
You can use the rest binding pattern (`...r`) in a binding pattern of a `match` statement to bind the fields that are not explicitly bound in the binding pattern. This is particularly useful when working with open records.
::: code binding_patterns_in_match_statement.bal :::
::: out binding_patterns_in_match_statement.out :::
## Related links
- [Match statement](/learn/by-example/match-statement/)
- [If statement](/learn/by-example/if-statement/)
- [Typed binding pattern](/learn/by-example/typed-binding-pattern)
================================================
FILE: examples/binding-patterns-in-match-statement/binding_patterns_in_match_statement.metatags
================================================
description: This BBE demonstrates how to use binding patterns in a `match` statement and matching mapping values in a `match` statement in Ballerina.
keywords: ballerina, ballerina by example, bbe, spread operator, binding pattern, match
================================================
FILE: examples/binding-patterns-in-match-statement/binding_patterns_in_match_statement.out
================================================
$ bal run binding_patterns_in_match_statement.bal
1, 2
1, 2, {"u":3,"v":4}
1, 2
================================================
FILE: examples/boolean/boolean.bal
================================================
import ballerina/io;
public function main() {
boolean flag = true;
io:println(flag);
int x1 = 3;
int x2 = 2;
// The example below will output `false`.
io:println(x1 < x2);
}
================================================
FILE: examples/boolean/boolean.md
================================================
# Boolean
The `boolean` type has two values: `true`, `false`. The `!` operator works on booleans only. `&&` and `||` operators short-circuit - the second operand is not evaluated if the result of evaluating the first operand is sufficient to identify the result of the logical expression. Usual comparison operators (`==`, `!=`, `<`, `>`, `<=`, and `>=`) produce boolean values.
::: code boolean.bal :::
::: out boolean.out :::
## Related links
- [If statement](/learn/by-example/if-statement/)
- [Match guard in match statement](/learn/by-example/match-guard-in-match-statement/)
================================================
FILE: examples/boolean/boolean.metatags
================================================
description: This BBE introduces the boolean type.
keywords: ballerina, ballerina by example, bbe, boolean, bool, true, false
================================================
FILE: examples/boolean/boolean.out
================================================
$ bal run boolean.bal
true
false
================================================
FILE: examples/break-statement/break_statement.bal
================================================
import ballerina/io;
public function main() {
foreach int i in 0...9 {
// Loop breaks when the condition is satisfied.
if (i > 5) {
break;
}
io:println(i);
}
int i = 0;
while true {
// Loop breaks when the condition is satisfied.
if i > 5 {
break;
}
io:println(i);
i = i + 1;
}
}
================================================
FILE: examples/break-statement/break_statement.md
================================================
# Break statement
A `break` statement can be used to terminate the execution within the nearest enclosing `while` or `foreach` statement.
::: code break_statement.bal :::
::: out break_statement.out :::
## Related links
- [Continue statement](/learn/by-example/continue-statement/)
- [While statement](/learn/by-example/while-statement/)
- [Foreach statement](/learn/by-example/foreach-statement/)
================================================
FILE: examples/break-statement/break_statement.metatags
================================================
description: This BBE demonstrates how to terminate a loop using the `break` statement in Ballerina.
keywords: ballerina, ballerina by example, bbe, foreach, loops, iterate, iterable, break, terminate loop, while, repeat
================================================
FILE: examples/break-statement/break_statement.out
================================================
$ bal run break_statement.bal
0
1
2
3
4
5
0
1
2
3
4
5
================================================
FILE: examples/built-in-integer-subtypes/built_in_integer_subtypes.bal
================================================
import ballerina/io;
// The `fn` function accepts an argument that belongs to `int:Unsigned32`
// and prints the result of performing the bitwise `&` operation with
// the `int:Unsigned16` maximum value.
function fn(int:Unsigned32 val) {
int:Unsigned16 max = 65535;
// Bitwise operations have special typing. Since using `&` with operands
// of types `int:Unsigned32` and `int:Unsigned16` will result in a value
// that belongs to `int:Unsigned16`, the static type of `val & max` is
// considered to be `int:Unsigned16`, which allows the following.
int:Unsigned16 res = val & max;
io:println(res);
}
public function main() {
// Since `72` belongs to `int:Unsigned32`, it can directly be passed as
// an argument to the `fn` function.
fn(72);
// The cast done in the `intFn` function will be successful since `43543`
// belongs to `int:Unsigned32`.
intFn(43543);
// The cast to `int:Unsigned32` panics since `4294967296` is out of range.
intFn(4294967296);
}
function intFn(int m) {
// Since an argument of type `int` cannot be passed to the `fn` function
// which only accepts values that belong to `int:Unsigned32`, a cast is attempted
// before passing `m` as an argument to `fn`. The cast attempt will panic if the
// value is out of range for `int:Unsigned32`.
fn( m);
}
================================================
FILE: examples/built-in-integer-subtypes/built_in_integer_subtypes.md
================================================
# Built-in integer subtypes
Ballerina has one integer type (`int`), which is a 64-bit, signed type.
The `ballerina/lang.int` lang library defines the following built-in subtypes of this `int` type.
- `int:Signed32`
- `int:Unsigned32`
- `int:Signed16`
- `int:Unsigned16`
- `int:Signed8`
- `int:Unsigned8` (same as `byte`)
The runtime behavior of operations on these subtypes are the same as for the `int` type. Bitwise operations with operands of these types have special typing, which allows using a more specific type for the operation.
Integer subtypes are useful for interfacing with external systems that use these types. They also allow implementations to optimize storage, particularly for arrays.
::: code built_in_integer_subtypes.bal :::
::: out built_in_integer_subtypes.out :::
================================================
FILE: examples/built-in-integer-subtypes/built_in_integer_subtypes.metatags
================================================
description: This BBE demonstrates built-in integer subtypes in Ballerina.
keywords: ballerina, ballerina by example, bbe, int, int subtypes, signed, unsigned
================================================
FILE: examples/built-in-integer-subtypes/built_in_integer_subtypes.out
================================================
$ bal run built_in_integer_subtypes.bal
72
43543
error: {ballerina}NumberConversionError {"message":"'int' value '4,294,967,296' cannot be converted to 'lang.int:Unsigned32'"}
at built_in_integer_subtypes:intFn(built_in_integer_subtypes.bal:35)
built_in_integer_subtypes:main(built_in_integer_subtypes.bal:27)
================================================
FILE: examples/built-in-string-subtype/built_in_string_subtype.bal
================================================
import ballerina/io;
public function main() {
// A variable of type `string:Char` to which only strings of length 1 can be assigned.
string:Char ch = "x";
io:println(ch);
// The `lang.string:toCodePoint` lang library function can be used with
// an expression of the `string:Char` type to retrieve the relevant code point value.
int cp = ch.toCodePointInt();
io:println(cp);
}
================================================
FILE: examples/built-in-string-subtype/built_in_string_subtype.md
================================================
# Built-in string subtype
The `ballerina/lang.string` lang library defines the `Char` type, which is a built-in subtype of `string`. A string belongs to the `string:Char` type if it has a length of 1.
This subtype is analogous to built-in subtypes of `int` and `xml`.
A `string:Char` value can be converted to a code point, which is represented as an `int` value.
::: code built_in_string_subtype.bal :::
::: out built_in_string_subtype.out :::
================================================
FILE: examples/built-in-string-subtype/built_in_string_subtype.metatags
================================================
description: This BBE demonstrates the built-in string subtype string:Char in Ballerina.
keywords: ballerina, ballerina by example, bbe, string, string subtype, Char
================================================
FILE: examples/built-in-string-subtype/built_in_string_subtype.out
================================================
$ bal run built_in_string_subtype.bal
x
120
================================================
FILE: examples/byte-type/byte_type.bal
================================================
import ballerina/io;
public function main() {
// The `byte` type consists of integers ranging from `0` to `255`.
byte b = 255;
io:println(b);
// Since the set of possible `byte` values is a subset of `int` values,
// the `byte` type is a subtype of the `int` type.
int i = b;
io:println(i);
}
================================================
FILE: examples/byte-type/byte_type.md
================================================
# Byte type
The byte type in Ballerina represents an 8-bit unsigned integer, with values ranging from 0 to 255.
::: code byte_type.bal :::
::: out byte_type.out :::
## Related links
- [Built-in integer subtypes](/learn/by-example/built-in-integer-subtypes/)
================================================
FILE: examples/byte-type/byte_type.metatags
================================================
description: This BBE introduces the Ballerina byte type.
keywords: ballerina, ballerina by example, bbe, bytes, byte, byte type, unsigned8
================================================
FILE: examples/byte-type/byte_type.out
================================================
$ bal run byte_type.bal
255
255
================================================
FILE: examples/c2c-docker-deployment/Cloud.toml
================================================
[container.image]
repository="wso2inc"
name="hello"
tag="v0.1.0"
================================================
FILE: examples/c2c-docker-deployment/build_output.out
================================================
$ bal build --cloud="docker"
Compiling source
wso2/hello:0.1.0
Generating executable
Generating artifacts...
@kubernetes:Docker - complete 2/2
Execute the below command to run the generated Docker image:
docker run -d -p 9090:9090 wso2inc/hello:v0.1.0
================================================
FILE: examples/c2c-docker-deployment/c2c_docker_deployment.bal
================================================
import ballerina/http;
// This code is completely focused on the business logic and it does not specify anything related to operations.
listener http:Listener helloEP = new(9090);
service http:Service /helloWorld on helloEP {
resource function get sayHello() returns string {
return "Hello from Docker!";
}
}
================================================
FILE: examples/c2c-docker-deployment/c2c_docker_deployment.md
================================================
# Docker
Ballerina supports generating Docker artifacts from code without any additional configuration. This simplifies the experience of developing and deploying Ballerina code in the cloud. Code to Cloud builds the containers and required artifacts by deriving the required values from the code. If you want to override the default values taken by the compiler, you can use a `Cloud.toml` file.
For more information, see [Code to Cloud Deployment](/learn/code-to-cloud-deployment/).
::: code c2c_docker_deployment.bal :::
Before you build the package, you need to override some default values taken by the compiler. To do this, create a filed named `Cloud.toml` in the package directory, and add the content below to it.
For all the supported key value properties, see [Code to Cloud specification](https://github.com/ballerina-platform/ballerina-spec/blob/master/c2c/code-to-cloud-spec.md).
::: code Cloud.toml :::
Execute the `bal build` command to build the Ballerina package. Code to Cloud generates only one container per package.
::: out build_output.out :::
Verify if the Docker image is generated.
::: out docker_images.out :::
Run the generated Docker image.
::: out docker_run.out :::
Invoke the service.
::: out execute_curl.out:::
================================================
FILE: examples/c2c-docker-deployment/docker_images.out
================================================
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wso2inc/hello v0.1.0 60d95f0928b2 About a minute ago 228MB
================================================
FILE: examples/c2c-docker-deployment/docker_run.out
================================================
$ docker run -d -p 9090:9090 wso2inc/hello:v0.1.0
c04194eb0b4d0d78cbc8ca55e0527d381d8ab4a1a68f8ea5dd3770a0845d5fbb
================================================
FILE: examples/c2c-docker-deployment/execute_curl.out
================================================
$ curl http://localhost:9090/helloWorld/sayHello
Hello from Docker!
================================================
FILE: examples/c2c-k8s-deployment/Cloud.toml
================================================
[container.image]
repository="wso2inc"
name="hello"
tag="v0.1.0"
================================================
FILE: examples/c2c-k8s-deployment/build_output.out
================================================
$ bal build --cloud="k8s"
Compiling source
wso2/hello:0.1.0
Generating executable
Generating artifacts...
@kubernetes:Service - complete 1/1
@kubernetes:Deployment - complete 1/1
@kubernetes:HPA - complete 1/1
@kubernetes:Docker - complete 2/2
Execute the below command to deploy the Kubernetes artifacts:
kubectl apply -f /home/anjana/bbe-make/k8s/target/kubernetes/hello
Execute the below command to access service via NodePort:
kubectl expose deployment hello-deployment --type=NodePort --name=hello-svc-local
================================================
FILE: examples/c2c-k8s-deployment/c2c_k8s_deployment.bal
================================================
import ballerina/http;
// This code is completely focused on the business logic and it does not specify anything related to the operations.
listener http:Listener helloEP = new(9090);
service http:Service /helloWorld on helloEP {
resource function get sayHello() returns string {
return "Hello from Kubernetes!";
}
}
================================================
FILE: examples/c2c-k8s-deployment/c2c_k8s_deployment.md
================================================
# Kubernetes
Ballerina supports generating Kubernetes artifacts from code without any additional configuration. This simplifies the experience of developing and deploying Ballerina code in the cloud. Code to Cloud builds the containers and required artifacts by deriving the required values from the code. If you want to override the default values taken by the compiler, you can use a `Cloud.toml` file.
For more information, see [Code to Cloud Deployment](/learn/code-to-cloud-deployment/).
::: code c2c_k8s_deployment.bal :::
Before you build the package, you need to override some default values taken by the compiler. To do this, create a filed named `Cloud.toml` in the package directory, and add the content below to it.
For all the supported key value properties, see [Code to Cloud Specification](https://github.com/ballerina-platform/ballerina-spec/blob/master/c2c/code-to-cloud-spec.md).
::: code Cloud.toml :::
Execute the `bal build` command to build the Ballerina package. Code to Cloud generates only one container per package.
::: out build_output.out :::
Push the created Docker image to Docker Hub.
::: out docker_push.out :::
Create the deployment using the Kubernetes artifacts.
::: out kubectl_apply.out :::
Verify the Kubernetes pods.
::: out kubectl_pods.out :::
Expose via NodePort to test in the developer environment.
::: out kubectl_expose.out :::
Get the External IP and port of the Kubernetes service.
::: out kubectl_svc.out :::
If the External IP of the `hello-svc-local` service is ``, you need to follow cluster-specific steps to obtain the external IP. If you are using Minikube, you can use the `minikube ip` command to obtain the IP.
::: out minikube_ip.out :::
Access the deployed service via CURL.
::: out execute_curl.out :::
================================================
FILE: examples/c2c-k8s-deployment/docker_push.out
================================================
$ docker push wso2inc/hello:v0.1.0
================================================
FILE: examples/c2c-k8s-deployment/execute_curl.out
================================================
$ curl http://192.168.49.2:31360/helloWorld/sayHello
Hello from Kubernetes!
================================================
FILE: examples/c2c-k8s-deployment/kubectl_apply.out
================================================
$ kubectl apply -f /home/wso2/project/target/kubernetes/hello-0.1.0
service/helloep-svc created
deployment.apps/wso2-hello-0--deployment created
horizontalpodautoscaler.autoscaling/wso2-hello-0--hpa created
================================================
FILE: examples/c2c-k8s-deployment/kubectl_expose.out
================================================
$ kubectl expose deployment hello-deployment --type=NodePort --name=hello-svc-local
service/hello-svc-local exposed
================================================
FILE: examples/c2c-k8s-deployment/kubectl_pods.out
================================================
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
wso2-hello-0--deployment-7d4d56457b-7jlzx 1/1 Running 0 57s
================================================
FILE: examples/c2c-k8s-deployment/kubectl_svc.out
================================================
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-svc ClusterIP 10.97.140.84 9090/TCP 13m
hello-svc-local NodePort 10.108.87.21 9090:31360/TCP 2m18s
================================================
FILE: examples/c2c-k8s-deployment/minikube_ip.out
================================================
$ minikube ip
192.168.49.2
================================================
FILE: examples/cache-basics/cache_basics.bal
================================================
import ballerina/cache;
import ballerina/io;
public function main() returns error? {
// This creates a new cache instance with the default configurations.
cache:Cache cache = new();
// Adds new entries to the cache.
check cache.put("key1", "value1");
check cache.put("key2", "value2");
// Checks for the cached key availability.
if (cache.hasKey("key1")) {
// Fetches the cached value.
string value = check cache.get("key1");
io:println("The value of the key1: " + value);
}
// Gets the keys of the cache entries.
string[] keys = cache.keys();
io:println("The existing keys in the cache: " + keys.toString());
// Gets the size of the cache.
int size = cache.size();
io:println("The cache size: ", size);
}
================================================
FILE: examples/cache-basics/cache_basics.md
================================================
# Cache basics
The `cache` library provides in-memory cache implementation APIs and uses the `Least Recently Used` algorithm-based eviction policy.
For more information on the underlying module, see the [`cache` module](https://lib.ballerina.io/ballerina/cache/latest/).
This example illustrates the basic operations provided by the `cache` library.
::: code cache_basics.bal :::
To run this sample, use the `bal run` command.
::: out cache_basics.out :::
================================================
FILE: examples/cache-basics/cache_basics.metatags
================================================
description: This BBE shows how to perform basic in-memory caching operations with the "Least Recently Used" algorithm in Ballerina.
keywords: ballerina, ballerina by examples, BBE, cache, put, get, size, hasKey
================================================
FILE: examples/cache-basics/cache_basics.out
================================================
$ bal run cache_basic.bal
The value of the key1: value1
The existing keys in the cache: ["key1","key2"]
The cache size: 2
================================================
FILE: examples/cache-basics/tests/cache_test.bal
================================================
import ballerina/test;
string[] outputs = [];
// This is the mock function, which will replace the real function.
@test:Mock {
moduleName: "ballerina/io",
functionName: "println"
}
test:MockFunction mock_printLn = new();
public function mockPrint(any|error... val) {
any|error value = val.reduce(getStringValue, "");
if (value is error) {
outputs.push(value.message());
} else {
outputs.push(value.toString());
}
}
@test:Config{}
function testFunc() {
test:when(mock_printLn).call("mockPrint");
// Invoking the main function.
error? output = main();
if (output is error) {
test:assertFail("Test failed");
} else {
test:assertEquals(outputs[0].toString(), "The value of the key1: value1");
test:assertEquals(outputs[1].toString(), "The existing keys in the cache: [\"key1\",\"key2\"]");
test:assertEquals(outputs[2].toString(), "The cache size: 2");
}
}
function getStringValue(any|error a, any|error b) returns string {
string aValue = a is error ? a.toString() : a.toString();
string bValue = b is error ? b.toString() : b.toString();
return (aValue + bValue);
}
================================================
FILE: examples/cache-invalidation/cache_invalidation.bal
================================================
import ballerina/cache;
import ballerina/io;
public function main() returns error? {
// This creates a new cache with the advanced configuration.
cache:Cache cache = new ({
// The maximum size of the cache is 10.
capacity: 10,
// The eviction factor is set to 0.2, which means at the
// time of eviction 10*0.2=2 entries get removed from the cache.
evictionFactor: 0.2,
// The default max age of the cache entry is set to 2 seconds.
defaultMaxAge: 2,
// The cache cleanup task runs every 3 seconds and clears all
// the expired entries.
cleanupInterval: 3
});
// Adds the new entries to the cache.
check cache.put("key1", "value1");
check cache.put("key2", "value2");
// Adds a new entry to the cache by overriding the default max age.
check cache.put("key3", "value3", 3600);
// Gets the keys of the cache entries.
string[] keys = cache.keys();
io:println("The existing keys in the cache: ", keys);
// Discards the given cache entry.
_ = check cache.invalidate("key2");
// Gets the keys of the cache entries.
io:println("The existing keys in after invalidating a given key: ",
cache.keys());
// Discards all the cache entries of the cache.
_ = check cache.invalidateAll();
// Gets the keys of the cache entries after all the keys are invalidated.
io:println("The keys after invalidating all the keys: ", cache.keys());
}
================================================
FILE: examples/cache-invalidation/cache_invalidation.md
================================================
# Cache invalidation
The `cache` library provides in-memory cache implementation APIs and uses the `Least Recently Used` algorithm-based eviction policy.
For more information on the underlying module, see the [`cache` module](https://lib.ballerina.io/ballerina/cache/latest/).
::: code cache_invalidation.bal :::
To run this sample, use the `bal run` command.
::: out cache_invalidation.out :::
================================================
FILE: examples/cache-invalidation/cache_invalidation.metatags
================================================
description: This BBE shows how to perform an in-memory caching invalidate operation with the "Least Recently Used" algorithm in Ballerina.
keywords: ballerina, ballerina by examples, BBE, cache, put, hasKey, invalidate, invalidateAll
================================================
FILE: examples/cache-invalidation/cache_invalidation.out
================================================
$ bal run cache_invalidation.bal
The existing keys in the cache: ["key1","key2","key3"]
The existing keys in after invalidating a given key: ["key1","key3"]
The keys after invalidating all the keys: []
================================================
FILE: examples/cache-invalidation/tests/cache_test.bal
================================================
import ballerina/test;
string[] outputs = [];
// This is the mock function, which will replace the real function.
@test:Mock {
moduleName: "ballerina/io",
functionName: "println"
}
test:MockFunction mock_printLn = new();
public function mockPrint(any|error... val) {
any|error value = val.reduce(getStringValue, "");
if (value is error) {
outputs.push(value.message());
} else {
outputs.push(value.toString());
}
}
@test:Config{}
function testFunc() {
test:when(mock_printLn).call("mockPrint");
// Invoking the main function.
error? output = main();
if (output is error) {
test:assertFail("Test failed");
} else {
test:assertEquals(outputs[0].toString(), "The existing keys in the cache: [\"key1\",\"key2\",\"key3\"]");
test:assertEquals(outputs[1].toString(), "The existing keys in after invalidating a given key: [\"key1\",\"key3\"]");
test:assertEquals(outputs[2].toString(), "The keys after invalidating all the keys: []");
}
}
function getStringValue(any|error a, any|error b) returns string {
string aValue = a is error ? a.toString() : a.toString();
string bValue = b is error ? b.toString() : b.toString();
return (aValue + bValue);
}
================================================
FILE: examples/casting-json-to-user-defined-type/casting_json_to_user_defined_type.bal
================================================
import ballerina/io;
type Coord record {
float x;
float y;
};
public function main() {
json j = {x: 1.0, y: 2.0};
// Here, the inherent type of `j` is not a subtype of `Coord`.
// Therefore, `j` cannot be directly converted to `Coord`.
// Use `cloneReadOnly()` to create a read-only copy of the mutable value `j`.
// Then, the resulting immutable value can be casted successfully.
json k = j.cloneReadOnly();
Coord c = k;
io:println(c.x);
io:println(c.y);
}
================================================
FILE: examples/casting-json-to-user-defined-type/casting_json_to_user_defined_type.md
================================================
# Casting JSON to user-defined type
In order to access the field in the `json` value, the easiest way is to convert the `json` value to a user-defined type.
The type-casting can be used to do that. However, if the cast fails, the program panics with an error. The recommended way to do this is by using langlib functions.
Casting to a user-defined type will work on mutable structure only if the inherent type (a structured value has an inherent type, which is a type descriptor that is part of the structured value's runtime value) of that structure is a subtype of the user-defined type.
Casting immutable values will work. However, it does not do numeric conversions.
::: code casting_json_to_user_defined_type.bal :::
::: out casting_json_to_user_defined_type.out :::
## Related links
- [JSON type](/learn/by-example/json-type/)
- [Open records](/learn/by-example/open-records/)
- [Control openness](/learn/by-example/controlling-openness/)
- [Anydata type](/learn/by-example/anydata-type/)
- [Converting from JSON to user defined type with langlib functions](/learn/by-example/converting-from-json-to-user-defined-type-with-langlib-functions/)
- [Check expression](/learn/by-example/check-expression/)
================================================
FILE: examples/casting-json-to-user-defined-type/casting_json_to_user_defined_type.metatags
================================================
description: This BBE demonstrates how to convert from JSON to record, convert JSON to user-defined type, cast mutable structures, do the numeric conversion on JSON elements, access JSON data, manipulate JSON data, and cast JSON to a user-defined type in Ballerina.
keywords: ballerina, ballerina by example, bbe, conversion, json, record, cast, access, manipulate
================================================
FILE: examples/casting-json-to-user-defined-type/casting_json_to_user_defined_type.out
================================================
$ bal run casting_json_to_user_defined_type.bal
1.0
2.0
================================================
FILE: examples/cdc-advanced-service/cdc_advanced_service.bal
================================================
import ballerina/log;
import ballerinax/cdc;
import ballerinax/mysql;
import ballerinax/mysql.cdc.driver as _;
listener mysql:CdcListener mysqlListener = new (
database = {
username: "root",
password: "Test@123",
includedDatabases: "store_db"
}
);
type Entity record {
int id;
};
type ProductReviews record {
int product_id;
int rating;
};
@cdc:ServiceConfig {
tables: ["store_db.products", "store_db.vendors"]
}
service on mysqlListener {
remote function onRead(Entity after, string tableName) returns cdc:Error? {
log:printInfo(`'${tableName}' cache entry created for Id: ${after.id}`);
}
remote function onCreate(Entity after, string tableName) returns cdc:Error? {
log:printInfo(`'${tableName}' cache entry created for Id: ${after.id}`);
}
remote function onUpdate(Entity before, Entity after, string tableName) returns cdc:Error? {
log:printInfo(`'${tableName}' cache entry updated for Id: ${after.id}.`);
}
remote function onDelete(Entity before, string tableName) returns cdc:Error? {
if tableName == "products" {
log:printInfo(`'products' cache entry deleted for Id: ${before.id}.`);
} else {
log:printInfo(`'vendors' cache entry deleted for Id: ${before.id}.`);
}
}
}
@cdc:ServiceConfig {
tables: ["store_db.product_reviews"]
}
service on mysqlListener {
remote function onRead(ProductReviews after, string tableName) returns cdc:Error? {
log:printInfo(`'product_tot_rating' cache added for Product Id: ${after.product_id}.`);
log:printInfo(`'product_reviews' cache entry added for Product Id: ${after.product_id}.`);
}
remote function onCreate(ProductReviews after, string tableName) returns cdc:Error? {
log:printInfo(`'product_tot_rating' cache added for Product Id: ${after.product_id}.`);
log:printInfo(`'product_reviews' cache entry added for Product Id: ${after.product_id}.`);
}
remote function onUpdate(ProductReviews before, ProductReviews after, string tableName) returns cdc:Error? {
int ratingDiff = after.rating - before.rating;
log:printInfo(`'product_tot_rating' cache updated for Product Id: ${after.product_id}.`);
}
remote function onDelete(ProductReviews before, string tableName) returns cdc:Error? {
log:printInfo(`'product_tot_rating' cache deleted for Product Id: ${before.product_id}.`);
log:printInfo(`'product_reviews' cache entry deleted for Product Id: ${before.product_id}.`);
}
}
================================================
FILE: examples/cdc-advanced-service/cdc_advanced_service.md
================================================
# Change Data Capture - Group Events by Table
The `cdc:Service` connects to a MySQL database using the `mysql:CdcListener`, allowing you to handle change data capture (CDC) events as typed records. The listener detects database changes and calls the relevant remote method (`onRead`, `onCreate`, `onUpdate`, or `onDelete`) in your service, passing the event data.
You can attach multiple `cdc:Service` instances to a single `mysql:CdcListener`. This lets you group related event handling logic into separate services, making your code easier to organize and maintain. Each service can focus on a specific set of tables or event types, improving readability and separation of concerns.
::: code cdc_advanced_service.bal :::
## Prerequisites
- To set up the database, see the [Change Data Capture Ballerina By Example - Prerequisites and Test Data](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/cdc-prerequisite).
Run the program by executing the following command.
::: out cdc_advanced_service.out :::
> **Tip:** To insert additional records for testing, run the `test_cdc_advance_listener.bal` file provided in the [Change Data Capture Ballerina By Example - Prerequisites and Test Data](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/cdc-prerequisite).
## Related links
- [`mysql:CdcListener` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest#CdcListener)
- [`cdc:Service` - API documentation](https://lib.ballerina.io/ballerinax/cdc/latest#Service)
- [`cdc:Service` - Specification](https://github.com/ballerina-platform/module-ballerinax-cdc/blob/main/docs/spec/spec.md#22-service)
================================================
FILE: examples/cdc-advanced-service/cdc_advanced_service.metatags
================================================
description: This BBE demonstrates how to use a CDC (Change Data Capture) service in Ballerina to capture and process real-time changes from a MySQL database.
keywords: ballerina, ballerina by example, bbe, mysql, cdc, change data capture, realtime, database, service, data streaming
================================================
FILE: examples/cdc-advanced-service/cdc_advanced_service.out
================================================
$ bal run cdc_advanced_service.bal
time=2025-05-27T22:26:20.509+05:30 level=INFO module="" message="\'product_tot_rating\' cache added for Product Id: 1001."
time=2025-05-27T22:26:20.520+05:30 level=INFO module="" message="\'product_reviews\' cache entry added for Product Id: 1001."
time=2025-05-27T22:26:20.522+05:30 level=INFO module="" message="\'product_tot_rating\' cache added for Product Id: 1001."
time=2025-05-27T22:26:20.523+05:30 level=INFO module="" message="\'product_reviews\' cache entry added for Product Id: 1001."
time=2025-05-27T22:26:20.525+05:30 level=INFO module="" message="\'product_tot_rating\' cache added for Product Id: 1002."
time=2025-05-27T22:26:20.526+05:30 level=INFO module="" message="\'product_reviews\' cache entry added for Product Id: 1002."
time=2025-05-27T22:26:20.532+05:30 level=INFO module="" message="\'products\' cache entry created for Id: 1001"
time=2025-05-27T22:26:20.534+05:30 level=INFO module="" message="\'products\' cache entry created for Id: 1002"
time=2025-05-27T22:26:20.536+05:30 level=INFO module="" message="\'vendors\' cache entry created for Id: 1"
time=2025-05-27T22:26:20.538+05:30 level=INFO module="" message="\'vendors\' cache entry created for Id: 2"
================================================
FILE: examples/cdc-prerequisite/README.md
================================================
# Change Data Capture Ballerina By Example - Prerequisites and Test Data
The Change Data Capture (CDC) BBEs are based on a fraud detection use case.
## Setting up the MySQL Databases
To set up the required MySQL databases for the BBEs, run the following Ballerina files using the command `bal run`:
1. `setup_finance_db.bal` – Creates the `finance_db` database and its tables.
2. `setup_store_db.bal` – Creates the `store_db` database and its tables.
## Inserting Test Data
To insert test data for the CDC service BBEs, run the following files with `bal run`:
1. `test_cdc_service.bal` – Inserts records into the `finance_db.transactions` table.
2. `test_cdc_advanced_service.bal` – Inserts additional records for advanced CDC scenarios.
================================================
FILE: examples/cdc-prerequisite/setup_finance_db.bal
================================================
import ballerina/sql;
import ballerinax/mysql;
import ballerinax/mysql.driver as _;
// Initializes the database as a prerequisite to `Change Data Capture` samples.
public function main() returns sql:Error? {
mysql:Client mysqlClient = check new (host = "localhost", port = 3306, user = "root",
password = "Test@123"
);
// Creates a database.
_ = check mysqlClient->execute(`CREATE DATABASE finance_db;`);
// Creates `transactions` table in the database.
_ = check mysqlClient->execute(`CREATE TABLE finance_db.transactions (
tx_id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2),
status VARCHAR(50),
created_at DATETIME
)`);
// Adds the records to the `transactions` table.
_ = check mysqlClient->execute(`INSERT INTO finance_db.transactions (user_id, amount, status, created_at)
VALUES(10, 9000.00, 'COMPLETED', '2025-04-01 08:00:00');`);
_ = check mysqlClient->execute(`INSERT INTO finance_db.transactions (user_id, amount, status, created_at)
VALUES(11, 12000.00, 'COMPLETED', '2025-04-01 08:10:00');`);
_ = check mysqlClient->execute(`INSERT INTO finance_db.transactions (user_id, amount, status, created_at)
VALUES(12, 4500.00, 'PENDING', '2025-04-01 08:30:00');`);
check mysqlClient.close();
}
================================================
FILE: examples/cdc-prerequisite/setup_store_db.bal
================================================
import ballerina/sql;
import ballerinax/mysql;
import ballerinax/mysql.driver as _;
// Initializes the database as a prerequisite to `Change Data Capture` samples.
public function main() returns sql:Error? {
mysql:Client mysqlClient = check new (host = "localhost", port = 3306, user = "root",
password = "Test@123"
);
// Creates a database.
_ = check mysqlClient->execute(`CREATE DATABASE store_db;`);
// Creates `vendors` table in the database.
_ = check mysqlClient->execute(`CREATE TABLE store_db.vendors (
id INT PRIMARY KEY,
name VARCHAR(255),
contact_info TEXT
);`);
// Adds the records to the `vendors` table.
_ = check mysqlClient->execute(`INSERT INTO store_db.vendors VALUES (1, 'Samsung', 'contact@samsung.com');`);
_ = check mysqlClient->execute(`INSERT INTO store_db.vendors VALUES (2, 'Apple', 'contact@apple.com');`);
// Creates `products` table in the database.
_ = check mysqlClient->execute(`CREATE TABLE store_db.products (
id INT PRIMARY KEY,
name VARCHAR(255),
price DECIMAL(10,2),
description TEXT,
vendor_id INT,
FOREIGN KEY (vendor_id) REFERENCES vendors(id)
);`);
// Adds the records to the `products` table.
_ = check mysqlClient->execute(`INSERT INTO store_db.products VALUES
(1001, 'Samsung Galaxy S24', 999.99, 'Flagship phone with AI camera', 1);`);
_ = check mysqlClient->execute(`INSERT INTO store_db.products VALUES
(1002, 'Apple iPhone 15 Pro', 1099.00, 'New titanium design', 2);`);
// Creates `product_reviews` table in the database.
_ = check mysqlClient->execute(`CREATE TABLE store_db.product_reviews (
review_id INT PRIMARY KEY,
product_id INT,
rating INT CHECK (rating BETWEEN 1 AND 5),
comment TEXT,
FOREIGN KEY (product_id) REFERENCES products(id)
);`);
// Adds the records to the `product_reviews` table.
_ = check mysqlClient->execute(`INSERT INTO store_db.product_reviews VALUES (1, 1001, 5, 'Amazing camera');`);
_ = check mysqlClient->execute(`INSERT INTO store_db.product_reviews VALUES (2, 1001, 4, 'Great battery life');`);
_ = check mysqlClient->execute(`INSERT INTO store_db.product_reviews VALUES (3, 1002, 5, 'Best iPhone yet');`);
check mysqlClient.close();
}
================================================
FILE: examples/cdc-prerequisite/test_cdc_advanced_service.bal
================================================
import ballerina/sql;
import ballerinax/mysql;
import ballerinax/mysql.driver as _;
// Insert test data for `Change Data Capture` samples.
public function main() returns sql:Error? {
mysql:Client mysqlClient = check new (host = "localhost", port = 3306, user = "root",
password = "Test@123"
);
_ = check mysqlClient->execute(
`UPDATE store_db.products SET price = price * 0.9 WHERE id = 1002;`
);
_ = check mysqlClient->execute(
`UPDATE store_db.product_reviews SET rating = rating - 1 WHERE product_id = 1002;`
);
_ = check mysqlClient->execute(
`INSERT store_db.products VALUES (1003, "Samsung Galaxy S20", 499.99, "Old Smartphone", 2);`
);
_ = check mysqlClient->execute(
`DELETE FROM store_db.products WHERE id = 1003;`
);
check mysqlClient.close();
}
================================================
FILE: examples/cdc-prerequisite/test_cdc_service.bal
================================================
import ballerina/sql;
import ballerinax/mysql;
import ballerinax/mysql.driver as _;
// Insert test data for `Change Data Capture` samples.
public function main() returns sql:Error? {
mysql:Client mysqlClient = check new (host = "localhost", port = 3306, user = "root",
password = "Test@123"
);
// Adds the records to the `transactions` table.
_ = check mysqlClient->execute(`INSERT INTO finance_db.transactions (user_id, amount, status, created_at)
VALUES (11, 2000.00, 'COMPLETED', '2025-04-01 08:10:00');`);
_ = check mysqlClient->execute(`INSERT INTO finance_db.transactions (user_id, amount, status, created_at)
VALUES (11, 12000.00, 'COMPLETED', '2025-04-01 08:10:00');`);
check mysqlClient.close();
}
================================================
FILE: examples/cdc-service/cdc_service.bal
================================================
import ballerina/log;
import ballerinax/cdc;
import ballerinax/mysql;
import ballerinax/mysql.cdc.driver as _;
type Transactions record {|
int tx_id;
int user_id;
float amount;
string status;
int created_at;
|};
listener mysql:CdcListener financeDBListener = new (
database = {
username: "root",
password: "Test@123",
includedDatabases: "finance_db"
}
);
service on financeDBListener {
isolated remote function onRead(Transactions trx) returns cdc:Error? {
if trx.amount > 10000.00 {
string fraudAlert =
string `Fraud detected! Id: ${trx.tx_id}, User Id: ${trx.user_id}, Amount: $${trx.amount}`;
log:printInfo(fraudAlert);
}
}
isolated remote function onCreate(Transactions trx) returns cdc:Error? {
if trx.amount > 10000.00 {
string fraudAlert =
string `Fraud detected! Id: ${trx.tx_id}, User Id: ${trx.user_id}, Amount: $${trx.amount}`;
log:printInfo(fraudAlert);
}
}
}
================================================
FILE: examples/cdc-service/cdc_service.md
================================================
# Change Data Capture - Listen to Database
The `cdc:Service` connects to a MySQL database using the `mysql:CdcListener`, allowing you to bind change data capture (CDC) events directly to subtypes of `record{}`.
The listener captures database changes and dispatches them as records to one of the `onRead`, `onCreate`, `onUpdate`, or `onDelete` remote methods in your service. To use this, specify the required payload type as the argument to these methods, which should be a subtype of `record{}`. When new CDC events are received, the method corresponding to the operation is invoked.
If the payload does not match the defined type, a `cdc:PayloadBindingError` will be logged. You can customize error handling by implementing the optional `onError` remote method in your service.
::: code cdc_service.bal :::
## Prerequisites
- To set up the database, see the [Change Data Capture Ballerina By Example - Prerequisites and Test Data](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/cdc-prerequisite).
Run the program by executing the following command.
::: out cdc_service.out :::
> **Tip:** To insert additional records for testing, run the `test_cdc_listener.bal` file provided in the [Change Data Capture Ballerina By Example - Prerequisites and Test Data](https://github.com/ballerina-platform/ballerina-distribution/tree/master/examples/cdc-prerequisite).
## Related links
- [`mysql:CdcListener` - API documentation](https://lib.ballerina.io/ballerinax/mysql/latest#CdcListener)
- [`cdc:Service` - API documentation](https://lib.ballerina.io/ballerinax/cdc/latest#Service)
- [`cdc:Service` - Specification](https://github.com/ballerina-platform/module-ballerinax-cdc/blob/main/docs/spec/spec.md#22-service)
================================================
FILE: examples/cdc-service/cdc_service.metatags
================================================
description: This BBE demonstrates how to use a CDC (Change Data Capture) service in Ballerina to capture and process real-time changes from a MySQL database.
keywords: ballerina, ballerina by example, bbe, mysql, cdc, change data capture, realtime, database, service, data streaming
================================================
FILE: examples/cdc-service/cdc_service.out
================================================
$ bal run cdc_service.bal
time=2025-05-27T22:17:45.632+05:30 level=INFO module="" message="Fraud detected! Id: 2, User Id: 11, Amount: $12000.0"
================================================
FILE: examples/chat-agents/chat_agents.bal
================================================
import ballerina/ai;
import ballerina/http;
import ballerina/time;
import ballerina/uuid;
// Declare a service attached to an `ai:Listener` listener
// to interact with the agent.
service /tasks on new ai:Listener(8080) {
resource function post chat(@http:Payload ai:ChatReqMessage request)
returns ai:ChatRespMessage|error {
string response = check taskAssistantAgent.run(request.message, request.sessionId);
return {message: response};
}
}
type Task record {|
string description;
time:Date dueBy?;
time:Date createdAt = time:utcToCivil(time:utcNow());
time:Date completedAt?;
boolean completed = false;
|};
// Simple in-memory task management.
isolated map tasks = {
"a2af0faa-3b73-4184-9be1-87b29a963be6": {
description: "Buy groceries",
dueBy: time:utcToCivil(time:utcAddSeconds(time:utcNow(), 60 * 5))
}
};
// Define the functions that the agent can use as tools.
// The LLM will identify the arguments to pass to these functions
// based on the user input and the tool (function) signatures.
@ai:AgentTool
isolated function addTask(string description, time:Date? dueBy) returns error? {
lock {
tasks[uuid:createRandomUuid()] = {description, dueBy: dueBy.clone()};
}
}
@ai:AgentTool
isolated function listTasks() returns Task[] {
lock {
return tasks.toArray().clone();
}
}
@ai:AgentTool
isolated function getCurrentDate() returns time:Date {
time:Civil {year, month, day} = time:utcToCivil(time:utcNow());
return {year, month, day};
}
// Define an AI agent with a system prompt and a set of tools.
// The agent will use these tools to help manage a task list,
// following the system prompt instructions.
final ai:Agent taskAssistantAgent = check new ({
systemPrompt: {
role: "Task Assistant",
instructions: string `You are a helpful assistant for
managing a to-do list. You can manage tasks and
help a user plan their schedule.`
},
// Specify the functions the agent can use as tools.
tools: [addTask, listTasks, getCurrentDate],
// Use the default model provider (with configuration added
// via a Ballerina VS Code command).
model: check ai:getDefaultModelProvider()
});
================================================
FILE: examples/chat-agents/chat_agents.md
================================================
# Chat agents
Ballerina enables developers to easily create intelligent chat agents powered by large language models (LLMs) and integrated with tools, including local tools, MCP tools, and external APIs. These chat agents can maintain conversations across multiple sessions, handle concurrent users, and seamlessly integrate with web services and external systems.
This example demonstrates how to create a chat agent service that manages to-do lists while maintaining separate conversation sessions for different users through externally managed session IDs.
Copy the source to a Ballerina project and use the `Try it` CodeLens above the service declaration to use a chat interface within VS Code.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code chat_agents.bal :::
::: out chat_agents.out :::
## Related links
- [The Agent with local tools example](/learn/by-example/ai-agent-local-tools)
- [The Agent with MCP integration example](/learn/by-example/ai-agent-mcp-integration)
- [The Agent with external endpoint integration example](/learn/by-example/ai-agent-external-endpoint-integration)
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/chat-agents/chat_agents.metatags
================================================
description: This BBE demonstrates chat agents.
keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider, agent, chat
================================================
FILE: examples/chat-agents/chat_agents.out
================================================
$ bal run chat_agents.bal
================================================
FILE: examples/check-expression/check_expression.bal
================================================
import ballerina/io;
// Convert `bytes` to a `string` value and then to an `int` value.
function intFromBytes(byte[] bytes) returns int|error {
string|error res = string:fromBytes(bytes);
// Explicitly check if the result is an error and
// immediately return if so.
if res is error {
return res;
}
return int:fromString(res);
}
// Same as `intFromBytes` but with `check` instead of explicitly checking for error and returning.
function intFromBytesWithCheck(byte[] bytes) returns int|error {
string str = check string:fromBytes(bytes);
return int:fromString(str);
}
public function main() {
int|error res1 = intFromBytesWithCheck([104, 101, 108, 108, 111]);
io:println(res1);
int|error res2 = intFromBytes([104, 101, 108, 108, 111]);
io:println(res2);
}
================================================
FILE: examples/check-expression/check_expression.md
================================================
# Check expression
If an expression can evaluate to an error value, you can use the `check` expression to indicate that you want the execution of the current block to terminate with that error as the result. This results in the error value being returned from the current function/worker, unless the `check` expression is used in a failure-handling statement (e.g., statement with on fail, retry statement).
::: code check_expression.bal :::
::: out check_expression.out :::
+ [`check` semantics](https://ballerina.io/learn/concurrency/#check-semantics)
================================================
FILE: examples/check-expression/check_expression.metatags
================================================
description: This BBE demonstrates how the check expression is used in Ballerina to handle errors
keywords: ballerina, ballerina by example, bbe, error, check
================================================
FILE: examples/check-expression/check_expression.out
================================================
$ bal run check_expression.bal
error("{ballerina/lang.int}NumberParsingError",message="'string' value 'hello' cannot be converted to 'int'")
error("{ballerina/lang.int}NumberParsingError",message="'string' value 'hello' cannot be converted to 'int'")
================================================
FILE: examples/check-semantics/check_semantics.bal
================================================
import ballerina/io;
public function main() returns error? {
do {
// If `check` gets an error from either `foo()` or `bar()` invocations, the error will be caught at the `on fail`
check foo();
check bar();
if !isOK() {
// Fails explicitly with an error.
fail error("not OK");
}
}
// Failure with the respective error is caught by the `on fail` block.
on fail var e {
io:println(e.toString());
return e;
}
return;
}
function foo() returns error? {
io:println("OK");
return;
}
function bar() returns error? {
io:println("OK");
return;
}
function isOK() returns boolean {
// Returns `false`.
return false;
}
================================================
FILE: examples/check-semantics/check_semantics.md
================================================
# Check semantics
`check` semantics is not to simply return on an `error` value. When `check` gets an `error` value, it fails. The enclosing block decides how to handle the failure. Most blocks pass the failure up to the enclosing block.
Function definition handles the failure by returning the error. `on fail` can catch the error. `fail` statement is like `check`, but it always fails. Differs from exceptions in that control flow is explicit.
::: code check_semantics.bal :::
::: out check_semantics.out :::
================================================
FILE: examples/check-semantics/check_semantics.metatags
================================================
description: This BBE explains the `check` semantics in Ballerina.
keywords: ballerina, ballerina by example, bbe, check, fail, exceptions, on fail, error
================================================
FILE: examples/check-semantics/check_semantics.out
================================================
$ bal run check_semantics.bal
OK
OK
error("not OK")
error: not OK
================================================
FILE: examples/child-loggers-with-context/child_loggers_with_context.bal
================================================
import ballerina/log;
function processUserRequest(string userId, string requestId) returns error? {
// Get the root logger
log:Logger rootLogger = log:root();
// Create a child logger with request-specific context
log:Logger requestLogger = check rootLogger.withContext(userId = userId, requestId = requestId);
// All logs from this logger will include the userId and requestId context
requestLogger.printInfo("User permissions validated successfully");
// Create a nested logger with additional context for a specific operation
log:Logger operationLogger = check requestLogger.withContext(operation = "dataTransformation");
operationLogger.printWarn("Using fallback transformation method", reason = "primary method unavailable");
operationLogger.printInfo("Data transformation completed", duration = "250ms");
}
public function main() returns error? {
// Process multiple requests with different contexts
check processUserRequest("alice123", "req-001");
check processUserRequest("bob456", "req-002");
}
================================================
FILE: examples/child-loggers-with-context/child_loggers_with_context.md
================================================
# Child loggers with context
This example demonstrates how to create child loggers with specific additional context using the Ballerina logging module. Child loggers inherit context from their parent loggers, allowing for hierarchical logging with layered context.
::: code child_loggers_with_context.bal :::
::: out child_loggers_with_context.out :::
The contextual logging APIs make it easy to trace complete request flows and correlate related log entries without manually managing context in each log statement.
## Related links
- [`log` module - Specification](https://ballerina.io/spec/log/#431-loggers-with-additional-context)
- [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest)
================================================
FILE: examples/child-loggers-with-context/child_loggers_with_context.metatags
================================================
description: BBE on how to create child loggers with context in Ballerina.
keywords: ballerina, ballerina by examples, bbe, log, context, contextual logging, context, child logger
================================================
FILE: examples/child-loggers-with-context/child_loggers_with_context.out
================================================
$ bal run child_loggers_with_context.bal
time=2025-08-29T12:28:56.385+05:30 level=INFO module=tharmigan/log_test message="User permissions validated successfully" env="prod" nodeId="server-001" userId="alice123" requestId="req-001"
time=2025-08-29T12:28:56.395+05:30 level=WARN module=tharmigan/log_test message="Using fallback transformation method" reason="primary method unavailable" env="prod" nodeId="server-001" userId="alice123" requestId="req-001" operation="dataTransformation"
time=2025-08-29T12:28:56.397+05:30 level=INFO module=tharmigan/log_test message="Data transformation completed" duration="250ms" env="prod" nodeId="server-001" userId="alice123" requestId="req-001" operation="dataTransformation"
time=2025-08-29T12:28:56.399+05:30 level=INFO module=tharmigan/log_test message="User permissions validated successfully" env="prod" nodeId="server-001" userId="bob456" requestId="req-002"
time=2025-08-29T12:28:56.401+05:30 level=WARN module=tharmigan/log_test message="Using fallback transformation method" reason="primary method unavailable" env="prod" nodeId="server-001" userId="bob456" requestId="req-002" operation="dataTransformation"
time=2025-08-29T12:28:56.402+05:30 level=INFO module=tharmigan/log_test message="Data transformation completed" duration="250ms" env="prod" nodeId="server-001" userId="bob456" requestId="req-002" operation="dataTransformation"
================================================
FILE: examples/client-class/client_class.bal
================================================
import ballerina/http;
import ballerina/io;
public type Album readonly & record {|
string title;
string artist;
|};
public client class Client {
// The HTTP client to access the HTTP services.
private final http:Client httpClient;
function init(string url) returns error? {
self.httpClient = check new (url);
}
resource function get [string path]() returns Album[]|error {
return check self.httpClient->/[path];
}
resource function post [string path](Album album) returns error? {
Album updatedAlbum = check self.httpClient->/[path].post(album);
io:println("\nPOST request: ", updatedAlbum);
}
}
public function main() returns error? {
Client albumClient = check new ("localhost:9090");
// `->` is used to access the resource/remote methods in the client class.
// Sends a `GET` request to the `/albums` resource.
Album[] albums = check albumClient->/["albums"];
io:println(albums);
Album newAlbum = {"title": "Sarah Vaughan and Clifford Brown", "artist": "Sarah Vaughan"};
// Sends a `POST` request to the `/albums` resource.
check albumClient->/["albums"].post(newAlbum);
}
================================================
FILE: examples/client-class/client_class.md
================================================
# Client class
The `client` keyword is used with class definition to define a client class. Ballerina supports defining client objects to allow a program to interact with remote network services. They are a special kind of object that contain `remote` and `resource` methods in addition to regular methods.
Similarly, class objects can be constructed using an object constructor as well.
## Prerequisites
- Run the HTTP service given in the [Basic REST service](/learn/by-example/http-basic-rest-service/) example.
::: code client_class.bal :::
Run the client program by executing the following command.
::: out client_class.out :::
## Related links
- [Resource methods](/learn/by-example/resource-methods/)
- [Defining classes](/learn/by-example/defining-classes/)
- [Object types](/learn/by-example/object-types/)
================================================
FILE: examples/client-class/client_class.metatags
================================================
description: This BBE demonstrates creating a client object from a client class definition, and sending `get` and `post` requests to an HTTP client.
keywords: ballerina, ballerina by example, bbe, class, client, http, remote, resource
================================================
FILE: examples/client-class/client_class.out
================================================
$ bal run client_class.bal
[{"title":"Blue Train","artist":"John Coltrane"},{"title":"Jeru","artist":"Gerry Mulligan"}]
POST request: {"title":"Sarah Vaughan and Clifford Brown","artist":"Sarah Vaughan"}
================================================
FILE: examples/combining-isolated-functions-and-lock/combining_isolated_functions_and_lock.bal
================================================
import ballerina/io;
type R record {
int v;
};
// The initialization expression of an `isolated` variable
// has to be an `isolated` expression, which itself will be
// an `isolated` root.
isolated R r = {v: 0};
isolated function setGlobal(int n) {
// An `isolated` variable can be accessed within
// a `lock` statement.
lock {
r.v = n;
}
}
public function main() {
setGlobal(200);
// Accesses the `isolated` variable within a
// `lock` statement.
lock {
io:println(r);
}
}
================================================
FILE: examples/combining-isolated-functions-and-lock/combining_isolated_functions_and_lock.md
================================================
# Combining isolated functions and lock
Combining `isolated` functions and `lock` allows `isolated` functions to use `lock` to access mutable module-level state. Key concept is `isolated` root. A value `r` is an `isolated` root if mutable state reachable from `r` cannot be reached from outside except through `r`.
An expression is an `isolated` expression if it follows rules that guarantee that its value will be an `isolated` root. E.g.,
- an expression with a type that is a subtype of `readonly` is always `isolated`
- an expression `[E1, E2]` is isolated if `E1` and `E2` are `isolated`
- an expression `f(E1, E2)` is `isolated` if `E1` and `E1` are `isolated`, and the type of `f` is an `isolated` function.
::: code combining_isolated_functions_and_lock.bal :::
Executing the above code changes the value of `V` to 200 as shown below.
::: out combining_isolated_functions_and_lock.out :::
================================================
FILE: examples/combining-isolated-functions-and-lock/combining_isolated_functions_and_lock.metatags
================================================
description: This BBE demonstrates combining isolated functions and lock in Ballerina.
keywords: ballerina, ballerina by example, bbe, isolated functions, lock
================================================
FILE: examples/combining-isolated-functions-and-lock/combining_isolated_functions_and_lock.out
================================================
$ bal run combining_isolated_functions_and_lock.bal
{"v":200}
================================================
FILE: examples/commit-rollback-handlers/commit_rollback_handlers.bal
================================================
import ballerina/io;
public function main() returns error? {
transaction {
check update();
check commit;
}
return;
}
transactional function update() returns error? {
check updateDatabase();
// Registers a commit handler to be invoked when `commit` is executed.
transaction:onCommit(sendEmail);
transaction:onRollback(logError);
}
function updateDatabase() returns error? {
io:println("Database updated");
return;
}
isolated function sendEmail('transaction:Info info) {
io:println("Email sent.");
}
isolated function logError(transaction:Info info, error? cause, boolean willRetry) {
io:println("Logged database update failure");
}
================================================
FILE: examples/commit-rollback-handlers/commit_rollback_handlers.md
================================================
# Commit/rollback handlers
Often code needs to get executed depending on whether a transaction is committed. Testing the result of the `commit` within the transaction statement works. However, it is inconvenient from a modularity perspective, particularly, when you want to undo changes on `rollback`.
This seems much worse in a distributed transaction when the transaction statement is in another program. Ballerina provides `commit`/`rollback` handlers, which are functions that get executed when the decision whether to commit is known.
::: code commit_rollback_handlers.bal :::
::: out commit_rollback_handlers.out :::
================================================
FILE: examples/commit-rollback-handlers/commit_rollback_handlers.metatags
================================================
description: This BBE introduces commit/rollback handlers in Ballerina
keywords: ballerina, ballerina by example, bbe, transactions, commit, rollback, handlers, commit handlers, rollback handlers
================================================
FILE: examples/commit-rollback-handlers/commit_rollback_handlers.out
================================================
$ bal run commit_rollback_handlers.bal
Database updated
Email sent.
================================================
FILE: examples/computed-field-key/computed_field_key.bal
================================================
import ballerina/io;
const ID = "unique-identifier";
function getStudentDetails(string studentId,
string name,
string org,
string orgName) returns map {
return {
// The `unique-identifier` value is substituted from the constant `ID` as the key.
[ID] : studentId,
"name": name,
// The key computed at runtime will be the concatenation of `_` and
// the argument passed for `org`.
["_" + org] : orgName
};
}
public function main() {
map studentDetails = getStudentDetails("stu123", "John", "school", "West High");
io:println(studentDetails);
}
================================================
FILE: examples/computed-field-key/computed_field_key.md
================================================
# Computed field key
Ballerina allows you to have computed values as keys in a map. This can be done by specifying the key as an expression within square brackets.
This is particularly useful when you want to define and use constants for key values.
::: code computed_field_key.bal :::
::: out computed_field_key.out :::
## Related links
- [Maps](/learn/by-example/maps/)
================================================
FILE: examples/computed-field-key/computed_field_key.metatags
================================================
description: This BBE demonstrates the use of computed field keys in mapping constructors, computed values as keys, and constants for key values in Ballerina.
keywords: ballerina, ballerina by example, bbe, computed key, mapping constructor, map, record, computed field key, computed name field
================================================
FILE: examples/computed-field-key/computed_field_key.out
================================================
$ bal run computed_field_key.bal
{"name":"John","unique-identifier":"stu123","_school":"West High"}
================================================
FILE: examples/conditional-send/conditional_send.bal
================================================
import ballerina/io;
public function main() {
boolean isDataReady = true;
worker w1 {
// A send action can be used in a conditional context.
if isDataReady {
10 -> function;
}
}
worker w2 {
if isDataReady {
1 -> function;
} else {
0 -> function;
}
}
// The send action corresponding to this receive action is conditionally executed.
// Thus, there is a possibility that the send action may not get executed.
// Therefore, the static type of the receive includes the `error:NoMessage` type
// indicating the absence of a message in such cases.
int|error:NoMessage w1Message = <- w1;
io:println(w1Message);
// Two different conditional send actions exist within the worker `w3`.
// Therefore, an alternate receive action can be used to receive them.
int|error:NoMessage w2Message = <- w2 | w2;
io:println(w2Message);
}
================================================
FILE: examples/conditional-send/conditional_send.md
================================================
# Conditional send
The send action in workers can be used in a conditional context, allowing for more flexible and dynamic inter-worker communication based on specific conditions. The receiver side in a conditional send might not always receive a message. Thus, to handle such scenarios, the static type of the receive action includes the `error:NoMessage` type.
::: code conditional_send.bal :::
::: out conditional_send.out :::
================================================
FILE: examples/conditional-send/conditional_send.metatags
================================================
description: This BBE demonstrates the use of conditional send within workers in Ballerina
keywords: ballerina, ballerina by example, bbe, workers, conditional send
================================================
FILE: examples/conditional-send/conditional_send.out
================================================
$ bal run conditional_send.bal
10
1
================================================
FILE: examples/configurable-variables/configurable_variables.bal
================================================
// The host of the database server. The default value is `localhost`.
configurable string dbHost = "localhost";
// This specifies that the password must be supplied in a configuration file.
configurable string password = ?;
================================================
FILE: examples/configurable-variables/configurable_variables.md
================================================
# Configurable variables
A module-level variable can be declared as configurable. The initializer of a configurable variable can be overridden at runtime (e.g., by a TOML file). A variable for which a configuration is required can use an initializer of `?`. The type of a configurable variable must be a subtype of `anydata`.
For more information, see [Configure a sample Ballerina service](/learn/configure-a-sample-ballerina-service/).
::: code configurable_variables.bal :::
::: out configurable_variables.out :::
================================================
FILE: examples/configurable-variables/configurable_variables.metatags
================================================
description: This BBE demonstrates configurable variables in Ballerina.
keywords: ballerina, ballerina by example, bbe, configurable, variable
================================================
FILE: examples/configurable-variables/configurable_variables.out
================================================
$ bal run configurable_variables.bal
================================================
FILE: examples/configuring-via-cli/configuring_via_cli.bal
================================================
import ballerina/io;
enum HttpVersion {
HTTP_1_0 = "1.0",
HTTP_1_1 = "1.1",
HTTP_2_0 = "2.0"
}
// The configurable variables of `float`, `union`, and `enum` types are initialized.
configurable float maxPayload = 1.0;
configurable string|int localId = ?;
configurable HttpVersion httpVersion = HTTP_1_0;
public function main() {
io:println("maximum payload (in MB): ", maxPayload);
io:println("local ID: ", localId);
io:println("HTTP version: ", httpVersion);
}
================================================
FILE: examples/configuring-via-cli/configuring_via_cli.md
================================================
# Configure via command-line arguments
The values of the configurable variables can be configured through the command-line arguments when executing the Ballerina program. The provided value is expected to be the `toString()` representation of the intended value.
The command-line-based configuration is only supported for configurable variables of types `int`, `byte`, `float`, `boolean`, `string`, `decimal`, `enum`, and `xml`.
The `-Ckey=value` syntax can be used to provide values through the command-line parameters.
For more information, see [Configure via command-line arguments](/learn/provide-values-to-configurable-variables/#provide-via-command-line-arguments/).
::: code configuring_via_cli.bal :::
::: out configuring_via_cli.out :::
================================================
FILE: examples/configuring-via-cli/configuring_via_cli.metatags
================================================
description: This BBE demonstrates how to configure configurable variables in Ballerina via command-line arguments.
keywords: ballerina, ballerina by example, bbe, configurable, variable, cli
================================================
FILE: examples/configuring-via-cli/configuring_via_cli.out
================================================
$ bal run configuring_via_cli.bal -- -CmaxPayload=2.5 -ClocalId=ID-1 -ChttpVersion=2.0
maximum payload (in MB): 2.5
local ID: ID-1
HTTP version: 2.0
================================================
FILE: examples/configuring-via-toml/configuring-via-toml.md
================================================
# Configure via TOML files
The values of the configurable variables can be configured through the configuration files in the TOML(v0.4) format.
The file location can be specified through an environment variable with the name `BAL_CONFIG_FILES`. Specifying multiple configuration files is supported using this environment variable with the OS-specific separator. If an environment variable is not specified, a file named `Config.toml` will be sought in the current working directory.
An environment variable with the name `BAL_CONFIG_DATA` can be used to provide the configuration file content instead of a separate file.
For more information, see [Configure via TOML syntax](/learn/provide-values-to-configurable-variables/#provide-via-toml-syntax/).
::: code configuring_via_toml.bal :::
To run the example, copy the following content to a file named `Config.toml` in the current directory.
::: code Config.toml :::
::: out configuring_via_toml.out :::
================================================
FILE: examples/configuring-via-toml/configuring_via_toml.bal
================================================
import ballerina/io;
type UserInfo record {|
readonly string username;
string password;
|};
type UserTable table key(username);
enum HttpVersion {
HTTP_1_0 = "1.0",
HTTP_1_1 = "1.1",
HTTP_2_0 = "2.0"
}
// The configurable variables of `float`, `string[]`, enum, `record`, and `table` types are initialized.
configurable float maxPayload = 1.0;
configurable string[] acceptTypes = ["text/plain"];
configurable HttpVersion httpVersion = HTTP_1_0;
configurable UserInfo & readonly admin = ?;
configurable UserTable & readonly users = ?;
public function main() {
io:println("maximum payload (in MB): ", maxPayload);
io:println("accepted content types: ", acceptTypes);
io:println("HTTP version: ", httpVersion);
io:println("admin details: ", admin);
io:println("users: ", users);
}
================================================
FILE: examples/configuring-via-toml/configuring_via_toml.metatags
================================================
description: This BBE demonstrates how to configure configurable variables in Ballerina via a TOML file.
keywords: ballerina, ballerina by example, bbe, configurable, variable, toml
================================================
FILE: examples/configuring-via-toml/configuring_via_toml.out
================================================
$ bal run configuring_via_toml.bal
maximum payload (in MB): 3.21
accepted content types: ["application/xml","application/json","text/plain"]
HTTP version: 1.0
admin details: {"username":"admin","password":"password"}
users: [{"username":"John","password":"abc123"},{"username":"Bob","password":"cde456"}]
================================================
FILE: examples/const-and-final/const_and_final.bal
================================================
import ballerina/io;
const int MAX_VALUE = 1000;
// Constants can be defined without the type. Then, the type is inferred from the right-hand side.
const URL = "https://ballerina.io";
// Mapping and list constants can also be defined.
const HTTP_OK = {httpCode: 200, message: "OK"};
const ERROR_CODES = [200, 202, 400];
// The value for the `msg` variable can only be assigned once.
final string msg = loadMessage();
public function main() {
io:println(MAX_VALUE);
io:println(URL);
io:println(HTTP_OK);
io:println(ERROR_CODES);
io:println(msg);
}
function loadMessage() returns string {
return "Hello World";
}
================================================
FILE: examples/const-and-final/const_and_final.md
================================================
# Const and final
`const` means immutable and known at compile-time. Its type is singleton: a set containing a single value. A variable or a class field can be declared as `final`, which means it cannot be assigned after it has been initialized.
::: code const_and_final.bal :::
::: out const_and_final.out :::
================================================
FILE: examples/const-and-final/const_and_final.metatags
================================================
description: This BBE demonstrates how `const` and `final` values are used in Ballerina.
keywords: ballerina, ballerina by example, bbe, const, final, map const, list const
================================================
FILE: examples/const-and-final/const_and_final.out
================================================
$ bal run const_and_final
1000
https://ballerina.io
{"httpCode":200,"message":"OK"}
[200,202,400]
Hello World
================================================
FILE: examples/constraint-validations/constraint_validations.bal
================================================
import ballerina/constraint;
// Constraint on the `int` type.
@constraint:Int {
minValue: 18
}
type Age int;
type Student record {|
// Constraint on the `string`-typed record field.
@constraint:String {
pattern: re`[0-9]{6}[A-Z|a-z]`
}
string id;
string name;
// Constrained type used as a record field.
Age age;
// Constraint on the `string[]`-typed record field.
@constraint:Array {
minLength: 1,
maxLength: 10
}
string[] subjects;
|};
public function main() returns error? {
Student student = {
id: "200146B",
name: "David John",
age: 25,
subjects: ["Maths", "Science", "English"]
};
// To validate the constraints on the `Student` record, the `validate` function should be
// called explicitly. If the validation is successful, then, this function returns the type
// descriptor of the value that is validated.
student = check constraint:validate(student);
// Set the student's age to 17, which will violate the `minValue` constraint on `Age`.
student.age = 17;
// When the validation fails, the `validate` function returns a `constraint:Error`.
student = check constraint:validate(student);
}
================================================
FILE: examples/constraint-validations/constraint_validations.md
================================================
# Constraint validations
Validating user input is a common requirement in most applications. This can prevent user entry errors before the app attempts to process the data.
The `constraint` library provides such validations for the following Ballerina types: `int`, `float`, `decimal`, `string`, and `anydata[]`.
For more information on the underlying module, see the [`constraint` module](https://lib.ballerina.io/ballerina/constraint/latest/).
::: code constraint_validations.bal :::
::: out constraint_validations.out :::
================================================
FILE: examples/constraint-validations/constraint_validations.metatags
================================================
description: This BBE shows how to apply and validate constraints in Ballerina.
keywords: ballerina, ballerina by example, bbe, constraint, validation
================================================
FILE: examples/constraint-validations/constraint_validations.out
================================================
$ bal run constraint_validations.bal
error: Validation failed for '$.age:minValue' constraint(s).
================================================
FILE: examples/consuming-services/consuming_services.bal
================================================
import ballerina/http;
import ballerina/io;
public function main() returns error? {
// A client object is created by applying `new` to a client class.
http:Client httpClient = check new ("https://api.github.com");
// The remote method calls use the `->` syntax. This enables the sequence diagram view.
http:Response resp = check httpClient->get("/orgs/ballerina-platform/repos");
io:println(resp.statusCode);
}
================================================
FILE: examples/consuming-services/consuming_services.md
================================================
# Consume services: client objects
Ballerina has a language construct called client objects. They are a special kind of objects that contain `remote` methods in addition to regular methods. `remote` methods are used to interact with a remote service.
Applications typically do not need to write client classes, which are either provided by library modules or generated from some flavor of IDL.
::: code consuming_services.bal :::
::: out consuming_services.out :::
================================================
FILE: examples/consuming-services/consuming_services.metatags
================================================
description: This BBE explains basics of consuming services in Ballerina.
keywords: ballerina, ballerina by example, bbe, Client, Client Objects, HTTP Client, Consuming services
================================================
FILE: examples/consuming-services/consuming_services.out
================================================
$ bal run consuming_services.bal
200
================================================
FILE: examples/continue-statement/continue_statement.bal
================================================
import ballerina/io;
public function main() {
foreach int i in 0...5 {
if (i == 2) {
// A `continue` statement can be used to skip the current iteration.
continue;
}
io:println(i);
}
int i = 0;
while i <= 5 {
if i == 2 {
i = i + 1;
// A `continue` statement can be used to skip the current iteration.
continue;
}
io:println(i);
i = i + 1;
}
}
================================================
FILE: examples/continue-statement/continue_statement.md
================================================
# Continue statement
`continue` statements can be used to skip the current iteration within a nearest enclosing `while` and `foreach` statement.
::: code continue_statement.bal :::
::: out continue_statement.out :::
## Related links
- [Break statement](/learn/by-example/break-statement/)
- [While statement](/learn/by-example/while-statement/)
- [Foreach statement](/learn/by-example/foreach-statement/)
================================================
FILE: examples/continue-statement/continue_statement.metatags
================================================
description: This BBE demonstrates how to use the `continue` statement to skip the current iteration in Ballerina.
keywords: ballerina, ballerina by example, bbe, foreach, loops, iterate, iterable, break, skip loop, while, repeat
================================================
FILE: examples/continue-statement/continue_statement.out
================================================
$ bal run continue_statement.bal
0
1
3
4
5
0
1
3
4
5
================================================
FILE: examples/controlling-openness/controlling_openness.bal
================================================
import ballerina/io;
type Student record {|
string name;
string country;
|};
type PartTimeStudent record {|
string name;
string country;
// Rest descriptor of type `string` allows additional fields with `string` values.
string...;
|};
public function main() {
// `s1` can only have fields exclusively specified in `Student`.
Student s1 = {name: "Anne", country: "UK"};
// `s1` is a `map` with `string` values.
map s2 = s1;
io:println(s2);
// `s3` has an additional `faculty` field.
PartTimeStudent s3 = {
name: "Anne",
country: "UK",
"faculty": "Science"
};
// Accesses the `faculty` field in `s3`.
string? faculty = s3["faculty"];
io:println(faculty);
// `s3` is a `map` with `string` values.
map s4 = s3;
io:println(s4);
}
================================================
FILE: examples/controlling-openness/controlling_openness.md
================================================
# Controlling openness
Use `record {| ... |}` to describe a record type that allows exclusively what is specified in the body. Use an open record of type `record {| T...; |}` to allow other fields of type `T`. `map` is the same as `record {| T...; |}`.
::: code controlling_openness.bal :::
::: out controlling_openness.out :::
## Related links
- [Records](/learn/by-example/records/)
- [Open Records](/learn/by-example/open-records/)
- [Maps](/learn/by-example/maps/)
================================================
FILE: examples/controlling-openness/controlling_openness.metatags
================================================
description: This BBE demonstrates controlling openness, open records, creating an open record, creating a closed record, inclusive record type, creating a record with unspecified fields, and allowing unspecified fields of a certain type in a record.
keywords: ballerina, ballerina by example, bbe, open record, closed record, record, record type, record field, rest type, rest field, inclusive record, unspecified fields
================================================
FILE: examples/controlling-openness/controlling_openness.out
================================================
$ bal run controlling_openness.bal
{"name":"Anne","country":"UK"}
Science
{"name":"Anne","country":"UK","faculty":"Science"}
================================================
FILE: examples/convert-from-json-to-user-defined-type/convert_from_json_to_user_defined_type.bal
================================================
import ballerina/io;
type Coord record {
float x;
float y;
};
type Book record {
xml book;
float price;
};
public function main() returns error? {
json j = {x: 1, y: 2};
// Argument is a `typedesc` value.
// The static return type depends on the argument.
// Even if `x` and `y` are `int` in `j` they will automatically convert to `float`
Coord c = check j.cloneWithType(Coord);
io:println(c.x);
// Argument defaulted from the context.
Coord d = check j.cloneWithType();
io:println(d.x);
Book book = {book: xml ` The Treasure Island `, price: 200.0};
json bookJson = book.toJson();
io:println(bookJson);
// `fromJsonWithType()` can be used to reverse conversions done by `toJson()`.
book = check bookJson.fromJsonWithType();
io:println(book);
// Below will result in an error, because the type of the field `book` in `bookJson` is `string`.
Book|error result = bookJson.cloneWithType();
io:println(result);
}
================================================
FILE: examples/convert-from-json-to-user-defined-type/convert_from_json_to_user_defined_type.md
================================================
# Convert from JSON to user-defined type
The `cloneWithType()` langlib function can be used to convert a value to a user-defined type. Result recursively uses specified type as inherent type of new value. Automatically performs numeric conversions as necessary.
Every part of the value is cloned including immutable structural values. Also `fromJsonWithType()` langlib function can be used for the same purpose and it also does the reverse of conversions done by toJson.
::: code convert_from_json_to_user_defined_type.bal :::
::: out convert_from_json_to_user_defined_type.out :::
## Related links
- [JSON type](/learn/by-example/json-type/)
- [Open records](/learn/by-example/open-records/)
- [Controlling openess](/learn/by-example/controlling-openness)
- [Check expression](/learn/by-example/check)
- [Casting JSON to user-defined type](/learn/by-example/casting-json-to-user-defined-type)
================================================
FILE: examples/convert-from-json-to-user-defined-type/convert_from_json_to_user_defined_type.metatags
================================================
description: This BBE demonstrates how to convert JSON to a user-defined type, do the numeric conversion on JSON elements, convert JSON to record, access JSON data, and manipulate JSON data in Ballerina.
keywords: ballerina, ballerina by example, bbe, json, record, cast, access, manipulate
================================================
FILE: examples/convert-from-json-to-user-defined-type/convert_from_json_to_user_defined_type.out
================================================
$ bal run convert_from_json_to_user_defined_type.bal
1.0
1.0
{"book":" The Treasure Island ","price":200.0}
{"book":` The Treasure Island `,"price":200.0}
error("{ballerina/lang.value}ConversionError",message="'map' value cannot be converted to 'Book':
field 'book' in record 'Book' should be of type 'xml<(lang.xml:Element|lang.xml:Comment|lang.xml:ProcessingInstruction|lang.xml:Text)>', found '" The Treasure...'")
================================================
FILE: examples/converting-from-table-and-xml-to-json/converting_from_table_and_xml_to_json.bal
================================================
import ballerina/io;
public function main() {
table users = table [{id: 1, name: "John"}, {id: 2, name: "Sam"}];
// `users` is converted to a JSON array.
json usersAsJson = users.toJson();
io:println(usersAsJson is json[]);
io:println(usersAsJson);
xml book = xml `The Lost World`;
// `book` is converted to a string.
json s = book.toJson();
io:println(s is string);
io:println(s);
}
================================================
FILE: examples/converting-from-table-and-xml-to-json/converting_from_table_and_xml_to_json.md
================================================
# Converting from `table` and `xml` to JSON
`toJson()` recursively converts `anydata` to `json`. Table values are converted to `json` arrays and `xml` values are converted to strings.
::: code converting_from_table_and_xml_to_json.bal :::
::: out converting_from_table_and_xml_to_json.out :::
## Related links
- [JSON type](/learn/by-example/json-type)
- [toJson](https://lib.ballerina.io/ballerina/lang.value/0.0.0#toJson)
================================================
FILE: examples/converting-from-table-and-xml-to-json/converting_from_table_and_xml_to_json.metatags
================================================
description: This BBE demonstrates how to convert table to JSON and convert XML to JSON in Ballerina.
keywords: ballerina, ballerina by example, bbe, conversion, json, table, toJson, xml
================================================
FILE: examples/converting-from-table-and-xml-to-json/converting_from_table_and_xml_to_json.out
================================================
$ bal run converting_from_table_and_xml_to_json.bal
true
[{"id":1,"name":"John"},{"id":2,"name":"Sam"}]
true
The Lost World
================================================
FILE: examples/converting-from-user-defined-type-to-json/converting_from_user_defined_type_to_json.bal
================================================
import ballerina/io;
// Closed type.
type ClosedCoord record {|
string name;
[float, float] cords;
|};
// Open type can have additional `anydata` fields.
type OpenCoord record {
string name;
[float, float] cords;
};
public function main() {
ClosedCoord a = {name: "Colombo", cords: [6.95, 79.84]};
// The conversion happens automatically because `a` is a subtype of `anydata`.
json j = a;
io:println(j);
OpenCoord b = {name: "Colombo", cords: [6.94, 79.83], "area": "03"};
// Use `toJson()` to convert `anydata` to `json`.
// Usually happens automatically with closed records.
json k = b.toJson();
io:println(k);
}
================================================
FILE: examples/converting-from-user-defined-type-to-json/converting_from_user_defined_type_to_json.md
================================================
# Convert from user-defined type to JSON
Conversion of a `json` value to JSON format is straightforward. Converting from an application-specific, user-defined subtype of `anydata` to `json` is also possible.
In many cases, the conversion happens automatically when the user-defined type is a subtype of JSON as well as of `anydata`. With tables, XML or records that are open to `anydata` use `toJson()` to convert `anydata` to `json`. APIs that generate JSON typically accept `anydata` and automatically apply `toJson()`.
::: code converting_from_user_defined_type_to_json.bal :::
::: out converting_from_user_defined_type_to_json.out :::
## Related links
- [JSON type](/learn/by-example/json-type/)
- [Open records](/learn/by-example/open-records/)
- [Controlling openess](/learn/by-example/controlling-openness)
================================================
FILE: examples/converting-from-user-defined-type-to-json/converting_from_user_defined_type_to_json.metatags
================================================
description: This BBE demonstrates how to convert user-defined type to JSON and convert JSON to record type in Ballerina.
keywords: ballerina, ballerina by example, bbe, conversion, json, record, toJson
================================================
FILE: examples/converting-from-user-defined-type-to-json/converting_from_user_defined_type_to_json.out
================================================
$ bal run converting_from_user_defined_type_to_json.bal
{"name":"Colombo","cords":[6.95,79.84]}
{"name":"Colombo","cords":[6.94,79.83],"area":"03"}
================================================
FILE: examples/counter-metrics/counter_metrics.bal
================================================
import ballerina/http;
import ballerina/io;
import ballerina/log;
import ballerina/observe;
import ballerinax/prometheus as _;
//Create a counter as a global variable in the service with the optional field description.
observe:Counter globalCounter = new ("total_orders",
desc = "Total quantity required");
service /onlineStoreService on new http:Listener(9090) {
resource function get makeOrder(http:Caller caller, http:Request req) {
//Incrementing the global counter defined with the default value 1.
globalCounter.increment();
//Create a counter with simply a name.
observe:Counter localCounter = new ("local_operations");
localCounter.increment();
//Increment the value of the counter by 20.
localCounter.increment(20);
//Create a counter with optional fields description, and tags.
observe:Counter registeredCounter = new ("total_product_order_quantity",
desc = "Total quantity required",
tags = {prodName: "HeadPhone", prodType: "Electronics"});
//Register the counter instance, therefore it is stored in the global registry and can be reported to the
//metrics server such as Prometheus. Additionally, this operation will register to the global registry for the
//first invocation and will throw an error if there is already a registration of different metrics instance
//or type. Subsequent invocations of register() will simply retrieve the stored metrics instance
//for the provided name and tags fields, and use that instance for the subsequent operations on the
//counter instance.
error? result = registeredCounter.register();
if (result is error) {
log:printError("Error in registering counter", 'error = result);
}
//Increase the amount of the registered counter instance by amount 10.
registeredCounter.increment(10);
//Get the value of the counter instances.
io:println("------------------------------------------");
io:println("Global Counter = ", globalCounter.getValue());
io:println("Local Counter = ", localCounter.getValue());
io:println("Registered Counter = ", registeredCounter.getValue());
io:println("------------------------------------------");
//Send reponse to the client.
http:Response res = new;
// Use a util method to set a string payload.
res.setPayload("Order Processed!");
// Send the response back to the caller.
result = caller->respond(res);
if (result is error) {
log:printError("Error sending response", 'error = result);
}
}
}
================================================
FILE: examples/counter-metrics/counter_metrics.client.out
================================================
$ curl http://localhost:9090/onlineStoreService/makeOrder
Order Processed!
================================================
FILE: examples/counter-metrics/counter_metrics.md
================================================
# Counter-based metrics
Ballerina supports Observability out of the box and Metrics is one of the three important aspects of Observability.To observe Ballerina code, the `--observability-included` build time flag should be given along with the `Config.toml` file when starting the service. The `Config.toml` file contains the required runtime configurations related to observability.
You can define and use metrics to measure your own logic. A counter is one type of the metrics that is supported by default in Ballerina, and it is a cumulative metric that represents a single monotonically-increasing counter whose value can only increase or be reset to zero.
For more information about configs and observing applications, see [Observe Ballerina programs](/learn/observe-ballerina-programs/).
::: code counter_metrics.bal :::
Invoke the service using the cURL command below.
::: out counter_metrics.client.out :::
To start the service, navigate to the directory that contains the
`.bal` file, and execute the `bal run` command below with the `--observability-included` build time flag and the `Config.toml` runtime configuration file.
::: out counter_metrics.server.out :::
================================================
FILE: examples/counter-metrics/counter_metrics.metatags
================================================
description: BBE on how to use the default Counter Metrics Observability feature in Ballerina.
keywords: ballerina, ballerina by example, bbe, observability, tracing, opentracing, counter
================================================
FILE: examples/counter-metrics/counter_metrics.server.out
================================================
$ BAL_CONFIG_FILES=Config.toml bal run --observability-included counter_metrics.bal
ballerina: started Prometheus HTTP listener 0.0.0.0:9797
------------------------------------------
Global Counter = 1
Local Counter = 21
Registered Counter = 10
------------------------------------------
------------------------------------------
Global Counter = 2
Local Counter = 21
Registered Counter = 20
------------------------------------------
------------------------------------------
Global Counter = 3
Local Counter = 21
Registered Counter = 30
------------------------------------------
================================================
FILE: examples/counter-metrics/tests/counter_metrics_test.bal
================================================
import ballerina/test;
import ballerina/io;
import ballerina/http;
@test:Config { }
function testFunc() returns error? {
// Invoking the main function
http:Client httpEndpoint = check new("http://localhost:9090");
string response1 = "Order Processed!";
// Send a GET request to the specified endpoint.
string response = check httpEndpoint->get("/onlineStoreService/makeOrder");
test:assertEquals(response, response1);
}
================================================
FILE: examples/covariance/covariance.bal
================================================
int[] iv = [1, 2, 3];
// Assigning `int[]` to `any[]` is allowed.
// The set of values allowed by `int` is a subset of set of values allowed by `any`
// The set of values allowed by `int[]` is a subset of set of values allowed by `any[]`
any[] av = iv;
public function main() {
// A runtime error or else `iv[0]` would have the wrong type.
av[0] = "str";
}
================================================
FILE: examples/covariance/covariance.md
================================================
# Covariance
Arrays and maps are covariant. Static type-checking guarantees that the result of a read from a mutable structure will be consistent with the static type.
Covariance means that a write to a mutable structure may result in a runtime error. Arrays, maps, and records have an `inherent` type that constrains mutation.
::: code covariance.bal :::
::: out covariance.out :::
================================================
FILE: examples/covariance/covariance.metatags
================================================
description: This BBE demonstrates covariance in Ballerina
keywords: ballerina, ballerina by example, bbe, covariance, arrays, maps
================================================
FILE: examples/covariance/covariance.out
================================================
$ bal run covariance.bal
error: {ballerina/lang.array}InherentTypeViolation {"message":"incompatible types: expected 'int', found 'string'"}
at covariance:main(covariance.bal:10)
================================================
FILE: examples/create-maps-with-query/create_maps_with_query.bal
================================================
import ballerina/io;
type Student record {|
readonly int id;
string firstName;
string lastName;
int score;
|};
public function main() returns error? {
table key(id) students = table [
{id: 1, firstName: "John", lastName: "Smith", score: 100},
{id: 2, firstName: "Jane", lastName: "Smith", score: 150},
{id: 4, firstName: "Fred", lastName: "Bloggs", score: 200},
{id: 7, firstName: "Bobby", lastName: "Clark", score: 300},
{id: 9, firstName: "Cassie", lastName: "Smith", score: 250}
];
// The type of the value in the `select` clause must belong to the
// `[string, T]` tuple type, where the type of the constructed value is `map`.
map studentScores = map from var student in students
select [student.firstName, student.score];
io:println(studentScores);
}
================================================
FILE: examples/create-maps-with-query/create_maps_with_query.md
================================================
# Create maps with query expressions
A query expressions can be used to create a map from an iterable value. A query expression should be preceded by the `map` keyword in this case.
The type of the value in the `select` clause must belong to the tuple type `[string, T]`, where the type of the constructed value is `map`.
::: code create_maps_with_query.bal :::
::: out create_maps_with_query.out :::
## Related links
- [Query expressions](/learn/by-example/query-expressions)
- [Sort iterable objects using query](/learn/by-example/sort-iterable-objects)
- [Let clause in query expression](/learn/by-example/let-clause)
- [Limit clause in query expression](/learn/by-example/limit-clause)
- [Joining iterable objects using query](/learn/by-example/joining-iterable-objects)
- [Querying tables](/learn/by-example/querying-tables)
- [Create tables with query expression](/learn/by-example/create-tables-with-query)
- [Create streams with query expression](/learn/by-example/create-streams-with-query)
- [On conflict clause in query expression](/learn/by-example/on-conflict-clause)
- [Nested query expressions](/learn/by-example/nested-query-expressions)
================================================
FILE: examples/create-maps-with-query/create_maps_with_query.metatags
================================================
description: This BBE demonstrates creating a map, filtering a map, creating a sub-map from a map, and creating a map from a table.
keywords: ballerina, ballerina by example, bbe, map, table, query, var
================================================
FILE: examples/create-maps-with-query/create_maps_with_query.out
================================================
$ bal run create_maps_with_query.bal
{"John":100,"Jane":150,"Fred":200,"Bobby":300,"Cassie":250}
================================================
FILE: examples/create-streams-with-query/create_streams_with_query.bal
================================================
import ballerina/io;
string[] students = ["John", "Mike", "Mia"];
public function main() {
int[] studentIds = [0, 1, 2];
// The `getStudentAPI()` is not invoked at the time of the stream creation.
var studentsStream = stream from var id in studentIds
select getStudentAPI(id);
// The `getStudentAPI()` is invoked when calling the `next()` method of the constructed stream.
var student = studentsStream.next();
// The `next()` will return an error if an evaluation of a query clause results in an error.
while (student !is error?) {
io:println(student.value);
student = studentsStream.next();
}
}
function getStudentAPI(int studentId) returns string {
io:println("request to get student name API");
return students[studentId];
}
================================================
FILE: examples/create-streams-with-query/create_streams_with_query.md
================================================
# Create stream with query
A query expression can be used to create streams. The query expression should be preceded by the `stream` keyword in this case. If the expected stream type is `stream`, then, the `select` clause must return values belonging to `T`.
When constructing a stream from a query expression, clauses in the query expression are executed lazily that are called as a result of the next operations being performed on the constructed stream. The failure of the `check` within the query will cause the stream to produce an error termination value.
::: code create_streams_with_query.bal :::
::: out create_streams_with_query.out :::
## Related links
- [Query expressions](/learn/by-example/query-expressions)
- [Sort iterable objects using query](/learn/by-example/sort-iterable-objects)
- [Let clause in query expression](/learn/by-example/let-clause)
- [Limit clause in query expression](/learn/by-example/limit-clause)
- [Joining iterable objects using query](/learn/by-example/joining-iterable-objects)
- [Querying tables](/learn/by-example/querying-tables)
- [Create maps with query expression](/learn/by-example/create-maps-with-query)
- [Create tables with query expression](/learn/by-example/create-tables-with-query)
- [On conflict clause in query expression](/learn/by-example/on-conflict-clause)
- [Nested query expressions](/learn/by-example/nested-query-expressions)
================================================
FILE: examples/create-streams-with-query/create_streams_with_query.metatags
================================================
description: This BBE demonstrates creating a stream of values, loading values lazily, saving resources by loading values when needed, the stream construct type, and handling the termination value of a stream.
keywords: ballerina, ballerina by example, bbe, stream, lazy, value, next, while
================================================
FILE: examples/create-streams-with-query/create_streams_with_query.out
================================================
$ bal run create_streams_with_query.bal
request to get student name API
John
request to get student name API
Mike
request to get student name API
Mia
================================================
FILE: examples/create-tables-with-query/create_tables_with_query.bal
================================================
import ballerina/io;
type Employee record {|
readonly int id;
string firstName;
string lastName;
int salary;
|};
public function main() {
table key(id) employees = table [
{id: 1, firstName: "John", lastName: "Smith", salary: 100},
{id: 2, firstName: "Fred", lastName: "Bloggs", salary: 2000}
];
// The `key(id)` key specifier specifies the key sequence of the constructed table.
// `select` clause must emit values belonging to `map`.
var highPaidEmployees = table key(id) from var e in employees
where e.salary >= 1000
select e;
io:println(highPaidEmployees);
// A table can also be constructed by providing the contextually expected type.
// `table` is the contextually expected type.
// `select` clause must emit values belonging to `Employee`.
table highPaidEmployees2 = from var e in employees
where e.salary >= 1000
select e;
io:println(highPaidEmployees2);
}
================================================
FILE: examples/create-tables-with-query/create_tables_with_query.md
================================================
# Create tables with a query
A query expressions can be used to create tables from an iterable value. One way of doing this is specifying `table` keyword as the query construct type before the query expression. The key of the created table can be specified explicitly. Second way is to provide a contextually expected type. The `select` clause must emit values belonging to `map`.
::: code create_tables_with_query.bal :::
::: out create_tables_with_query.out :::
## Related links
- [Query expressions](/learn/by-example/query-expressions)
- [Sort iterable objects using query](/learn/by-example/sort-iterable-objects)
- [Let clause in query expression](/learn/by-example/let-clause)
- [Limit clause in query expression](/learn/by-example/limit-clause)
- [Joining iterable objects using query](/learn/by-example/joining-iterable-objects)
- [Querying tables](/learn/by-example/querying-tables)
- [Create maps with query expression](/learn/by-example/create-maps-with-query)
- [Create streams with query expression](/learn/by-example/create-streams-with-query)
- [On conflict clause in query expression](/learn/by-example/on-conflict-clause)
- [Nested query expressions](/learn/by-example/nested-query-expressions)
================================================
FILE: examples/create-tables-with-query/create_tables_with_query.metatags
================================================
description: This BBE demonstrates creating tables with a query in Ballerina, creating a table, filtering a table, and creating a sub table from a table.
keywords: ballerina, ballerina by example, bbe, creating table, query expression, query, where, key, var
================================================
FILE: examples/create-tables-with-query/create_tables_with_query.out
================================================
$ bal run create_tables_with_query.bal
[{"id":2,"firstName":"Fred","lastName":"Bloggs","salary":2000}]
[{"id":2,"firstName":"Fred","lastName":"Bloggs","salary":2000}]
================================================
FILE: examples/csv-streams-to-record-array/csv_streams_to_record_array.bal
================================================
import ballerina/data.csv;
import ballerina/io;
type Book record {
string name;
string author;
decimal price;
string publishedDate;
};
type BookMinimal record {|
string name;
string author;
|};
const csvFilePath = "./files/csv_file.csv";
public function main() returns error? {
// Write a CSV file.
check io:fileWriteCsv(csvFilePath, [["name", "author", "price", "publishedDate"],
["Effective Java", "Joshua Bloch", "45.99", "2018-01-01"],
["Clean Code", "Robert C. Martin", "37.5", "2008-08-01"]]);
// Read the CSV file as a byte stream.
stream bookStream = check io:fileReadBlocksAsStream(csvFilePath);
// Parse as CSV byte stream to an array of records.
Book[] bookArray = check csv:parseStream(bookStream);
io:println(bookArray);
bookStream = check io:fileReadBlocksAsStream(csvFilePath);
// Parse the CSV byte stream as an array of records with data projection.
// Here only the fields specified in the target record type (`name` and `author` fields)
// are included in the constructed value.
BookMinimal[] briefBookArray = check csv:parseStream(bookStream);
io:println(briefBookArray);
}
================================================
FILE: examples/csv-streams-to-record-array/csv_streams_to_record_array.md
================================================
# Parse CSV byte stream to Ballerina record array
The Ballerina `data.csv` library allows parsing the CSV byte streams as arrays of Ballerina records, making it easier to process CSV data directly from `streams`. This functionality supports data projection, enabling developers to extract and map only the required fields to the target `record` type.
::: code csv_streams_to_record_array.bal :::
::: out csv_streams_to_record_array.out :::
================================================
FILE: examples/csv-streams-to-record-array/csv_streams_to_record_array.metatags
================================================
description: This BBE demonstrates the parsing of CSV byte streams into Ballerina record arrays, both with and without data projection.
keywords: ballerina, ballerina by example, bbe, csv, csv byte streams, csv streams, record, record array, parseStream, csv data module, data.csv, data projection, csv to stream
================================================
FILE: examples/csv-streams-to-record-array/csv_streams_to_record_array.out
================================================
$ bal run csv_streams_to_record_array.bal
[{"name":"Effective Java","author":"Joshua Bloch","price":45.99,"publishedDate":"2018-01-01"},{"name":"Clean Code","author":"Robert C. Martin","price":37.5,"publishedDate":"2008-08-01"}]
[{"name":"Effective Java","author":"Joshua Bloch"},{"name":"Clean Code","author":"Robert C. Martin"}]
================================================
FILE: examples/csv-string-to-anydata-array/csv_string_to_anydata_array.bal
================================================
import ballerina/data.csv;
import ballerina/io;
public function main() returns error? {
string csvString = string `name,author,price,publishedDate
Effective Java,Joshua Bloch,45.99,2018-01-01
Clean Code,Robert C. Martin,37.50,2008-08-01`;
// Parse the CSV string as an array of `string` arrays.
string[][] bookArray = check csv:parseString(csvString);
io:println(bookArray);
// Parse the CSV string to an array of `anydata` arrays.
anydata[][] bookArray2 = check csv:parseString(csvString);
io:println(bookArray2);
// Parse the CSV string to an array of `string` arrays with data projection.
// Here only the first two headers are extracted to the resulting array,
// based on the array member size specified in the expected type.
string[][2] bookArray3 = check csv:parseString(csvString);
io:println(bookArray3);
}
================================================
FILE: examples/csv-string-to-anydata-array/csv_string_to_anydata_array.md
================================================
# Parse CSV string to arrays
The Ballerina `data.csv` library offers a number of functions for parsing CSV strings to subtypes of `anydata[][]`, enabling developers to effectively parse CSV string data into flexible data structures. Additionally, it provides the option to select specific fields for inclusion in the resulting arrays of `anydata` arrays, allowing developers to extract only the required data.
::: code csv_string_to_anydata_array.bal :::
::: out csv_string_to_anydata_array.out :::
================================================
FILE: examples/csv-string-to-anydata-array/csv_string_to_anydata_array.metatags
================================================
description: This BBE demonstrates the parsing of a CSV string into an array of anydata arrays, both with and without data projection.
keywords: ballerina, ballerina by example, bbe, csv, csv to array, csv string, anydata, anydata array, parseString, csv data module, data.csv, data projection
================================================
FILE: examples/csv-string-to-anydata-array/csv_string_to_anydata_array.out
================================================
$ bal run csv_string_to_anydata_array.bal
[["Effective Java","Joshua Bloch","45.99","2018-01-01"],["Clean Code","Robert C. Martin","37.50","2008-08-01"]]
[["Effective Java","Joshua Bloch",45.99,"2018-01-01"],["Clean Code","Robert C. Martin",37.5,"2008-08-01"]]
[["Effective Java","Joshua Bloch"],["Clean Code","Robert C. Martin"]]
================================================
FILE: examples/csv-string-to-record-array/csv_string_to_record_array.bal
================================================
import ballerina/data.csv;
import ballerina/io;
type Book record {
string name;
string author;
decimal price;
string publishedDate;
};
type BookMinimal record {|
string name;
string author;
|};
public function main() {
string csvString = string `name,author,price,publishedDate
Effective Java,Joshua Bloch,45.99,2018-01-01
Clean Code,Robert C. Martin,37.50,2008-08-01`;
// Parse CSV string to a array of records.
Book[]|csv:Error bookRecords = csv:parseString(csvString);
io:println(bookRecords);
// Parse CSV string to a array of records with data projection.
// Here only the fields specified in the target record type (`name` and `author` fields)
// are included in the constructed value.
BookMinimal[]|csv:Error briefBookRecords = csv:parseString(csvString);
io:println(briefBookRecords);
}
================================================
FILE: examples/csv-string-to-record-array/csv_string_to_record_array.md
================================================
# Parse CSV string to array of records
The ballerina `data.csv` library offers a range of functions for parsing CSV strings to array of records, enabling developers to parse CSV string data to structured records while allowing for the selection of specific fields within the expected record array.
::: code csv_string_to_record_array.bal :::
::: out csv_string_to_record_array.out :::
================================================
FILE: examples/csv-string-to-record-array/csv_string_to_record_array.metatags
================================================
description: This BBE demonstrates conversion of CSV string to array of records with and without data projection.
keywords: ballerina, ballerina by example, bbe, csv, csv string, record, record array, parseString, csv data module, data.csv, data projection
================================================
FILE: examples/csv-string-to-record-array/csv_string_to_record_array.out
================================================
$ bal run csv_string_to_record_array.bal
[{"name":"Effective Java","author":"Joshua Bloch","price":45.99,"publishedDate":"2018-01-01"},{"name":"Clean Code","author":"Robert C. Martin","price":37.5,"publishedDate":"2008-08-01"}]
[{"name":"Effective Java","author":"Joshua Bloch"},{"name":"Clean Code","author":"Robert C. Martin"}]
================================================
FILE: examples/csv-user-configurations/csv_user_configurations.bal
================================================
import ballerina/data.csv;
import ballerina/io;
// Defines the structure of an `Employee` record.
type Employee record {|
int id;
string name;
string department;
|};
// Defines the structure of an `EmployeeDepartment` record.
type EmployeeDepartment record {|
int id;
string department;
|};
public function main() returns error? {
// This CSV string contains `|` separated values and a comment.
// The `"` character in `O\"Connor` value is escaped using the `\` character.
string csvString = string `id|name|department # This is a comment
1|O\"Connor|Engineering
2|Doe|HR
3|Jane|Finance
4|John|Engineering`;
// Defines the options for parsing the CSV string.
// The `|` character is used as the delimiter for separating fields.
// The `\` character is used to escape characters (e.g., quotes) within the data.
// The `#` character indicates the start of a comment, lines starting with `#` will be ignored.
// The second and fourth data rows will be skipped during parsing.
csv:ParseOptions parseOptions = {
delimiter: "|",
escapeChar: "\\",
comment: "#",
skipLines: [2, 4]
};
// Parse the CSV string into an array of `Employee` records with specified options.
Employee[] employees = check csv:parseString(csvString, parseOptions);
io:println(employees);
Employee[] employeeRecords = [
{id: 1, name: "John", department: "Engineering"},
{id: 2, name: "Doe", department: "HR"},
{id: 3, name: "Jane", department: "Finance"},
{id: 4, name: "John", department: "Engineering"}
];
// Defines the options for transforming the `Employee` records.
// The second and fourth data rows will be skipped during transformation.
csv:TransformOptions transformOptions = {
skipLines: [2, 4]
};
// Transform the `employee` records into `EmployeeDepartment` records with specified options.
EmployeeDepartment[] employeeDepartments = check csv:transform(employeeRecords, transformOptions);
io:println(employeeDepartments);
}
================================================
FILE: examples/csv-user-configurations/csv_user_configurations.md
================================================
# Handle CSV with custom configurations
Ballerina provides flexible CSV data handling through custom configurations, enabling developers to define delimiters, escape characters, and skip specific lines. This approach allows efficient parsing and transformation of CSV files, streamlining the integration process.
::: code csv_user_configurations.bal :::
::: out csv_user_configurations.out :::
================================================
FILE: examples/csv-user-configurations/csv_user_configurations.metatags
================================================
description: This BBE demonstrates how to process CSV data using custom configurations.
keywords: ballerina, ballerina by example, bbe, csv, csv records, record, record array, transform, parseString, csv data module, data.csv, data projection, csv configs, csv configurations
================================================
FILE: examples/csv-user-configurations/csv_user_configurations.out
================================================
$ bal run csv_user_configurations.bal
[{"id":1,"name":"O"Connor","department":"Engineering"},{"id":3,"name":"Jane","department":"Finance"}]
[{"id":1,"department":"Engineering"},{"id":3,"department":"Finance"}]
================================================
FILE: examples/custom-logger/custom_logger.bal
================================================
import ballerina/log;
final map & readonly logLevelWeight = {
ERROR: 1000,
WARN: 900,
INFO: 800,
DEBUG: 700
};
// Custom logger implementation that implements the Logger type
public isolated class ApplicationLogger {
*log:Logger;
private final string applicationName;
private final string version;
private final readonly & log:KeyValues context;
private log:Level currentLevel;
private final ApplicationLogger? parent;
public isolated function init(string applicationName, string version, log:KeyValues context = {},
ApplicationLogger? parent = ()) {
self.applicationName = applicationName;
self.version = version;
self.parent = parent;
self.currentLevel = log:INFO;
self.context = context.cloneReadOnly();
}
public isolated function printInfo(string|log:PrintableRawTemplate msg, error? 'error = (),
error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) {
if logLevelWeight[self.getLevel()] > logLevelWeight[log:INFO] {
return;
}
string evaluatedMsg = msg is string ? msg : log:evaluateTemplate(msg);
string printableMsg = string `[Application: ${self.applicationName} v${self.version}] ${evaluatedMsg}`;
log:KeyValues newKeyValues = {...self.context};
foreach [string, log:Value] [k, v] in keyValues.entries() {
newKeyValues[k] = v;
}
log:printInfo(printableMsg, 'error, stackTrace, newKeyValues);
}
public isolated function printWarn(string|log:PrintableRawTemplate msg, error? 'error = (),
error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) {
// Add similar implementation or a custom logic for warnings
}
public isolated function printError(string|log:PrintableRawTemplate msg, error? 'error = (),
error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) {
// Add similar implementation or a custom logic for warnings
}
public isolated function printDebug(string|log:PrintableRawTemplate msg, error? 'error = (),
error:StackFrame[]? stackTrace = (), *log:KeyValues keyValues) {
// Add similar implementation or a custom logic for warnings
}
public isolated function withContext(*log:KeyValues keyValues) returns log:Logger|error {
log:KeyValues newContext = {...self.context};
foreach [string, log:Value] [k, v] in keyValues.entries() {
newContext[k] = v;
}
return new ApplicationLogger(self.applicationName, self.version, newContext, self);
}
public isolated function getLevel() returns log:Level {
ApplicationLogger? p = self.parent;
if p is ApplicationLogger {
return p.getLevel();
}
lock {
return self.currentLevel;
}
}
public isolated function setLevel(log:Level level) returns error? {
if self.parent is ApplicationLogger {
return error("unsupported operation: cannot set level on a child logger");
}
lock {
self.currentLevel = level;
}
}
}
public function main() returns error? {
// Use custom application logger
ApplicationLogger appLogger = new ("OrderService", "2.1.0");
appLogger.printInfo("Application logger initialized", feature = "order_processing");
// Create contextual logger from custom logger
log:Logger orderLogger = check appLogger.withContext(orderId = "ORDER-12345",
customerId = "CUST-67890");
orderLogger.printInfo("Processing order");
orderLogger.printDebug("Validating order details");
appLogger.printInfo("Application processing completed");
}
================================================
FILE: examples/custom-logger/custom_logger.md
================================================
# Custom logger
This example demonstrates how to implement custom loggers that conform to the `log:Logger` type. Custom loggers can automatically include specific context and provide specialized logging behavior for different components.
::: code custom_logger.bal :::
::: out custom_logger.out :::
> **Note:** In this example, the log module print methods are used to simulate the logging behaviour. So the root logger configurations will be applied to the log messages. But in a real world scenario, you would implement the actual logging logic inside the custom logger methods with custom log formats and destinations.
The custom logger implementation allows for flexible and reusable logging strategies tailored to specific application needs.
## Related links
- [`log` module - Specification](https://ballerina.io/spec/log/#41-logger)
- [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest)
================================================
FILE: examples/custom-logger/custom_logger.metatags
================================================
description: BBE on how to implement custom logger that conform to the Logger type in Ballerina.
keywords: ballerina, ballerina by examples, bbe, log, custom logger, logger type
================================================
FILE: examples/custom-logger/custom_logger.out
================================================
$ bal run custom_logger.bal
time=2025-08-26T09:04:14.761+05:30 level=INFO module="" message="[Application: OrderService v2.1.0] Application logger initialized" feature="order_processing"
time=2025-08-26T09:04:14.776+05:30 level=INFO module="" message="[Application: OrderService v2.1.0] Processing order" orderId="ORDER-12345" customerId="CUST-67890"
time=2025-08-26T09:04:14.778+05:30 level=INFO module="" message="[Application: OrderService v2.1.0] Application processing completed"
================================================
FILE: examples/custom-prefetch-methods/custom_prefetch_methods.bal
================================================
import ballerina/graphql;
import ballerina/graphql.dataloader;
import ballerina/http;
public type Book record {|
int id;
string title;
|};
type BookRow record {|
readonly int id;
string title;
int author;
|};
type AuthorRow record {|
readonly int id;
string name;
|};
final readonly & table key(id) authorTable = table [
{id: 1, name: "J.K. Rowling"},
{id: 2, name: "Stephen King"}
];
final readonly & table key(id) bookTable = table [
{id: 1, title: "Harry Potter and the Sorcerer's Stone", author: 1},
{id: 2, title: "The Shining", author: 2},
{id: 3, title: "Harry Potter and the Chamber of Secrets", author: 1},
{id: 4, title: "It", author: 2},
{id: 5, title: "Harry Potter and the Prisoner of Azkaban", author: 1},
{id: 6, title: "The Stand", author: 2}
];
isolated function bookLoaderFunction(readonly & anydata[] ids) returns BookRow[][]|error {
final readonly & int[] keys = check ids.ensureType();
return keys.'map(isolated function(readonly & int key) returns BookRow[] {
return bookTable.'filter(book => book.author == key).toArray();
});
}
@graphql:ServiceConfig {
contextInit
}
service /graphql on new graphql:Listener(9090) {
resource function get authors() returns Author[] {
return from AuthorRow authorRow in authorTable
select new (authorRow);
}
}
public isolated distinct service class Author {
private final readonly & AuthorRow author;
isolated function init(readonly & AuthorRow author) {
self.author = author;
}
isolated resource function get name() returns string {
return self.author.name;
}
// Define a prefetch function with a custom name.
isolated function prefetchBooks(graphql:Context ctx) {
dataloader:DataLoader bookLoader = ctx.getDataLoader("bookLoader");
bookLoader.add(self.author.id);
}
// Annotate the resource function to use a prefetch function having a custom name.
// This configuration instructs the GraphQL engine to call the `prefetchBooks` function
// before calling the `books` resource function.
@graphql:ResourceConfig {
prefetchMethodName: "prefetchBooks"
}
isolated resource function get books(graphql:Context ctx) returns Book[]|error {
dataloader:DataLoader bookLoader = ctx.getDataLoader("bookLoader");
BookRow[] bookrows = check bookLoader.get(self.author.id);
return from BookRow bookRow in bookrows
select {id: bookRow.id, title: bookRow.title};
}
}
isolated function contextInit(http:RequestContext requestContext, http:Request request) returns graphql:Context {
graphql:Context ctx = new;
ctx.registerDataLoader("bookLoader", new dataloader:DefaultDataLoader(bookLoaderFunction));
return ctx;
}
================================================
FILE: examples/custom-prefetch-methods/custom_prefetch_methods.client.out
================================================
$ curl 'http://localhost:9090/graphql' -H 'Content-Type: application/json' -d '{"query": "{ authors { name books { title } } }"}'
{"data":{"authors":[{"name":"J.K. Rowling", "books":[{"title":"Harry Potter and the Sorcerer's Stone"}, {"title":"Harry Potter and the Chamber of Secrets"}, {"title":"Harry Potter and the Prisoner of Azkaban"}]}, {"name":"Stephen King", "books":[{"title":"The Shining"}, {"title":"It"}, {"title":"The Stand"}]}]}}
================================================
FILE: examples/custom-prefetch-methods/custom_prefetch_methods.graphql
================================================
{
authors {
name
books {
title
}
}
}
================================================
FILE: examples/custom-prefetch-methods/custom_prefetch_methods.md
================================================
# GraphQL service - Custom prefetch methods
A prefetch method in the context of Ballerina GraphQL is a method that is invoked before the actual resolver method. By default the prefetch method name has the convention of having the prefix `pre` followed by the resolver method name.
If you want to use a custom prefetch method, the `prefetchMethodName` field of the `graphql:ResourceConfig` annotation can be used to override the default prefetch method name. This configuration is useful when the Query and Mutation operations have fields with the same name, and when it is required to use different prefetch methods for each field. To override the default prefetch method name, annotate the target field with the `graphql:ResourceConfig` annotation and specify the prefetch method name as the value of the `prefetchMethodName` field.
::: code custom_prefetch_methods.bal :::
Run the service by executing the following command.
::: out custom_prefetch_methods.server.out :::
Send the following document to the GraphQL endpoint to test the service.
::: code custom_prefetch_methods.graphql :::
To send the document, execute the following cURL command in a separate terminal.
::: out custom_prefetch_methods.client.out :::
## Related links
- [`graphql:ResourceConfig` annotation - API documentation](https://lib.ballerina.io/ballerina/graphql/latest#ResourceConfig)
- [Prefetch Method Name Configuration - Specification](/spec/graphql/#722-prefetch-method-name-configuration)
- [The `prefetch` Method - Specification](/spec/graphql/#10633-define-the-corresponding-prefetch-method)
================================================
FILE: examples/custom-prefetch-methods/custom_prefetch_methods.metatags
================================================
description: BBE on overriding a default prefetch method name.
keywords: ballerina, ballerina by example, bbe, graphql, dataloader, prefetch method
================================================
FILE: examples/custom-prefetch-methods/custom_prefetch_methods.server.out
================================================
$ bal run graphql_prefetch_config.bal
================================================
FILE: examples/decimal-type/decimal_type.bal
================================================
import ballerina/io;
// The `decimal` type represents the set of 128-bits IEEE 754R decimal floating point numbers.
decimal nanos = 1d/1000000000d;
function floatSurprise() {
float f = 100.10 - 0.01;
io:println(f);
}
public function main() {
floatSurprise();
io:println(nanos);
// Numeric literals can use `d` or `D` suffix for them to be interpreted as `decimal` values.
// (Similarly, the `f` or `F` suffix can be used for `float`).
var d = 12345d;
io:println(d is decimal);
}
================================================
FILE: examples/decimal-type/decimal_type.md
================================================
# Decimal type
The `decimal` type is the third numeric type. It works like `int` and `float` types. It is a separate basic type and belongs to the `anydata` type.
There is no implicit conversions between `decimal` and other numeric types. It can represent `decimal` fractions exactly and it preserves precision. The `decimal` type does not include infinity, NaN, or negative zero. It supports floating-point precision to 34 decimal digits.
::: code decimal_type.bal :::
::: out decimal_type.out :::
================================================
FILE: examples/decimal-type/decimal_type.metatags
================================================
description: This BBE demonstrates decimal type in Ballerina.
keywords: ballerina, ballerina by example, bbe, decimal type, decimal
================================================
FILE: examples/decimal-type/decimal_type.out
================================================
$ bal run decimal_type.bal
100.08999999999999
1E-9
true
================================================
FILE: examples/default-values-for-function-parameters/default_values_for_function_parameters.bal
================================================
import ballerina/io;
// Use the value of the preceding `str` parameter to initialize the `end` parameter.
function subString(string str, int 'start = 0, int end = str.length()) returns string {
return str.substring('start, end);
}
public function main() {
// Call the `subString` function using the default values of `start` and `end` parameters.
io:println(subString("Ballerina Language"));
// Call the `subString` function by passing a value for the `start` default parameter.
io:println(subString("Ballerina Language", 10));
// Call the `subString` function by passing values for `start` and `end` default parameters.
io:println(subString("Ballerina Language", 10, 14));
}
================================================
FILE: examples/default-values-for-function-parameters/default_values_for_function_parameters.md
================================================
# Default values for function parameters
Ballerina allows specifying default values for function parameters. You can use any expression such as a literal or a function call as the default value of a parameter. Additionally, the default value expressions can use the values of the preceding parameters.
::: code default_values_for_function_parameters.bal :::
::: out default_values_for_function_parameters.out :::
## Related links
- [Rest Parameters](/learn/by-example/rest-parameters/)
- [Provide function arguments by name](/learn/by-example/provide-function-arguments-by-name/)
- [Rest arguments](/learn/by-example/rest-arguments/)
================================================
FILE: examples/default-values-for-function-parameters/default_values_for_function_parameters.metatags
================================================
description: This BBE demonstrates defining default parameter values in a function using the preceding parameter value as the default parameter value in Ballerina.
keywords: ballerina, ballerina by example, bbe, default parameter, default value, functions
================================================
FILE: examples/default-values-for-function-parameters/default_values_for_function_parameters.out
================================================
$ bal default_values_for_function_parameters.bal
Ballerina Language
Language
Lang
================================================
FILE: examples/default-values-for-record-fields/default_values_for_record_fields.bal
================================================
import ballerina/io;
type Student record {|
string name;
int age;
// Specifies a default value for `code`.
string code = "Admitted";
|};
type PartTimeStudent record {|
*Student;
int studyHours;
|};
public function main() returns error? {
// Creates a `Student` record without explicitly specifying a value for the `code` field.
Student s1 = {name: "Anne", age: 23};
io:println(s1);
json j = {name: "Anne", age: 23};
// Calling the `value:cloneWithType()` function with `Student` will make use of the default values
// in `Student`.
Student s2 = check j.cloneWithType(Student);
io:println(s2);
// `*Student` copies the default values.
PartTimeStudent s3 = {name: "Anne", age: 23, studyHours: 6};
io:println(s3);
}
================================================
FILE: examples/default-values-for-record-fields/default_values_for_record_fields.md
================================================
# Default values for record fields
Ballerina allows default values for record fields as part of the record's type descriptor. A default value is an expression. Default values do not affect static typing. They only affect the use of type descriptors to construct records. Calling the `value:cloneWithType()` function with a record type descriptor `T` will make use of the default values in `T` if required. Similarly, using `*T` in type inclusions also copies the default values.
::: code default_values_for_record_fields.bal :::
::: out default_values_for_record_fields.out :::
## Related links
- [Records](/learn/by-example/records/)
- [Type inclusion for records](/learn/by-example/type-inclusion-for-records/)
- [Maps](/learn/by-example/maps/)
================================================
FILE: examples/default-values-for-record-fields/default_values_for_record_fields.metatags
================================================
description: This BBE demonstrates the default values for record fields, adding default values to record fields, and default values in inclusions.
keywords: ballerina, ballerina by example, bbe, record, record type, record field, inclusion, record inclusion, default values
================================================
FILE: examples/default-values-for-record-fields/default_values_for_record_fields.out
================================================
$ bal run default_values_for_record_fields.bal
{"name":"Anne","age":23,"code":"Admitted"}
{"name":"Anne","age":23,"code":"Admitted"}
{"studyHours":6,"name":"Anne","age":23,"code":"Admitted"}
================================================
FILE: examples/defining-classes/defining_classes.bal
================================================
import ballerina/io;
class Engineer {
// A `final` field must be assigned exactly once.
final string name;
int age;
// The `init` method initializes the object.
function init(string name, int age) {
// The `init` method can initialize the `final` field.
self.name = name;
self.age = age;
}
function getName() returns string {
// Methods use `self` to access their fields.
return self.name;
}
function getAge() returns int {
return self.age;
}
}
public function main() {
// Arguments to `new` are passed as arguments to `init`.
Engineer engineer = new Engineer("Alice", 52);
io:println(engineer.getName());
io:println(engineer.getAge());
}
================================================
FILE: examples/defining-classes/defining_classes.md
================================================
# Defining classes
Classes provide a template for bundling data and functionality together. In Ballerina, classes are defined at the module level. The `init` method is used to initialize an object and any arguments passed to the `new` expression are passed as arguments to the `init` method. The `init` method can also be used to initialize the `final` fields.
The `self` variable is bound to the object and can be used to access the fields and methods of the object.
::: code defining_classes.bal :::
::: out defining_classes.out :::
## Related links
- [Object](/learn/by-example/object/)
- [Init return type](/learn/by-example/init-return-type/)
- [Object value from class definition](/learn/by-example/object-value-from-class-definition/)
================================================
FILE: examples/defining-classes/defining_classes.metatags
================================================
description: This BBE demonstrates defining classses, invoking object methods, and initializing object fields in Ballerina.
keywords: ballerina, ballerina by example, bbe, class, object, init method, new
================================================
FILE: examples/defining-classes/defining_classes.out
================================================
$ bal run defining_classes.bal
Alice
52
================================================
FILE: examples/dependent-types/dependent_types.bal
================================================
import ballerina/io;
public function main() returns error? {
string|int s1 = "Ballerina";
// `lang.value:ensureType()` is a is a dependently-typed function.
// The return type of `ensureType` function is dependent on the
// value of the parameter, which is `string` type descriptor here.
string s2 = check s1.ensureType(string);
io:println(s2);
json j = 12.5d;
// `ensureType()` function is called on `j`, and it returns a `float` value which is
// assigned to `f`, which is of the `float` type. This happens because the function parameter
// `t` of `ensureType` has a default value of `<>` which causes the compiler to infer the
// argument for `t` from the function call context.
float f = check j.ensureType();
io:println(f);
}
================================================
FILE: examples/dependent-types/dependent_types.md
================================================
# Dependent types
Ballerina supports a concept called dependent types that makes the return type of a function dependent on the value of a parameter. Unlike the normal convention where the return type is defined independently, here the return type depends on a parameter which is a type descriptor. When the parameter has a default value of `<>` the compiler infers the argument for the parameter from the function call context.
::: code dependent_types.bal :::
::: out dependent_types.out :::
================================================
FILE: examples/dependent-types/dependent_types.metatags
================================================
description: This BBE demonstrates dependent types in Ballerina.
keywords: ballerina, ballerina by example, bbe, dependent type
================================================
FILE: examples/dependent-types/dependent_types.out
================================================
$ bal run dependent_types.bal
Ballerina
12.5
================================================
FILE: examples/destructure-records-using-query/destructure_records_using_query.bal
================================================
import ballerina/io;
type Person record {|
string first;
string last;
anydata...;
|};
public function main() {
Person[] persons = [
{first: "Melina", last: "Kodel", "yearOfBirth": 1994},
{first: "Tom", last: "Riddle", "yearOfBirth": 1926}
];
// A `Person` record is destructured here as a projection with the `first` and `last` fields.
// `{first, last}` is the mapping binding pattern. It is the same as `{first:first, last:last}`.
var names = from var {first, last} in persons
select {first, last};
io:println(names);
// Specify the type explicitly before the binding pattern instead of `var`.
names = from Person {first, last} in persons
select {first, last};
io:println(names);
// This maps all the remaining fields of the `Person` record other than the field `first`
// using the rest binding pattern.
var lastNameAndDOB = from var {first: _, ...restDetail} in persons
select restDetail;
io:println(lastNameAndDOB);
}
================================================
FILE: examples/destructure-records-using-query/destructure_records_using_query.md
================================================
# Destructure records using queries
Destructuring records is particularly useful with the query expressions. But works anywhere you can have `var`. `var` is followed by a binding pattern. You can also specify the type explicitly before the binding pattern without using `var`.
`{x}` is short for `{x: x}` in both binding patterns and record constructors.
::: code destructure_records_using_query.bal :::
::: out destructure_records_using_query.out :::
## Related links
- [Query expressions](/learn/by-example/query-expressions)
- [Sort iterable objects using query](/learn/by-example/sort-iterable-objects)
- [Let clause in query expression](/learn/by-example/let-clause)
- [Limit clause in query expression](/learn/by-example/limit-clause)
- [Joining iterable objects using query](/learn/by-example/joining-iterable-objects)
- [Querying tables](/learn/by-example/querying-tables)
- [Create maps with query expression](/learn/by-example/create-maps-with-query)
- [Create tables with query expression](/learn/by-example/create-tables-with-query)
- [Create streams with query expression](/learn/by-example/create-streams-with-query)
- [On conflict clause in query expression](/learn/by-example/on-conflict-clause)
- [Nested query expressions](/learn/by-example/nested-query-expressions)
================================================
FILE: examples/destructure-records-using-query/destructure_records_using_query.metatags
================================================
description: This BBE demonstrates using binding patterns in a query expression, record destructuring in a query expression and accessing values in a query iteration.
keywords: ballerina, ballerina by example, bbe, query, var, binding pattern, from, access
================================================
FILE: examples/destructure-records-using-query/destructure_records_using_query.out
================================================
$ bal run destructuring_records_with_query.bal
[{"first":"Melina","last":"Kodel"},{"first":"Tom","last":"Riddle"}]
[{"first":"Melina","last":"Kodel"},{"first":"Tom","last":"Riddle"}]
[{"last":"Kodel","yearOfBirth":1994},{"last":"Riddle","yearOfBirth":1926}]
================================================
FILE: examples/direct-llm-calls/direct_llm_calls.bal
================================================
import ballerina/ai;
import ballerina/io;
// Use the default model provider (with configuration added via a Ballerina VS Code command).
final ai:ModelProvider model = check ai:getDefaultModelProvider();
type JokeResponse record {|
string setup;
string punchline;
|};
public function main(string subject) returns error? {
// Use an insertion to insert the subject into the prompt.
// The response is expected to be a string.
string joke = check model->generate(`Tell me a joke about ${subject}!`);
io:println(joke);
// An LLM call with a structured response type.
JokeResponse jokeResponse = check model->generate(`Tell me a joke about ${subject}!`);
io:println("Setup: ", jokeResponse.setup);
io:println("Punchline: ", jokeResponse.punchline);
}
================================================
FILE: examples/direct-llm-calls/direct_llm_calls.md
================================================
# Direct large language model (LLM) calls
The `ai:ModelProvider` type is a unified abstraction to integrate with large language models (LLMs) through provider-specific modules such as [ballerinax/ai.openai](https://central.ballerina.io/ballerinax/ai.openai/latest), [ballerinax/ai.anthropic](https://central.ballerina.io/ballerinax/ai.anthropic/latest), etc.
The `generate` method of the model provider accepts a prompt in natural language, generates a JSON schema corresponding to the type descriptor argument (generally, the expected type), makes the call to the LLM, and binds the relevant content from the response to the expected type, allowing seamless integration with LLMs.
This example demonstrates how to make direct calls to LLMs using the model provider, enabling type-safe AI integrations across different providers.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code direct_llm_calls.bal :::
::: out direct_llm_calls.out :::
## Related links
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/direct-llm-calls/direct_llm_calls.metatags
================================================
description: This BBE demonstrates how to do direct LLM calls.
keywords: ballerina, ballerina by example, BBE, ai, llm, model-provider
================================================
FILE: examples/direct-llm-calls/direct_llm_calls.out
================================================
$ bal run direct_llm_calls.bal -- subject=programming
Why do programmers prefer dark mode?
Because light attracts bugs!
Setup: Why do programmers prefer dark mode?
Punchline: Because light attracts bugs!
================================================
FILE: examples/direct-llm-calls-with-history/direct_llm_calls_with_history.bal
================================================
import ballerina/ai;
import ballerina/io;
// Use the default model provider (with configuration added via a Ballerina VS Code command).
final ai:ModelProvider model = check ai:getDefaultModelProvider();
public function main(string subject) returns error? {
// Create a user message with the prompt as the content.
ai:ChatUserMessage userMessage = {
role: ai:USER,
content: `Tell me a joke about ${subject}!`
};
// Use an array to hold the conversation history.
ai:ChatMessage[] messages = [userMessage];
// Use the `chat` method to make a call with the conversation history.
// Alternatively, you can pass a single message (`userMessage`) too.
ai:ChatAssistantMessage assistantMessage = check model->chat(messages);
// Update the conversation history with the assistant's response.
messages.push(assistantMessage);
// Print the joke from the assistant's response.
string? joke = assistantMessage?.content;
io:println(joke);
// Continue the conversation by asking for an explanation.
messages.push({
role: ai:USER,
content: "Can you explain it?"
});
ai:ChatAssistantMessage assistantMessage2 = check model->chat(messages);
// Since the conversation history is passed, the model can provide a relevant explanation.
string? explanation = assistantMessage2?.content;
io:println(explanation);
}
================================================
FILE: examples/direct-llm-calls-with-history/direct_llm_calls_with_history.md
================================================
# Direct large language model (LLM) calls with history
The `ai:ModelProvider` type is a unified abstraction to integrate with large language models (LLMs) through provider-specific modules such as [ballerinax/ai.openai](https://central.ballerina.io/ballerinax/ai.openai/latest), [ballerinax/ai.anthropic](https://central.ballerina.io/ballerinax/ai.anthropic/latest), etc.
The `chat` method of the model provider accepts a user message or an array of chat messages, makes the call to the LLM with the message(s), and returns the relevant content of the response from the LLM.
This example demonstrates how to make direct calls to LLMs using the model provider, maintaining the chat history.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code direct_llm_calls_with_history.bal :::
::: out direct_llm_calls_with_history.out :::
## Related links
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/direct-llm-calls-with-history/direct_llm_calls_with_history.metatags
================================================
description: This BBE demonstrates how to do direct LLM calls, maintaining the chat history.
keywords: ballerina, ballerina by example, BBE, ai, llm, history, model-provider
================================================
FILE: examples/direct-llm-calls-with-history/direct_llm_calls_with_history.out
================================================
$ bal run direct_llm_calls_with_history.bal -- subject=programming
Why do programmers prefer dark mode?
Because light attracts bugs!
Sure! The joke plays on two concepts:
1. **Dark Mode vs. Light Mode**: Many programmers prefer using "dark mode" in their coding environments because it's easier on the eyes, especially during long coding sessions. Dark mode features a dark background with lighter text, while light mode has a light background with darker text.
2. **Bugs**: In programming, a "bug" refers to an error or flaw in the code that can cause it to behave unexpectedly. In a more literal sense, bugs (like insects) are often attracted to light sources.
So the humor comes from the double meaning of "bugs." The joke suggests that if programmers use light mode (which is bright), they might attract actual bugs, just like real insects are drawn to light. Therefore, by using dark mode, they’re avoiding both programming errors and actual bugs!
================================================
FILE: examples/direct-llm-calls-with-multimodal-input/direct_llm_calls_with_multimodal_input.bal
================================================
import ballerina/ai;
import ballerina/io;
// Use the default model provider (with configuration added via a Ballerina VS Code command).
final ai:ModelProvider model = check ai:getDefaultModelProvider();
// The type representing the expected response from the model.
// The generated JSON schema will also include the documentation.
# Description of an image.
type Description record {|
# A sentence describing the image.
string description;
# The confidence of the description, on a scale of 0 to 1.
decimal confidence;
# Categories that the image falls into.
string[] categories;
|};
public function main() returns error? {
// Define an `ai:ImageDocument` value with a URL to an image.
ai:ImageDocument image = {
content: "https://ballerina.io/img/branding/ballerina_logo_dgrey_png.png"
};
// Use the `generate` method with an image document as an interpolation.
// The model provider implementation will detect the multimodal input and handle
// constructing the request appropriately.
Description? description = check model->generate(`
Describe this image.
${image}
If it is not possible to describe the image, respond with null`);
// Handle the case where the model could not describe the image.
if description is () {
io:println("Could not describe the image");
return;
}
// Print the description.
io:println(description);
}
================================================
FILE: examples/direct-llm-calls-with-multimodal-input/direct_llm_calls_with_multimodal_input.md
================================================
# Direct large language model (LLM) calls with multimodal input
The `ai:ModelProvider` type is a unified abstraction to integrate with large language models (LLMs) through provider-specific modules such as [ballerinax/ai.openai](https://central.ballerina.io/ballerinax/ai.openai/latest), [ballerinax/ai.anthropic](https://central.ballerina.io/ballerinax/ai.anthropic/latest), etc.
If multimodal input is supported by an LLM, the model provider implementation for the LLM handles insertions of the `ai:Document` types (`ai:ImageDocument`, `ai:AudioDocument`, and `ai:FileDocument`) as multimodal input, and creates the request as expected by the LLM API.
This example demonstrates how to make direct calls to LLMs using the model provider, with multimodal input.
> Note: This example uses the default model provider implementation. To generate the necessary configuration, open up the VS Code command palette (`Ctrl` + `Shift` + `P` or `command` + `shift` + `P`), and run the `Configure default WSO2 Model Provider` command to add your configuration to the `Config.toml` file. If not already logged in, log in to the Ballerina Copilot when prompted. Alternatively, to use your own keys, use the relevant `ballerinax/ai.` model provider implementation.
For more information on the underlying module, see the [`ballerina/ai` module](https://lib.ballerina.io/ballerina/ai/latest/).
::: code direct_llm_calls_with_multimodal_input.bal :::
::: out direct_llm_calls_with_multimodal_input.out :::
## Related links
- [The `ballerinax/ai.anthropic` module](https://central.ballerina.io/ballerinax/ai.anthropic/latest)
- [The `ballerinax/ai.azure` module](https://central.ballerina.io/ballerinax/ai.azure/latest)
- [The `ballerinax/ai.openai` module](https://central.ballerina.io/ballerinax/ai.openai/latest)
- [The `ballerinax/ai.ollama` module](https://central.ballerina.io/ballerinax/ai.ollama/latest)
- [The `ballerinax/ai.deepseek` module](https://central.ballerina.io/ballerinax/ai.deepseek/latest)
- [The `ballerinax/ai.mistral` module](https://central.ballerina.io/ballerinax/ai.mistral/latest)
================================================
FILE: examples/direct-llm-calls-with-multimodal-input/direct_llm_calls_with_multimodal_input.metatags
================================================
description: This BBE demonstrates how to do direct LLM calls with multimodal input.
keywords: ballerina, ballerina by example, BBE, ai, llm, multimodal, model-provider
================================================
FILE: examples/direct-llm-calls-with-multimodal-input/direct_llm_calls_with_multimodal_input.out
================================================
$ bal run direct_llm_calls_with_multimodal_input.bal
{"description":"The word 'Ballerina' written in black text, with the letter 'l' stylized to look like a ballet dancer","confidence":0.98,"categories":["text","logo","typography"]}
================================================
FILE: examples/directories/directories.bal
================================================
import ballerina/file;
import ballerina/io;
public function main() returns error? {
// Creates a new directory with any non-existent parents.
string dirPath = check file:joinPath("foo", "bar");
check file:createDir(dirPath, file:RECURSIVE);
io:println("The " + dirPath + " directory created successfully.");
// Checks whether the directory of the provided path exists.
boolean dirExists = check file:test("foo", file:EXISTS);
io:println("Is foo directory exist: ", dirExists.toString());
// Copies the directory with another name.
check file:copy(dirPath, "test", file:REPLACE_EXISTING);
io:println("The " + dirPath + " directory copied successfully.");
// Renames the directory to another new name.
check file:rename("foo", "test1");
io:println("The foo directory renamed successfully.");
// Gets the list of files/directories in the given directory.
file:MetaData[] readDirResults = check file:readDir("test1");
io:println("Directory path: ", readDirResults[0].absPath);
io:println("Directory size: ", readDirResults[0].size.toString());
io:println("Is directory: ", readDirResults[0].dir.toString());
io:println("Modified at ", readDirResults[0].modifiedTime.toString());
// Removes the directory in the specified file path.
check file:remove("test");
// Removes the directory in the specified file path with all its children.
check file:remove("test1", file:RECURSIVE);
io:println("Directories removed successfully.");
}
================================================
FILE: examples/directories/directories.md
================================================
# Directories
The `file` library provides APIs to perform directory operations.
For more information on the underlying module, see the [`file` module](https://lib.ballerina.io/ballerina/file/latest/).
::: code directories.bal :::
To run this sample, use the `bal run` command.
::: out directories.out :::
================================================
FILE: examples/directories/directories.metatags
================================================
description: This BBE shows how to perform directory operations in Ballerina.
keywords: ballerina, ballerina by examples, BBE, directory, file, path
================================================
FILE: examples/directories/directories.out
================================================
$ bal run directories.bal
The foo/bar directory created successfully.
Is foo directory exist: true
The foo/bar directory copied successfully.
The foo directory renamed successfully.
Directory path: /A/B/C/test1/bar
Directory size: 64
Is directory: true
Modified at 1621859440 0.871000000
Directories removed successfully.
================================================
FILE: examples/directory-listener/directory_listener.bal
================================================
import ballerina/file;
import ballerina/log;
// The listener monitors any modifications done to a specific directory.
listener file:Listener inFolder = new ({
path: "/home/ballerina/fs-server-connector/observed-dir",
recursive: false
});
// The directory listener should have at least one of these predefined resources.
service "localObserver" on inFolder {
// This function is invoked once a new file is created in the listening directory.
remote function onCreate(file:FileEvent m) {
log:printInfo("Create: " + m.name);
}
// This function is invoked once an existing file is deleted from the listening directory.
remote function onDelete(file:FileEvent m) {
log:printInfo("Delete: " + m.name);
}
// This function is invoked once an existing file is modified in the listening directory.
remote function onModify(file:FileEvent m) {
log:printInfo("Modify: " + m.name);
}
}
================================================
FILE: examples/directory-listener/directory_listener.md
================================================
# Directory listener
The `file` library provides a `Directory Listener`, which is used to listen to the given directory in the local file system.
It notifies when new files are created in the directory or when the existing files are deleted or modified.
For more information on the underlying module, see the [`file` module](https://lib.ballerina.io/ballerina/file/latest/).
Before running this, change the value of the `path` field to indicate the path of the directory that you want to monitor.
As the recursive property is set to false, the listener does not monitor the child directories of the main directory that it listens to.
::: code directory_listener.bal :::
To run this sample, use the `bal run` command. After running the sample,
create a new file called `test1.txt` in the directory called `observed-dir`, modify it, and delete it.
::: out directory_listener.out :::
================================================
FILE: examples/directory-listener/directory_listener.metatags
================================================
description: This BBE shows how the Ballerina directory service monitors any modifications done to files inside a specific directory.
keywords: ballerina, ballerina by example, BBE, file, directory, directory listener
================================================
FILE: examples/directory-listener/directory_listener.out
================================================
$ bal run directory_listener.bal
time = 2020-12-12 13:49:08,497 level = INFO module = "" message = "Create: /home/ballerina/fs-server-connector/observed-dir/test1.txt"
time = 2020-12-12 13:49:41,709 level = INFO module = "" message = "Modify: /home/ballerina/fs-server-connector/observed-dir/test1.txt"
time = 2020-12-12 13:50:04,997 level = INFO module = "" message = "Delete: /home/ballerina/fs-server-connector/observed-dir/test1.txt"
================================================
FILE: examples/distinct-object-types/distinct_object_types.bal
================================================
import ballerina/io;
class Person {
public string name;
function init(string name) {
self.name = name;
}
};
// The `DistinctPerson` type is a proper subtype of the `Person` type.
distinct class DistinctPerson {
public string name;
function init(string name) {
self.name = name;
}
}
// The `SomeWhatDistinctPerson` type is a subtype of the `DistinctPerson` type
// since it includes the `DistinctPerson` type's type IDs via inclusion.
class SomeWhatDistinctPerson {
*DistinctPerson;
public string name;
function init(string name) {
self.name = name;
}
}
// The `EvenMoreDistinctPerson` type is a proper subtype of the `DistinctPerson`
// type since it has an additional type ID.
distinct class EvenMoreDistinctPerson {
*DistinctPerson;
public string name;
function init(string name) {
self.name = name;
}
}
public function main() {
Person person = new ("John Smith");
io:println(person is DistinctPerson);
DistinctPerson distinctPerson = new ("Alice Johnson");
io:println(distinctPerson is Person);
SomeWhatDistinctPerson someWhatDistinctPerson = new ("Michael Brown");
io:println(someWhatDistinctPerson is DistinctPerson);
io:println(distinctPerson is SomeWhatDistinctPerson);
EvenMoreDistinctPerson evenMoreDistinctPerson = new ("Sarah Wilson");
io:println(evenMoreDistinctPerson is DistinctPerson);
io:println(distinctPerson is EvenMoreDistinctPerson);
}
================================================
FILE: examples/distinct-object-types/distinct_object_types.md
================================================
# Distinct object types
For more explicit control over object type relations you can use `distinct` object types. Each distinct object type declaration has a unique type ID. When you include a distinct object type within another object type declaration, the new type's type ID set will include the type IDs of the included type. When checking if a given object type `OSub` is a subtype of a distinct object type `OSuper` there is the additional requirement that the `OSub` type must contain all the type IDs of the `OSuper` type.
This way you can achieve the same behavior as a nominal type system within Ballerina's structured type system, which is useful to support features such as GraphQL API interfaces.
::: code distinct_object_types.bal :::
::: out distinct_object_types.out :::
## Related links
- [Object types](/learn/by-example/object-types/)
- [Error subtyping](/learn/by-example/error-subtyping/)
- [Defining classes](/learn/by-example/defining-classes/)
- [Flexibly typed](https://ballerina.io/why-ballerina/flexibly-typed/)
- [GraphQL service - Interfaces](https://ballerina.io/learn/by-example/graphql-interfaces/)
================================================
FILE: examples/distinct-object-types/distinct_object_types.metatags
================================================
description: This BBE demonstrates distinct object types, defining nominal typing, object subtyping, and defining a class in Ballerina.
keywords: ballerina, ballerina by example, bbe, object, class, distinct, self, new
================================================
FILE: examples/distinct-object-types/distinct_object_types.out
================================================
$ bal distinct_object_types.bal
false
true
true
true
true
false
================================================
FILE: examples/docker-hello-world/Cloud.toml
================================================
[container.image]
repository="wso2inc"
name="hello"
tag="v0.1.0"
================================================
FILE: examples/docker-hello-world/build_output.out
================================================
$ bal build --cloud="docker"
Compiling source
wso2/hello:0.1.0
Generating executable
Generating artifacts...
@kubernetes:Docker - complete 2/2
Execute the below command to run the generated Docker image:
docker run -d -p 9090:9090 wso2inc/hello:v0.1.0
================================================
FILE: examples/docker-hello-world/docker_hello_world.bal
================================================
import ballerina/http;
// This code is completely focused on the business logic and it does not depend on the deployment.
listener http:Listener helloEP = new(9090);
service http:Service /helloWorld on helloEP {
resource function get sayHello() returns string {
return "Hello from Docker!";
}
}
================================================
FILE: examples/docker-hello-world/docker_hello_world.md
================================================
# Docker - Hello world
Ballerina supports generating Docker artifacts from code without any additional configuration. This simplifies the experience of developing and deploying Ballerina code in the cloud. Code to Cloud builds the containers and required artifacts by deriving the required values from the code. If you want to override the default values taken by the compiler, you can use a `Cloud.toml` file.
For more information, see [Code to Cloud Deployment](/learn/code-to-cloud-deployment/).
::: code docker_hello_world.bal :::
Before you build the package, you need to override some default values taken by the compiler. To do this, create a file named `Cloud.toml` in the package directory, and add the content below to it.
For all the supported key value properties, see [Code to Cloud specification](https://github.com/ballerina-platform/ballerina-spec/blob/master/c2c/code-to-cloud-spec.md).
::: code Cloud.toml :::
Execute the `bal build` command to build the Ballerina package. Code to Cloud generates only one container per package.
>**Note:** macOS users with Apple Silicon chips need to set an environment variable named `DOCKER_DEFAULT_PLATFORM` to `linux/amd64`, before building the image. This is because the Ballerina Docker image is not supported on Apple Silicon chips yet. Run `export DOCKER_DEFAULT_PLATFORM=linux/amd64` to set the environment variable.
::: out build_output.out :::
Verify if the Docker image is generated.
::: out docker_images.out :::
Run the generated Docker image.
::: out docker_run.out :::
Invoke the service.
::: out execute_curl.out:::
================================================
FILE: examples/docker-hello-world/docker_images.out
================================================
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wso2inc/hello v0.1.0 60d95f0928b2 About a minute ago 228MB
================================================
FILE: examples/docker-hello-world/docker_run.out
================================================
$ docker run -d -p 9090:9090 wso2inc/hello:v0.1.0
c04194eb0b4d0d78cbc8ca55e0527d381d8ab4a1a68f8ea5dd3770a0845d5fbb
================================================
FILE: examples/docker-hello-world/execute_curl.out
================================================
$ curl http://localhost:9090/helloWorld/sayHello
Hello from Docker!
================================================
FILE: examples/documentation/documentation.bal
================================================
// Lines starting with `#` contain structured documentation in Markdown format.
# Adds two integers.
// Documents the parameters of the function.
# + x - an integer
# + y - another integer
// Documents the return parameter of the function.
# + return - the sum of `x` and `y`
public function add(int x, int y) returns int {
return x + y;
}
================================================
FILE: examples/documentation/documentation.md
================================================
# Documentation
Annotations would be inconvenient for specifying structured documentation. Ballerina-flavored Markdown (BFM) is an additional convention on top of Markdown, which makes it more convenient for documenting Ballerina code.
::: code documentation.bal :::
::: out documentation.out :::
================================================
FILE: examples/documentation/documentation.metatags
================================================
description: This BBE demonstrates documentation in Ballerina, which is a Markdown format with additional conventions to write structured documentations in Ballerina.
keywords: ballerina, ballerina by example, bbe, documentation, Ballerina-flavored Markdown, BFM
================================================
FILE: examples/documentation/documentation.out
================================================
$ bal run documentation.bal
================================================
FILE: examples/dynamic-listener/dynamic_listener.bal
================================================
import ballerina/http;
import ballerina/lang.runtime;
http:Listener httpListener = check new (9090);
http:Service helloService = service object {
resource function get greeting() returns string {
// Send a response back to the caller.
return "Hello, World!";
}
// The resource function that will shutdown the server.
resource function post shutdown(http:Caller caller) returns error? {
// Send a response back to the caller.
check caller->respond("Shutting down the server");
// Stop the listener.
// This will be called automatically if the program exits by means of a system call.
check httpListener.gracefulStop();
// Deregister the listener dynamically.
runtime:deregisterListener(httpListener);
}
};
public function main() returns error? {
// Attach the service to the listener along with the resource path.
check httpListener.attach(helloService, "foo/bar");
// Start the listener.
check httpListener.'start();
// Register the listener dynamically.
runtime:registerListener(httpListener);
}
================================================
FILE: examples/dynamic-listener/dynamic_listener.client.out
================================================
$ curl http://localhost:9090/foo/bar/greeting
Hello, World!
# Invoke the shutdown resource to deregister the listener.
$ curl http://localhost:9090/foo/bar/shutdown -X POST
Shutting down the server
================================================
FILE: examples/dynamic-listener/dynamic_listener.md
================================================
# Dynamic listener
Dynamic listeners allow registering/deregistering a module listener dynamically. This example demonstrates how to register and deregister an HTTP listener and terminate it in the process.
::: code dynamic_listener.bal :::
Run the service as follows.
::: out dynamic_listener.server.out :::
Invoke the service by executing the following cURL command in a new terminal.
::: out dynamic_listener.client.out :::
================================================
FILE: examples/dynamic-listener/dynamic_listener.metatags
================================================
description: Registering/deregistering a module listener dynamically.
keywords: ballerina, ballerina by example, listener, dynamic listener, bbe, service
================================================
FILE: examples/dynamic-listener/dynamic_listener.server.out
================================================
$ bal run dynamic_listener.bal
================================================
FILE: examples/edi-to-record/bal_project.out
================================================
$ bal new edi_to_record
$ cd edi_to_record
$ bal add sorder
================================================
FILE: examples/edi-to-record/codegen_command.out
================================================
$ bal edi codegen -i resources/simple_order_schema.json -o modules/sorder/sorder.bal
================================================
FILE: examples/edi-to-record/edi_to_record.bal
================================================
import ballerina/io;
import edi_to_record.sorder;
public function main() returns error? {
string ediText = check io:fileReadString("resources/order.edi");
sorder:SimpleOrder simpleOrder = check sorder:fromEdiString(ediText);
io:println("Order Id: ", simpleOrder.header.orderId);
foreach sorder:Items_Type item in simpleOrder.items {
io:println("Item: ", item.item, ", Quantity: ", item.quantity);
}
}
================================================
FILE: examples/edi-to-record/edi_to_record.md
================================================
# EDI to record conversion
EDI is a widely used message format for business-to-business (B2B) communications. Ballerina simplifies working with EDI data by converting them to Ballerina records, so that all operations related to Ballerina records can be applied on EDI data as well (e.g. transforming EDI data, writing EDI data to databases, transering EDI data over various network protocols, etc.).
First, it is necessary to write a schema for the EDI data type that needs to be processed. Below is a simple EDI schema for sales order data. According to the schema, data must begin with a `HDR` segment followed by zero or more `ITM` segments. `HDR` segment contains four fields: `code`, `orderId`, `organization`, and `date`. `ITM` segment has three fields: `code`, `item`, and `quantity`.
::: code schema.json :::
Below is an example EDI text that can be parsed using the above schema.
::: code order.edi :::
Create a new Ballerina project named `edi_to_record` and create a module named `sorder` inside that project by using the below commands.
::: code bal_project.out :::
Create a new folder named `resources` in the root of the project and copy the schema file into it. At this point, directory structure of the project would look like below:
::: out package_structure.out :::
Get the EDI tool from the Ballerina central using the below command:
::: out tool_pull_command.out :::
Run the below command from the project root directory to generate the Ballerina parser for the above schema.
::: out codegen_command.out :::
>Note that it is recommended to place generated code for each EDI schema in a separate module in order to avoid conflicts.
Write a Ballerina program by using generated methods and records to work with EDI data.
::: code edi_to_record.bal :::
Run the program using the command below:
::: out output.out :::
================================================
FILE: examples/edi-to-record/edi_to_record.metatags
================================================
description: This BBE demonstrates how convert EDI text into Ballerina records based on an EDI schema.
keywords: ballerina, ballerina by example, bbe, edi, electronic data interchange, b2b, integration
================================================
FILE: examples/edi-to-record/order.edi
================================================
HDR*ORDER_1201*ABC_Store*2008-01-01~
ITM*A-250*12~
ITM*A-45*100~
ITM*D-10*58~
ITM*K-80*250~
ITM*T-46*28~
================================================
FILE: examples/edi-to-record/output.out
================================================
$ bal run
Order Id: ORDER_1201
Item: A-250, Quantity: 12
Item: A-45, Quantity: 100
Item: D-10, Quantity: 58
Item: K-80, Quantity: 250
Item: T-46, Quantity: 28
================================================
FILE: examples/edi-to-record/package_structure.out
================================================
└── edi_to_record
├── Ballerina.toml
├── Dependencies.toml
├── main.bal
├── modules
│ └── sorder
│ ├── Module.md
│ ├── resources
│ ├── sorder.bal
│ └── tests
│ └── lib_test.bal
└── resources
└── simple_order_schema.json
================================================
FILE: examples/edi-to-record/schema.json
================================================
{
"name": "SimpleOrder",
"delimiters" : {"segment" : "~", "field" : "*", "component": ":", "repetition": "^"},
"segments" : [
{
"code": "HDR",
"tag" : "header",
"minOccurances": 1,
"fields" : [{"tag": "code"}, {"tag" : "orderId", "required": true}, {"tag" : "organization"}, {"tag" : "date"}]
},
{
"code": "ITM",
"tag" : "items",
"maxOccurances" : -1,
"fields" : [{"tag": "code"}, {"tag" : "item", "required": true}, {"tag" : "quantity", "required": true, "dataType" : "int"}]
}
]
}
================================================
FILE: examples/edi-to-record/tool_pull_command.out
================================================
$ bal tool pull edi
================================================
FILE: examples/email-client-ssl-tls/email_client_ssl_tls.bal
================================================
import ballerina/email;
import ballerina/log;
public function main() returns error? {
email:ImapClient imapClient = check new (
"imap.email.com",
"reader@email.com",
"pass456",
security = email:SSL,
secureSocket = {
cert: "../resource/path/to/public.crt"
}
);
do {
while true {
email:Message? email = check imapClient->receiveMessage(timeout = 30);
if email is email:Message {
log:printInfo("Received an email", subject = email.subject, body = email?.body);
}
}
} on fail var err {
log:printError(err.message(), stackTrace = err.stackTrace());
check imapClient->close();
}
}
================================================
FILE: examples/email-client-ssl-tls/email_client_ssl_tls.md
================================================
# Email client - SSL/TLS
The `email:ImapClient` can be configured to communicate through SSL/TLS by providing a certificate file. The certificate can be provided through the `secureSocket` field of the client configuration. In addition to the certificate configuration, an optional `security` configuration is available to define the underlying transport protocol which needs to be used. The Ballerina `email` module supports both `STARTTLS` and `SSL` as the transport protocol. Use this to interact with email servers based on SSL/TLS encrypted secured connection.
>**Note:** The Ballerina `email` module also provides an `email:PopClient` which can be used likewise.
::: code email_client_ssl_tls.bal :::
## Prerequisites
- The email server should be up and running.
Run the sample code by executing the following command.
::: out email_client_ssl_tls.out :::
## Related links
- [`email:SecureSocket` - API documentation](https://lib.ballerina.io/ballerina/email/latest#SecureSocket)
- [`email:Security` enum - API documentation](https://lib.ballerina.io/ballerina/email/latest#Security)
================================================
FILE: examples/email-client-ssl-tls/email_client_ssl_tls.metatags
================================================
description: This BBE is about receiving emails over SSL/TLS secured connection. It has the client functionality for receiving emails.
keywords: ballerina, ballerina by example, bbe, email, POP3, POP, IMAP, ssl, tls
================================================
FILE: examples/email-client-ssl-tls/email_client_ssl_tls.out
================================================
$ bal run email_client_ssl_tls.bal
================================================
FILE: examples/email-service-ssl-tls/email_service_ssl_tls.bal
================================================
import ballerina/email;
import ballerina/log;
listener email:ImapListener emailListener = new ({
host: "imap.email.com",
username: "reader@email.com",
password: "pass456",
security: email:SSL,
secureSocket: {
cert: "../resource/path/to/public.crt"
}
});
service "observer" on emailListener {
remote function onMessage(email:Message email) {
log:printInfo("Received an email", subject = email.subject, content = email?.body);
}
remote function onError(email:Error emailError) {
log:printError(emailError.message(), stackTrace = emailError.stackTrace());
}
remote function onClose(email:Error? closeError) {
if closeError is email:Error {
log:printInfo(closeError.message(), stackTrace = closeError.stackTrace());
}
}
}
================================================
FILE: examples/email-service-ssl-tls/email_service_ssl_tls.md
================================================
# Email service - SSL/TLS
The `email:Service` receives messages from an email server via IMAP using the `email:ImapListener`. An `email:ImapListener` secured with SSL/TLS is created by providing the `secureSocket` configuration which requires the certificate of the email server as the `cert`. In addition to the certificate configuration, an optional `security` configuration is available to define the underlying transport protocol which needs to be used. The Ballerina `email` module supports both `STARTTLS` and `SSL` as the transport protocol. Use this to interact with email servers based on SSL/TLS encrypted secured connection.
>**Note:** The Ballerina `email` module also provides an `email:PopListener` which can be used likewise.
::: code email_service_ssl_tls.bal :::
## Prerequisites
- Email server should be up and running.
Run the service by executing the command below.
::: out email_service_ssl_tls.out :::
## Related links
- [`email:SecureSocket` - API documentation](https://lib.ballerina.io/ballerina/email/latest#SecureSocket)
- [`email:Security` enum - API documentation](https://lib.ballerina.io/ballerina/email/latest#Security)
================================================
FILE: examples/email-service-ssl-tls/email_service_ssl_tls.metatags
================================================
description: This BBE is about receiving emails over SSL/TLS secured connection. It has a listener-based functionality for receiving emails.
keywords: ballerina, ballerina by example, bbe, email, POP3, POP, IMAP, listener, ssl, tls
================================================
FILE: examples/email-service-ssl-tls/email_service_ssl_tls.out
================================================
$ bal run email_service_ssl_tls.bal
================================================
FILE: examples/ensureType-function/ensureType_function.bal
================================================
import ballerina/io;
function demo(anydata v) returns float|error {
// `v` is cast to the `float` type by calling `ensureType()`.
// `ensureType()` returns an error if the cast is not possible unlike the usual cast operation,
// which panics.
return v.ensureType(float);
}
public function main() {
float|error f1 = demo(12.5d);
io:println(f1);
float|error f2 = demo("12.5");
io:println(f2);
}
================================================
FILE: examples/ensureType-function/ensureType_function.md
================================================
# `ensureType` function
The `ensureType()` function is a lang library function that is similar to a cast. It takes a `typedesc` value as an argument. It returns an error if the cast is not possible.
::: code ensureType_function.bal :::
::: out ensureType_function.out :::
================================================
FILE: examples/ensureType-function/ensureType_function.metatags
================================================
description: This BBE demonstrates ensureType function in Ballerina.
keywords: ballerina, ballerina by example, bbe, ensureType
================================================
FILE: examples/ensureType-function/ensureType_function.out
================================================
$ bal run ensureType_function.bal
12.5
error("{ballerina}TypeCastError",message="incompatible types: 'string' cannot be cast to 'float'")
================================================
FILE: examples/enumerations/enumerations.bal
================================================
import ballerina/io;
// This is shorthand for,
// const RED = "RED";
// const GREEN = "GREEN";
// const BLUE = "BLUE";
// type Color RED|GREEN|BLUE;
enum Color {
RED,
GREEN,
BLUE
}
// An `enum` member can explicitly specify an associated expression.
enum Language {
ENG = "English",
TL = "Tamil",
SI = "Sinhala"
}
public function main() {
io:println(RED);
io:println(ENG);
}
================================================
FILE: examples/enumerations/enumerations.md
================================================
# Enumerations
`Enumerations` are shorthand for unions of `string` constants. A `const` can be used as a singleton type. An `enum` is not a distinct type. You can specify the string constants explicitly.
::: code enumerations.bal :::
::: out enumerations.out :::
================================================
FILE: examples/enumerations/enumerations.metatags
================================================
description: This BBE demonstrates enumerations in Ballerina.
keywords: ballerina, ballerina by example, bbe, enum, enumerations
================================================
FILE: examples/enumerations/enumerations.out
================================================
$ bal run enumerations.bal
RED
English
================================================
FILE: examples/environment-variables/environment_variables.bal
================================================
import ballerina/io;
import ballerina/os;
public function main() {
// Returns the environment variable value associated with the `HTTP_PORT`.
string port = os:getEnv("HTTP_PORT");
io:println("HTTP_PORT: ", port);
// Returns the username of the current user.
string username = os:getUsername();
io:println("Username: ", username);
// Returns the current user's home directory path.
string userHome = os:getUserHome();
io:println("Userhome: ", userHome);
}
================================================
FILE: examples/environment-variables/environment_variables.md
================================================
# Environment variables
The `os` library provides functions to retrieve information about the OS and the current users of the OS.
For more information on the underlying module, see the [`os` module](https://lib.ballerina.io/ballerina/os/latest/).
::: code environment_variables.bal :::
To run this sample use the `bal run` command.
::: out environment_variables.out :::
================================================
FILE: examples/environment-variables/environment_variables.metatags
================================================
description: BBE on how to to retrieve information about the OS.
keywords: ballerina, ballerina by examples, BBE, OS, environment
================================================
FILE: examples/environment-variables/environment_variables.out
================================================
$ bal run environment_variables.bal
HTTP_PORT: 5005
Username: Alex
Userhome: /Users/Alex
================================================
FILE: examples/error-binding-pattern/error_binding_pattern.bal
================================================
import ballerina/io;
type SampleErrorData record {|
int code;
string reason;
|};
type SampleError error;
public function main() {
// The value of the error message is ignored by using `_`.
// The value of the error cause is set to the `cause` variable.
// The values in the error detail mapping is set to the `code` and `reason` variables.
var error(_, cause, code = code, reason = reason) = getSampleError();
io:println("Cause: ", cause);
io:println("Info: ", code);
io:println("Fatal: ", reason);
// The values in the cause and detail mapping are ignored in this binding.
SampleError error(message2) = getSampleError();
io:println("Message: ", message2);
error? errorCause;
int errorCode;
// The error value is destructured and assigned to the variable references.
// The values of the error cause and `code` are assigned to the `errorCause` and `errorCode` variables.
// The error message and the `reason` field in the detail mapping are ignored.
error(_, errorCause, code = errorCode) = getSampleError();
io:println("Cause: ", errorCause);
io:println("Code: ", errorCode);
}
function getSampleError() returns SampleError {
return error("Transaction Failure", error("Database Error"), code = 20,
reason = "deadlock condition");
}
================================================
FILE: examples/error-binding-pattern/error_binding_pattern.md
================================================
# Error binding pattern
An error binding pattern matches the different elements of an error value such as the error message, error cause, and error detail mapping. It can also be used to destructure the reason, cause, and detail mapping values from an error value, and assign them to the existing variable references.
::: code error_binding_pattern.bal :::
::: out error_binding_pattern.out :::
## Related links
- [Binding patterns](/learn/by-example/binding-patterns/)
- [Typed binding pattern](/learn/by-example/typed-binding-pattern/)
- [Rest binding pattern in error binding pattern](/learn/by-example/rest-binding-pattern-in-error-binding-pattern/)
================================================
FILE: examples/error-binding-pattern/error_binding_pattern.metatags
================================================
description: This BBE demonstrates the error binding pattern, error typed binding, and error destructuring with binding patterns.
keywords: ballerina, ballerina by example, bbe, binding pattern, error binding pattern, error binding, error typed binding, error destructuring
================================================
FILE: examples/error-binding-pattern/error_binding_pattern.out
================================================
$ bal run error_binding_pattern.bal
Cause: error("Database Error")
Info: 20
Fatal: deadlock condition
Message: Transaction Failure
Cause: error("Database Error")
Code: 20
================================================
FILE: examples/error-binding-pattern-in-match-statement/error_binding_pattern_in_match_statement.bal
================================================
import ballerina/io;
type DatabaseErrorDetail record {|
int code;
string reason;
|};
type NetworkErrorDetail record {|
string 'type;
string phase;
int elapsedTime;
|};
type DatabaseError error;
type NetworkError error;
const ERROR = "Generic Error";
function errorMatch(error e) {
match e {
// This error binding pattern matches a `DatabaseError` type error value and
// binds its `code` detail mapping to the `code` variable.
var error DatabaseError(code = code) => {
io:println("Matched DatabaseError with code:", code);
}
// This error binding pattern matches an error value with an `ERROR` error message and
// binds its `code` detail mapping to the `code` variable.
var error(ERROR, code = code) => {
io:println("Matched Generic Error with message:", ERROR, " and code:", code);
}
// This error binding pattern matches the value of the error message in an error with the `message`
// variable, the value of the `type` field in the detail mapping is matched with the `errorType` variable, and
// the `...otherDetails` rest parameter is matched with the remaining detail fields.
var error(message, 'type = errorType, ...otherDetails) => {
io:println("Matched NetworkError with message:", message, ", type:", errorType, ", phase:",
otherDetails["phase"], " and elapsedTime:", otherDetails["elapsedTime"]);
}
}
}
public function main() {
error e1 = error("Generic Error", code = 20);
DatabaseError e2 = error("Database Error", code = 2, reason = "connection failure");
NetworkError e3 = error NetworkError("Bad Request", 'type = "http error", phase = "application",
elapsedTime = 338);
errorMatch(e1);
errorMatch(e2);
errorMatch(e3);
}
================================================
FILE: examples/error-binding-pattern-in-match-statement/error_binding_pattern_in_match_statement.md
================================================
# Error binding pattern in match statement
Error binding patterns can be used in a match statement to bind parts of a successful match of an error value to variables. You can use the rest binding pattern (`...r`) in the error binding pattern of a match statement to bind the detail mapping fields that are not explicitly bound in the binding pattern.
::: code error_binding_pattern_in_match_statement.bal :::
::: out error_binding_pattern_in_match_statement.out :::
## Related links
- [Match statement](/learn/by-example/match-statement/)
- [Error binding pattern](/learn/by-example/error-binding-pattern/)
================================================
FILE: examples/error-binding-pattern-in-match-statement/error_binding_pattern_in_match_statement.metatags
================================================
description: This BBE demonstrates matching error values in a match statement and using the error binding pattern in a match statement
keywords: ballerina, ballerina by example, bbe, binding pattern, error binding pattern, error binding, error typed binding, error, error value, match statement, match
================================================
FILE: examples/error-binding-pattern-in-match-statement/error_binding_pattern_in_match_statement.out
================================================
$ bal run error_binding_pattern_in_match_statement.bal
Matched Generic Error with message:Generic Error and code:20
Matched DatabaseError with code:2
Matched NetworkError with message:Bad Request, type:http error, phase:application and elapsedTime:338
================================================
FILE: examples/error-cause/error_cause.bal
================================================
import ballerina/io;
function foo(string s) returns error|int {
var res = int:fromString(s);
if res is error {
// The error constructor is called with the additional argument `res`, which is the
// error returned in case the `int:fromString()` function returns an error.
// This creates an error with a specified error and cause.
return error("not an integer", res);
} else {
return res;
}
}
public function main() {
error|int result = foo("1.1");
io:println(result);
if (result is error) {
// The `error:cause()` lang library function can be used to extract the cause
// from an error.
io:println(result.cause());
}
}
================================================
FILE: examples/error-cause/error_cause.md
================================================
# Error cause
Ballerina facilitates having a cause in an error value. It can be passed as a positional argument to the error constructor. The cause argument is optional in the error constructor.
::: code error_cause.bal :::
::: out error_cause.out :::
================================================
FILE: examples/error-cause/error_cause.metatags
================================================
description: This BBE demonstrates how to add a cause to the error and access it.
keywords: ballerina, ballerina by example, bbe, error cause
================================================
FILE: examples/error-cause/error_cause.out
================================================
$ bal run error_cause.bal
error("not an integer",error("{ballerina/lang.int}NumberParsingError",message="'string' value '1.1' cannot be converted to 'int'"))
error("{ballerina/lang.int}NumberParsingError",message="'string' value '1.1' cannot be converted to 'int'")
================================================
FILE: examples/error-detail/error_detail.bal
================================================
import ballerina/io;
// `err` is an error type constructed from named arguments to add some details to the error value.
error err = error("Whoops", httpCode = 27);
// The `HttpDetail` type is a record type that defines the`httpCode` field of type `int`.
type HttpDetail record {
int httpCode;
};
// When included in the declaration of the `error` error type, it uses the
// `HttpDetail` record type as the error detail record.
error httpError = error("Whoops", httpCode = 27);
// The `error:detail()` lang library function facilitates extracting the detail value
// out of the error.
HttpDetail d = httpError.detail();
public function main() {
io:println(d);
}
================================================
FILE: examples/error-detail/error_detail.md
================================================
# Error detail
Error detail is a map that contains arbitrary extra details about the context of the error. The `error` type describes an error value with a detail map that has the type `T`. Named arguments of the error constructor specify the fields of the detail record. An immutable copy of each field is made in the detail record using the `cloneReadOnly` function.
::: code error_detail.bal :::
::: out error_detail.out :::
================================================
FILE: examples/error-detail/error_detail.metatags
================================================
description: This BBE demonstrates how to add a detail component to the error and access it.
keywords: ballerina, ballerina by example, bbe, error detail
================================================
FILE: examples/error-detail/error_detail.out
================================================
$ bal run error_detail.bal
{"httpCode":27}
================================================
FILE: examples/error-handling/error_handling.bal
================================================
import ballerina/io;
// Converts `bytes` to a `string` value and then to an `int` value.
function intFromBytes(byte[] bytes) returns int|error {
string|error ret = string:fromBytes(bytes);
// The `is` operator can be used to distinguish errors from other values.
if ret is error {
return ret;
} else {
return int:fromString(ret);
}
}
// The `main` function can return an `error` value.
public function main() returns error? {
int|error res = intFromBytes([104, 101, 108, 108, 111]);
if res is error {
// The `check` expression is the shorthand for this pattern of
// checking if a value is an `error` value and it is returning that value.
return res;
} else {
io:println("result: ", res);
return;
}
}
================================================
FILE: examples/error-handling/error_handling.md
================================================
# Error handling
Usually, a function handles errors by passing them up to its caller. The `main` function can also return an `error` value.
::: code error_handling.bal :::
::: out error_handling.out :::
================================================
FILE: examples/error-handling/error_handling.metatags
================================================
description: This BBE demonstrates how errors are handled in Ballerina
keywords: ballerina, ballerina by example, bbe, error, error handling
================================================
FILE: examples/error-handling/error_handling.out
================================================
$ bal run error_handling.bal
error: {ballerina/lang.int}NumberParsingError {"message":"'string' value 'hello' cannot be converted to 'int'"}
================================================
FILE: examples/error-logging/error_logging.bal
================================================
import ballerina/log;
function processOrder(int orderId) returns error? {
if orderId <= 0 {
return error("Invalid order ID", orderId = orderId);
}
// Simulate processing logic here
}
function connectToDatabase() returns error? {
error dbError = error("Connection timeout");
return error("Failed to connect to database", dbError, retryCount = 3);
}
public function main() {
// Basic error logging
error simpleError = error("Something went wrong");
log:printError("Application encountered an error", simpleError);
// Error logging with additional context
error? processOrderRes = processOrder(-1);
if processOrderRes is error {
log:printError("Order processing failed", processOrderRes,
operation = "processOrder",
timestamp = "2025-08-25T10:30:00Z");
}
// Error with causes and stack trace
error? databaseConnectionRes = connectToDatabase();
if databaseConnectionRes is error {
log:printError("Database operation failed", databaseConnectionRes,
serviceName = "OrderService",
severity = "HIGH");
}
// Custom error with detailed information
error customError = error("Validation failed",
'field = "email",
value = "invalid-email",
expectedFormat = "user@domain.com");
log:printError("User input validation error", customError,
userId = "user123",
action = "registration");
}
================================================
FILE: examples/error-logging/error_logging.md
================================================
# Error logging
This example demonstrates how to log errors with detailed information including error messages, causes, stack traces, and additional context in Ballerina.
::: code error_logging.bal :::
Error logging in Ballerina automatically includes the error message, stack trace, and any error details. You can also add additional contextual information to help with debugging and monitoring.
::: out error_logging.out :::
The logged errors include comprehensive information such as the error message, causes (for nested errors), stack trace showing the call hierarchy, and any additional context provided through key-value pairs.
## Related links
- [`log` module - Specification](https://ballerina.io/spec/log/#2-logging)
- [`log` module - API documentation](https://lib.ballerina.io/ballerina/log/latest)
================================================
FILE: examples/error-logging/error_logging.metatags
================================================
description: BBE on how to log error messages with detailed error information and context in Ballerina.
keywords: ballerina, ballerina by examples, bbe, log, error, exception, stack trace
================================================
FILE: examples/error-logging/error_logging.out
================================================
$ bal run error-logging.bal
time=2025-08-25T10:35:20.456+05:30 level=ERROR module="" message="Application encountered an error" error={"causes":[],"message":"Something went wrong","detail":{},"stackTrace":[{"callableName":"main","moduleName":(),"fileName":"error-logging.bal","lineNumber":18}]}
time=2025-08-25T10:35:20.458+05:30 level=ERROR module="" message="Order processing failed" error={"causes":[],"message":"Invalid order ID","detail":{"orderId":-1},"stackTrace":[{"callableName":"processOrder","moduleName":(),"fileName":"error-logging.bal","lineNumber":5},{"callableName":"main","moduleName":(),"fileName":"error-logging.bal","lineNumber":21}]} operation="processOrder" timestamp="2025-08-25T10:30:00Z"
time=2025-08-25T10:35:20.460+05:30 level=ERROR module="" message="Database operation failed" error={"causes":[{"message":"Connection timeout","detail":{},"stackTrace":[{"callableName":"connectToDatabase","moduleName":(),"fileName":"error-logging.bal","lineNumber":12}]}],"message":"Failed to connect to database","detail":{"retryCount":3},"stackTrace":[{"callableName":"connectToDatabase","moduleName":(),"fileName":"error-logging.bal","lineNumber":13},{"callableName":"main","moduleName":(),"fileName":"error-logging.bal","lineNumber":28}]} serviceName="OrderService" severity="HIGH"
time=2025-08-25T10:35:20.462+05:30 level=ERROR module="" message="User input validation error" error={"causes":[],"message":"Validation failed","detail":{"field":"email","value":"invalid-email","expectedFormat":"user@domain.com"},"stackTrace":[{"callableName":"main","moduleName":(),"fileName":"error-logging.bal","lineNumber":36}]} userId="user123" action="registration"
================================================
FILE: examples/error-reporting/error_reporting.bal
================================================
import ballerina/io;
type Person record {
string name;
int age;
};
function validatePeople(Person[] people) returns error? {
if people.length() == 0 {
// Create an error value specifying only the error message.
return error("Expected a non-empty array");
}
foreach Person p in people {
io:println("Validating ", p.name);
error? err = validatePerson(p);
if err is error {
// Create a new error value with the validation error as the cause
// and the `Person` value for which validation failed in the detail mapping.
return error("Validation failed for a person", err, person = p);
}
}
}
function validatePerson(Person person) returns error? {
int age = person.age;
if age < 0 {
// If validation fails for age, create a new error value specifying
// an error message and the age value for which validation failed.
return error("Age cannot be negative", age = age);
}
}
public function main() {
Person[] people = [
{name: "Alice", age: 25},
{name: "Bob", age: -1},
{name: "Charlie", age: 30}
];
// Note how the `Person` value after the value for which validation fails is
// not processed.
error? err = validatePeople(people);
if err is error {
printError(err);
}
}
// Helper function to print internals of error value.
function printError(error err) {
io:println("Message: ", err.message());
io:println("Detail: ", err.detail());
io:println("Stack trace: ", err.stackTrace());
error? cause = err.cause();
if cause is error {
io:println("Cause:");
printError(cause);
}
}
================================================
FILE: examples/error-reporting/error_reporting.md
================================================
# Errors
Ballerina does not have exceptions. Instead functions report invalid states by returning error values. Each error value has,
1. Message, a human-readable `string` value describing the error.
2. Cause, which is an `error` value if this error was caused due to another error, which needs to be propagated, otherwise nil.
3. Detail, a mapping value consisting of additional information about the error.
4. Stack trace, a snapshot of the state of the execution stack when the error value was created.
Error values are immutable.
You can create a new error value using an error constructor. As the first argument to the error constructor, it expects the message string. As the second argument, you can optionally pass in an `error?` value for cause. Subsequent named arguments, if specified, will be used to create the detail mapping. The stack trace is provided by the runtime.
::: code error_reporting.bal :::
::: out error_reporting.out :::
## Related links
- [Error subtyping](https://ballerina.io/learn/by-example/error-subtyping/)
- [Error cause](https://ballerina.io/learn/by-example/error-cause/)
- [Error detail](https://ballerina.io/learn/by-example/error-detail/)
================================================
FILE: examples/error-reporting/error_reporting.metatags
================================================
description: This BBE demonstrates how errors are reported in Ballerina
keywords: ballerina, ballerina by example, bbe, error, error reporting
================================================
FILE: examples/error-reporting/error_reporting.out
================================================
$ bal run error_reporting.bal
Validating Alice
Validating Bob
Message: Validation failed for a person
Detail: {"person":{"name":"Bob","age":-1}}
Stack trace: [callableName: validatePeople fileName: error_reporting.bal lineNumber: 20,callableName: main fileName: error_reporting.bal lineNumber: 42]
Cause:
Message: Age cannot be negative
Detail: {"age":-1}
Stack trace: [callableName: validatePerson fileName: error_reporting.bal lineNumber: 30,callableName: validatePeople fileName: error_reporting.bal lineNumber: 16,callableName: main fileName: error_reporting.bal lineNumber: 42]
================================================
FILE: examples/error-subtyping/error_subtyping.bal
================================================
import ballerina/io;
type InvalidIntDetail record {|
int value;
|};
// Subtype of `InvalidIntDetail`.
type InvalidI32Detail record {|
int:Signed32 value;
|};
// Error with the `InvalidIntDetail` type as the detail type.
type InvalidIntError error;
// Error with `InvalidI32Detail` as the detail type. Thus it is a subtype of `InvalidIntError`.
type InvalidI32Error error;
// Distinct error with the `InvalidIntDetail` type as the detail type and a unique type ID.
// Therefore, this is a proper subtype of `InvalidIntError`, but doesn't have a subtype relationship
// with `AnotherDistinctIntError` because they have different type IDs.
type DistinctIntError distinct error;
// Another distinct error with `InvalidIntDetail` as the detail type, but a different type ID to `DistinctIntError`
// This is also a proper subtype of `InvalidIntError`, but doesn't have a subtype relationship with `DistinctIntError`
type AnotherDistinctIntError distinct error;
public function main() {
InvalidI32Error e1 = createInvalidI32Error(5);
io:println(e1 is InvalidIntError);
InvalidIntError e2 = createInvalidIntError(5);
io:println(e2 is DistinctIntError);
DistinctIntError e3 = createDistinctInvalidIntError(5);
// This is true because `InvalidInt` is not a distinct type, thus it ignores the type id of `e3`.
io:println(e3 is InvalidIntError);
// This is false because `DistinctIntError` and `AnotherDistinctIntError` have different type ids.
io:println(e3 is AnotherDistinctIntError);
}
// Helper functions to create errors.
function createInvalidIntError(int value) returns InvalidIntError {
return error("Invalid int", value = value);
}
function createDistinctInvalidIntError(int value) returns DistinctIntError {
return error("Invalid int", value = value);
}
function createInvalidI32Error(int:Signed32 value) returns InvalidI32Error {
return error("Invalid i32", value = value);
}
================================================
FILE: examples/error-subtyping/error_subtyping.md
================================================
# Error subtyping
If we want to identify if a given `error` type (say `ESub`) is a subtype of another error type (say `ESuper`), first we need to check if `ESuper` is a distinct error type. If it is not, then `ESub` is a subtype if and only if the detail type of `ESub` is a subtype of the detail type of `ESuper`.
If more explicit control over error type relations is desired you can use `distinct` error types. Each declaration of a distinct error type has a unique type ID. If `ESuper` is a distinct error type there is the additional requirement that the type ID set of `ESub` must contain all the type IDs of `ESuper`. In other words, with distinct error types, typing relationships can be made more like nominal typing.
Note that you can create subtypes of distinct error types by intersecting them with other error types.
::: code error_subtyping.bal :::
::: out error_subtyping.out :::
## Related links
- [Type intersection for error types](https://ballerina.io/learn/by-example/error-type-intersection/)
================================================
FILE: examples/error-subtyping/error_subtyping.metatags
================================================
description: This BBE demonstrates how subtypes of errors are defined in Ballerina
keywords: ballerina, ballerina by example, bbe, error, error subtypes
================================================
FILE: examples/error-subtyping/error_subtyping.out
================================================
$ bal run error_subtyping.bal
true
false
true
false
================================================
FILE: examples/error-type-intersection/error_type_intersection.bal
================================================
import ballerina/io;
type InputErrorDetail record {|
int|string value;
|};
type NumericErrorDetail record {|
int|float value;
|};
type InputError error;
type NumericError error;
// `NumericInputError` has detail type, `record {| int value |}`.
type NumericInputError InputError & NumericError;
type DistinctInputError distinct error;
type DistinctNumericError distinct error;
// `DistinctNumericInputError` has type IDs of both `DistinctInputError` and `DistinctNumericError`.
type DistinctNumericInputError DistinctInputError & DistinctNumericError;
public function main() {
NumericInputError e1 = error("Numeric input error", value = 5);
// `e1` belongs to `InputError` since its detail type is a subtype of `InputErrorDetail`.
io:println(e1 is InputError);
// `e1` doesn't belong to `DistinctInputError` since it doesn't have the type ID of `DistinctInputError`.
io:println(e1 is DistinctInputError);
DistinctNumericInputError e2 = error("Distinct numeric input error", value = 5);
// `e2` belongs to `InputError` since its detail type is a subtype of `InputErrorDetail`.
io:println(e2 is InputError);
// `e2` belongs to `DistinctInputError` since its type ID set includes the type id of `DistinctInputError`.
io:println(e2 is DistinctInputError);
}
================================================
FILE: examples/error-type-intersection/error_type_intersection.md
================================================
# Type intersection for error types
If you intersect two `error` types, the resulting type's detail type is the intersection of the detail types of both types. Furthermore, if any of the types being intersected is a distinct type, then the resultant type's type ID set includes all the type IDs of that type. Thus it is a subtype of both types. This way, you can create an error type that is a subtype of multiple distinct types and also use a more specific detail type.
::: code error_type_intersection.bal :::
::: out error_type_intersection.out :::
+ [Error subtyping](https://ballerina.io/learn/by-example/error-subtyping/)
================================================
FILE: examples/error-type-intersection/error_type_intersection.metatags
================================================
description: This BBE demonstrates type intersection for error types.
keywords: ballerina, ballerina by example, bbe, error, intersection, distinct
================================================
FILE: examples/error-type-intersection/error_type_intersection.out
================================================
$ bal run error_type_intersection.bal
true
false
true
true
================================================
FILE: examples/expression-equality/expression_equality.bal
================================================
import ballerina/io;
public function main() {
map student = {"name": "John", "age": "25"};
map student2 = {"name": "John", "age": "25"};
// The `==` check evaluates to `true` since the values are considered equal based on the
// equality of members.
io:println(student == student2);
// The `!=` check evaluates to `false` since the values are considered equal based on the
// equality of members.
io:println(student != student2);
// The `===` check evaluates to `false` since references are different.
io:println(student === student2);
// Assign the value assigned to the `student` variable to the `student3` variable.
map student3 = student;
// The `===` check evaluates to `true` since references are same.
io:println(student3 === student);
// The `!==` check evaluates to `false` since references are same.
io:println(student3 !== student);
// Since values of simple types do not have storage identity
// `===` and `==` return the same result, except for floating point values.
int a = 1;
anydata b = 1;
// The `==` and `===` checks evaluates to `true` since the values are equal.
io:println(a == b);
io:println(a === b);
decimal c = 1.0;
decimal d = 1.00;
// The `==` check evaluates to `true` since the values are equal.
io:println(c == d);
// The `===` check evaluates to `false` since `c` and `d` are distinct values with different precision.
io:println(c === d);
string s1 = "Hello";
string s2 = "Hello";
// String values also do not have storage identity, and `===` checks are
// the same as `==` checks for string values also.
io:println(s1 == s2);
io:println(s1 === s2);
}
================================================
FILE: examples/expression-equality/expression_equality.md
================================================
# Expression equality
Expression equality is determined using two operators: `==` for value equality and `===` for reference equality. The `==` operator checks for deep equality between two values by comparing the actual data. In contrast, the `===` operator checks whether two values share the same storage identity, meaning it checks if the values reference the same memory location.
Values with storage identity, such as structured types like maps and arrays, have an identity that comes from the location where the value is stored. For such values, the `===` check determines if two references point to the same location. For simple types such as integers and booleans, which do not have storage identity, `===` behaves the same as `==` except for floating point values.
::: code expression_equality.bal :::
::: out expression_equality.out :::
## Related links
- [Maps](/learn/by-example/maps)
================================================
FILE: examples/expression-equality/expression_equality.metatags
================================================
description: This BBE demonstrates expression equality checks in Ballerina.
keywords: ballerina, ballerina by example, bbe, ==, !=, ===, !==, equality
================================================
FILE: examples/expression-equality/expression_equality.out
================================================
$ bal run expression_equality.bal
true
false
false
true
false
true
true
true
false
true
true
================================================
FILE: examples/expression-oriented-style/expression_oriented_style.bal
================================================
import ballerina/io;
// You can define the body of a function using the `=>` notation followed by the return value expression
// instead of using curly braces to define the function body block, when the function body is just
// a return statement with an expression.
function inc1(int x) returns int => x + 1;
// `inc2` is effectively the same as` inc1`.
function inc2(int x) returns int {
return x + 1;
}
var obj = object {
private int x = 1;
// This can also be used to define the body of a method.
function getX() returns int => self.x;
};
// Let expressions allow you to do more with an expression.
// Here, the let expression defines a variable that can be used in the function call.
function hypot(float x) returns float =>
let float x2 = x * x in float:sqrt(x2 + x2);
public function main() {
int x = 2;
io:println(inc1(x));
io:println(inc2(x));
io:println(obj.getX());
io:println(hypot(1.5));
}
================================================
FILE: examples/expression-oriented-style/expression_oriented_style.md
================================================
# Expression-oriented style
Ballerina supports statements for familiarity but also tries to enable an expression-oriented style to be more intuitive.
For example instead of using an if statement Ballerina allows `return x > 0 ? 1 : 0;`.
::: code expression_oriented_style.bal :::
::: out expression_oriented_style.out :::
================================================
FILE: examples/expression-oriented-style/expression_oriented_style.metatags
================================================
description: This BBE demonstrates the expression-oriented style in ballerina.
keywords: ballerina, ballerina by example, bbe, expression, expression-bodied
================================================
FILE: examples/expression-oriented-style/expression_oriented_style.out
================================================
$ bal run expression_oriented_style.bal
3
3
1
2.1213203435596424
================================================
FILE: examples/filepaths/filepaths.bal
================================================
import ballerina/file;
import ballerina/io;
public function main() returns error? {
// Gets the absolute representation of the path.
string absValue = check file:getAbsolutePath("test.txt");
io:println("Absolute path: ", absValue);
// Checks whether the path is absolute.
boolean isAbs = check file:isAbsolutePath("/A/B/C");
io:println("/A/B/C is absolute: ", isAbs);
// Gets the base name of the path.
string name = check file:basename("/A/B/C");
io:println("Filename of /A/B/C: ", name);
// Gets the enclosing parent directory.
string parentPath = check file:parentPath("/A/B/C");
io:println("Parent of /A/B/C: ", parentPath);
// Gets the shortest path name equivalent to the path by purely lexical processing.
string normalizedPath = check file:normalizePath("foo/../bar", file:CLEAN);
io:println("Normalized path of foo/../bar: ", normalizedPath);
// Gets the list of path elements joined by the OS-specific path separator.
string[] parts = check file:splitPath("/A/B/C");
io:println(string `Path elements of /A/B/C: ${parts.toString()}`);
// Joins any number of path elements into a single path.
string path = check file:joinPath("/", "foo", "bar");
io:println("Built path of '/', 'foo', 'bar': ", path);
// Returns a relative path that is logically equivalent to the target path when joined to the base path.
string relPath = check file:relativePath("a/b/c", "a/c/d");
io:println("Relative path between 'a/b/c' and 'a/c/d': ", relPath);
}
================================================
FILE: examples/filepaths/filepaths.md
================================================
# File paths
The `file` library also provides APIs to manipulate file paths in a way that is compatible with the target operating system.
For more information on the underlying module, see the [`file` module](https://lib.ballerina.io/ballerina/file/latest/).
::: code filepaths.bal :::
To run this sample, use the `bal run` command.
::: out filepaths.out :::
================================================
FILE: examples/filepaths/filepaths.metatags
================================================
description: This BBE shows how to manipulate file paths in a way that is compatible with the target Operating System in Ballerina.
keywords: ballerina, ballerina by examples, BBE, file, directory, path, filepath
================================================
FILE: examples/filepaths/filepaths.out
================================================
$ bal run filepaths.bal
On Unix:
Absolute path of test.txt: /A/test.txt
/A/B/C is absolute: true
Filename of /A/B/C: C
Parent of /A/B/C: /A/B
Normalized path of foo/../bar: bar
Path elements of /A/B/C: ["A","B","C"]
Built path of '/', 'foo', 'bar': /foo/bar
Relative path between 'a/b/c' and 'a/c/d': ../../c/d
On Windows:
Absolute path of test.txt: \A\test.txt
/A/B/C is absolute: false
Filename of /A/B/C: C
Parent of /A/B/C: \A\B
Normalized path of foo/../bar: bar
Path elements of /A/B/C: ["A","B","C"]
Built path of '/', 'foo', 'bar': \foo\bar
Relative path between 'a/b/c' and 'a/c/d': ..\..\c\d
================================================
FILE: examples/filepaths/tests/filepath_test.bal
================================================
import ballerina/test;
import ballerina/os;
string[] outputs = [];
// This is the mock function, which will replace the real function.
@test:Mock {
moduleName: "ballerina/io",
functionName: "println"
}
test:MockFunction mock_printLn = new();
public function mockPrint(any... val) {
outputs.push(val.reduce(function (any a, any b) returns string => a.toString() + b.toString(), "").toString());
}
boolean isWindows = os:getEnv("OS") != "";
@test:Config {}
function testFunc() returns error? {
test:when(mock_printLn).call("mockPrint");
// Invoking the main function.
check main();
string absolutePath;
string filename;
string parent;
string normalized;
string elements;
string buildPath;
string relative;
if (isWindows) {
absolutePath ="/A/B/C is absolute: false";
filename ="Filename of /A/B/C: C";
parent ="Parent of /A/B/C: \\A\\B";
normalized ="Normalized path of foo/../bar: bar";
elements ="Path elements of /A/B/C: [\"A\",\"B\",\"C\"]";
buildPath ="Built path of '/', 'foo', 'bar': \\foo\\bar";
relative ="Relative path between 'a/b/c' and 'a/c/d': ..\\..\\c\\d";
} else {
absolutePath ="/A/B/C is absolute: true";
filename ="Filename of /A/B/C: C";
parent ="Parent of /A/B/C: /A/B";
normalized ="Normalized path of foo/../bar: bar";
elements ="Path elements of /A/B/C: [\"A\",\"B\",\"C\"]";
buildPath ="Built path of '/', 'foo', 'bar': /foo/bar";
relative ="Relative path between 'a/b/c' and 'a/c/d': ../../c/d";
}
test:assertEquals(outputs[1], absolutePath);
test:assertEquals(outputs[2], filename);
test:assertEquals(outputs[3], parent);
test:assertEquals(outputs[4], normalized);
test:assertEquals(outputs[5], elements);
test:assertEquals(outputs[6], buildPath);
test:assertEquals(outputs[7], relative);
}
================================================
FILE: examples/files/files.bal
================================================
import ballerina/file;
import ballerina/io;
public function main() returns error? {
// Creates a file in the given file path.
check file:create("bar.txt");
io:println("The bar.txt file created successfully.");
// Checks whether the file exists on the provided path.
boolean fileExists = check file:test("bar.txt", file:EXISTS);
io:println("Does the bar.txt file exist: ", fileExists.toString());
// Checks whether the file is readable or not.
fileExists = check file:test("bar.txt", file:READABLE);
io:println("Is the bar.txt file readable: ", fileExists.toString());
// Checks whether the file is writable or not.
fileExists = check file:test("bar.txt", file:WRITABLE);
io:println("Is the bar.txt file writeable: ", fileExists.toString());
// Copies the file or directory to the new path.
check file:copy("bar.txt", "bar1.txt", file:REPLACE_EXISTING);
io:println("The bar.txt file copied successfully.");
check file:rename("bar.txt", "bar2.txt");
io:println("The bar.txt file renamed successfully.");
// Gets the metadata information of the file.
file:MetaData fileMetadata = check file:getMetaData("bar1.txt");
io:println("File path: ", fileMetadata.absPath);
io:println("File size: ", fileMetadata.size.toString());
io:println("Is directory: ", fileMetadata.dir.toString());
io:println("Modified at ", fileMetadata.modifiedTime.toString());
// Removes the file in the specified file path.
check file:remove("bar1.txt");
check file:remove("bar2.txt");
io:println("Files removed successfully.");
}
================================================
FILE: examples/files/files.md
================================================
# Files
The `file` library provides APIs to perform file-system operations.
For more information on the underlying module, see the [`file` module](https://lib.ballerina.io/ballerina/file/latest/).
::: code files.bal :::
To run this sample, use the `bal run` command.
::: out files.out :::
================================================
FILE: examples/files/files.metatags
================================================
description: This BBE shows how to perform file-system operations in Ballerina.
keywords: ballerina, ballerina by examples, BBE, file, path
================================================
FILE: examples/files/files.out
================================================
$ bal run files.bal
The bar.txt file created successfully.
Does the bar.txt file exist: true
Is the bar.txt file readable: true
Is the bar.txt file writeable: true
The bar.txt file copied successfully.
The bar.txt file renamed successfully.
File path: /A/B/bar1.txt
File size: 0
Is directory: false
Modified at [1643011228,0.178000000]
Files removed successfully.
================================================
FILE: examples/filler-values-of-a-list/filler_values_of_a_list.bal
================================================
import ballerina/io;
public function main() {
// Since the filler value for int is `0`,
// the `severity` array will be initialized as [0, 0, 0].
int[3] severity = [];
io:println(severity);
// Since the filler value for `boolean` is `false` and the rest type can contain no members,
// the `scores` will be initialized as `[false]`.
[boolean, int...] scores = [];
io:println(scores);
// As the filler value for string is `""`,
// the `names` array will be initialized as ["John", "Mike", ""].
string[3] names = ["John", "Mike"];
io:println(names);
// As the filler value for the list is an empty list,
// the `orderItems` tuple will be initialized as `[["carrot", "apple"], ["avacado", "egg"], ["", ""]]``.
string[3][2] orderItems = [["carrot", "apple"], ["avocado", "egg"]];
io:println(orderItems);
}
================================================
FILE: examples/filler-values-of-a-list/filler_values_of_a_list.md
================================================
# Filler values of a list
Lists can be initialized with the help of filler values. These are the default values that a list member will be initialized with if you do not provide a value in the list constructor. This gives you the flexibility to assign the actual values later for lists.
::: code filler_values_of_a_list.bal :::
::: out filler_values_of_a_list.out :::
## Related links
- [Tuples](/learn/by-example/tuples)
- [Arrays](/learn/by-example/arrays)
- [Nested arrays](/learn/by-example/nested-arrays)
================================================
FILE: examples/filler-values-of-a-list/filler_values_of_a_list.metatags
================================================
description: Initialize an array and a collection with default values.
keywords: ballerina, ballerina by example, bbe, filler, default, initialize, array, collection, tuple
================================================
FILE: examples/filler-values-of-a-list/filler_values_of_a_list.out
================================================
$ bal run filler_values_of_a_list.bal
[0,0,0]
[false]
["John","Mike",""]
[["carrot","apple"],["avocado","egg"],["",""]]
================================================
FILE: examples/floating-point-numbers/floating_point_numbers.bal
================================================
import ballerina/io;
public function main() {
float x = 1.0;
int n = 5;
// Numeric literals can use `f` or `F` suffix for them to be interpreted as `float` values.
// (Similarly, the `d` or `D` suffix can be used for `decimal`).
var f = 12345f;
io:println(f is float);
// No implicit conversions between integers and floating point values are allowed.
// You can use `` for explicit conversions.
float y = x + n;
io:println(y);
}
================================================
FILE: examples/floating-point-numbers/floating_point_numbers.md
================================================
# Floating point numbers
The `float` type is IEEE 64-bit binary floating point and supports the same arithmetic operators as `int`.
::: code floating_point_numbers.bal :::
::: out floating_point_numbers.out :::
================================================
FILE: examples/floating-point-numbers/floating_point_numbers.metatags
================================================
description: This BBE introduces the float type in Ballerina.
keywords: ballerina, ballerina by example, bbe, floating point numbers, float, NaN
================================================
FILE: examples/floating-point-numbers/floating_point_numbers.out
================================================
$ bal run floating_point_numbers.bal
true
6.0
================================================
FILE: examples/flush/flush.bal
================================================
import ballerina/io;
public function main() {
worker A {
10 -> B;
// This transmission will not happen.
"Hello" -> B;
// Flush all messages sent to worker 'B'.
// Worker 'A' will stop here until all messages are sent or until a failure occurs in 'B'.
error? result = trap flush B;
// This will return the `panic` error.
io:println("Result from worker B : ", result ?: "nil");
}
worker B {
int value = <- A;
io:println(string `Received integer ${value} from worker A`);
if value == 10 {
panic error("Error in worker B");
}
string text = <- A;
io:println(string `Received string "${text}" from worker A`);
}
}
================================================
FILE: examples/flush/flush.md
================================================
# Flush
The `flush` action is used by the workers to check whether all the messages sent to a given worker are successfully recovered or not. If the transmission fails with an `error` or a `panic`, then, the error will be propagated to the waiting strand. Otherwise, it will return `nil`.
::: code flush.bal :::
::: out flush.out :::
================================================
FILE: examples/flush/flush.metatags
================================================
description: This BBE demonstrates how the `flush` action can be used for concurrency.
keywords: ballerina, ballerina by example, bbe, flush, concurrency
================================================
FILE: examples/flush/flush.out
================================================
$ bal run flush.bal
Received integer 10 from worker A
Result from worker B : error("Error in worker B")
================================================
FILE: examples/foreach-statement/foreach_statement.bal
================================================
import ballerina/io;
public function main() {
string[] names = ["Bob", "Jo", "Ann", "Tom"];
// Loop through a list.
foreach string name in names {
io:println(name);
}
// Iterate a structure.
map grades = { Bob : 65, Jo : 70, Ann : 75, Tom : 60};
int sum = 0;
foreach int grade in grades {
sum += grade;
}
io:println("Average :", sum/grades.length());
// Binding patterns can be used with the `foreach` statement.
// Values in `resultList` are assigned to `name`, `grade` variables in the binding pattern.
[string, int][] resultList = [["Bob", 65], ["Jo", 70], ["Ann", 75], ["Tom", 60]];
// Binding patterns (e.g., `[string, int]` and `[name, grade]`) are used with the `foreach` statement.
foreach [string, int] [name, grade] in resultList {
io:println("Name:", name, " ", "Grade:", grade);
}
}
================================================
FILE: examples/foreach-statement/foreach_statement.md
================================================
# Foreach statement
The `foreach` statement iterates over an `iterable` value such as arrays, tuples, maps, records, string, and tables etc. by executing a block of statements for each value in the iteration order. For a string value, it will iterate over each code point of the string.
The syntax contains a variable and an `in` keyword followed by an expression and a block of statements. The variable can also be in the form of a type binding pattern and the expression can be an action invocation, which should evaluate to an iterable value.
::: code foreach_statement.bal :::
::: out foreach_statement.out :::
## Related links
- [Break statement](/learn/by-example/break-statement/)
- [Continue statement](/learn/by-example/continue-statement/)
- [While statement](/learn/by-example/while-statement/)
================================================
FILE: examples/foreach-statement/foreach_statement.metatags
================================================
description: This BBE demonstrates how to use the `foreach` statement to loop a list, iterate a structure, and use `foreach` statement with binding patterns in Ballerina.
keywords: ballerina, ballerina by example, bbe, foreach, loops, iterate, iterable, collections
================================================
FILE: examples/foreach-statement/foreach_statement.out
================================================
$ bal run foreach_statement.bal
Bob
Jo
Ann
Tom
Average :67
Name:Bob Grade:65
Name:Jo Grade:70
Name:Ann Grade:75
Name:Tom Grade:60
================================================
FILE: examples/fork/fork.bal
================================================
import ballerina/io;
public function main(int? userInput) {
if userInput is int {
int num = userInput;
fork {
worker A {
num -> B;
string value = <- B;
io:println(string `Received string '${value}' from worker B`);
}
worker B {
int value = <- A;
io:println(string `Received int '${value}' from worker A`);
"a" -> A;
}
}
} else {
io:println("Not forked");
}
}
================================================
FILE: examples/fork/fork.md
================================================
# Fork
The fork statement starts one or more named workers, which run in parallel with each other, each in its own new strand. Variables and parameters in scope for the fork statement remain in scope within the workers. Message passing can be done only between the workers created within the fork statement and cannot be done with the enclosing function's default worker and its named workers. Unlike named workers outside a fork statement, with a fork statement, workers can be defined anywhere within the function body (e.g., within a conditional block).
::: code fork.bal :::
::: out fork.out :::
================================================
FILE: examples/fork/fork.metatags
================================================
description: This BBE demonstrates how the `fork` statement can be used for concurrency.
keywords: ballerina, ballerina by example, bbe, fork, concurrency, named workers
================================================
FILE: examples/fork/fork.out
================================================
$ bal run fork.bal -- 5
Received int '5' from worker A
Received string 'a' from worker B
================================================
FILE: examples/ftp-client-receive-file/ftp_client_receive_file.bal
================================================
import ballerina/ftp;
import ballerina/io;
public function main() returns error? {
// Creates the client with the connection parameters, host, username, and
// password. An error is returned in a failure. The default port number
// `21` is used with these configurations.
ftp:Client fileClient = check new ({
host: "ftp.example.com",
auth: {
credentials: {
username: "user1",
password: "pass456"
}
}
});
// Reads a file from an FTP server for a given file path. In error cases,
// an error is returned.
stream fileStream = check fileClient->get("/server/logFile.txt");
// Write the content to a file.
check io:fileWriteBlocksFromStream("./local/newLogFile.txt", fileStream);
// Closes the file stream to finish the `get` operation.
check fileStream.close();
}
================================================
FILE: examples/ftp-client-receive-file/ftp_client_receive_file.md
================================================
# FTP client - Receive file
The `ftp:Client` connects to a given FTP server, and then sends and receives files as byte streams. An `ftp:Client` is created by giving the host-name and required credentials. Once connected, `get` method is used to read files as byte streams from the FTP server. Use this to transfer files from a remote file system to a local file system.
::: code ftp_client_receive_file.bal :::
## Prerequisites
- Start a [FTP server](https://hub.docker.com/r/stilliard/pure-ftpd/) instance.
- Run the FTP client given in the [FTP client - Send file](/learn/by-example/ftp-client-send-file) example to put a file in the FTP server.
Run the program by executing the following command. The newly-added file will appear in the local directory.
::: out ftp_client_receive_file.out :::
## Related links
- [`ftp:Client->get` method - API documentation](https://lib.ballerina.io/ballerina/ftp/latest#Client#get)
- [FTP client - Specification](/spec/ftp/#321-insecure-client)
================================================
FILE: examples/ftp-client-receive-file/ftp_client_receive_file.metatags
================================================
description: This example demonstrates getting file content from a remote FTP server.
keywords: ballerina, ballerina by example, bbe, FTP
================================================
FILE: examples/ftp-client-receive-file/ftp_client_receive_file.out
================================================
$ bal run ftp_client_read.bal
================================================
FILE: examples/ftp-client-send-file/ftp_client_send_file.bal
================================================
import ballerina/ftp;
import ballerina/io;
public function main() returns error? {
// Creates the client with the connection parameters, host, username, and
// password. An error is returned in a failure. The default port number
// `21` is used with these configurations.
ftp:Client fileClient = check new ({
host: "ftp.example.com",
auth: {
credentials: {
username: "user1",
password: "pass456"
}
}
});
// Add a new file to the given file location. In error cases,
// an error is returned. The local file is provided as a stream of
// `io:Block` in which 1024 is the block size.
stream